src/waypoint.cpp
branchcustombridgeheads
changeset 5649 55c8267c933f
parent 5643 3778051e8095
child 5650 aefc131bf5ce
equal deleted inserted replaced
5648:1608018c5ff2 5649:55c8267c933f
       
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 
       
     6 #include "command.h"
       
     7 #include "functions.h"
       
     8 #include "gfx.h"
       
     9 #include "map.h"
       
    10 #include "order.h"
       
    11 #include "rail_map.h"
       
    12 #include "bridge_map.h"
       
    13 #include "saveload.h"
       
    14 #include "station.h"
       
    15 #include "tile.h"
       
    16 #include "town.h"
       
    17 #include "waypoint.h"
       
    18 #include "variables.h"
       
    19 #include "table/strings.h"
       
    20 #include "vehicle.h"
       
    21 #include "yapf/yapf.h"
       
    22 #include "date.h"
       
    23 
       
    24 enum {
       
    25 	MAX_WAYPOINTS_PER_TOWN = 64,
       
    26 };
       
    27 
       
    28 /**
       
    29  * Called if a new block is added to the waypoint-pool
       
    30  */
       
    31 static void WaypointPoolNewBlock(uint start_item)
       
    32 {
       
    33 	Waypoint *wp;
       
    34 
       
    35 	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
       
    36 	 * TODO - This is just a temporary stage, this will be removed. */
       
    37 	for (wp = GetWaypoint(start_item); wp != NULL; wp = (wp->index + 1U < GetWaypointPoolSize()) ? GetWaypoint(wp->index + 1U) : NULL) wp->index = start_item++;
       
    38 }
       
    39 
       
    40 DEFINE_OLD_POOL(Waypoint, Waypoint, WaypointPoolNewBlock, NULL)
       
    41 
       
    42 /* Create a new waypoint */
       
    43 static Waypoint* AllocateWaypoint(void)
       
    44 {
       
    45 	Waypoint *wp;
       
    46 
       
    47 	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
       
    48 	 * TODO - This is just a temporary stage, this will be removed. */
       
    49 	for (wp = GetWaypoint(0); wp != NULL; wp = (wp->index + 1U < GetWaypointPoolSize()) ? GetWaypoint(wp->index + 1U) : NULL) {
       
    50 		if (!IsValidWaypoint(wp)) {
       
    51 			uint index = wp->index;
       
    52 
       
    53 			memset(wp, 0, sizeof(*wp));
       
    54 			wp->index = index;
       
    55 
       
    56 			return wp;
       
    57 		}
       
    58 	}
       
    59 
       
    60 	/* Check if we can add a block to the pool */
       
    61 	if (AddBlockToPool(&_Waypoint_pool)) return AllocateWaypoint();
       
    62 
       
    63 	return NULL;
       
    64 }
       
    65 
       
    66 /* Update the sign for the waypoint */
       
    67 static void UpdateWaypointSign(Waypoint* wp)
       
    68 {
       
    69 	Point pt = RemapCoords2(TileX(wp->xy) * TILE_SIZE, TileY(wp->xy) * TILE_SIZE);
       
    70 	SetDParam(0, wp->index);
       
    71 	UpdateViewportSignPos(&wp->sign, pt.x, pt.y - 0x20, STR_WAYPOINT_VIEWPORT);
       
    72 }
       
    73 
       
    74 /* Redraw the sign of a waypoint */
       
    75 static void RedrawWaypointSign(const Waypoint* wp)
       
    76 {
       
    77 	MarkAllViewportsDirty(
       
    78 		wp->sign.left - 6,
       
    79 		wp->sign.top,
       
    80 		wp->sign.left + (wp->sign.width_1 << 2) + 12,
       
    81 		wp->sign.top + 48);
       
    82 }
       
    83 
       
    84 /* Update all signs */
       
    85 void UpdateAllWaypointSigns(void)
       
    86 {
       
    87 	Waypoint *wp;
       
    88 
       
    89 	FOR_ALL_WAYPOINTS(wp) {
       
    90 		UpdateWaypointSign(wp);
       
    91 	}
       
    92 }
       
    93 
       
    94 /* Internal handler to delete a waypoint */
       
    95 void DestroyWaypoint(Waypoint *wp)
       
    96 {
       
    97 	RemoveOrderFromAllVehicles(OT_GOTO_WAYPOINT, wp->index);
       
    98 
       
    99 	if (wp->string != STR_NULL) DeleteName(wp->string);
       
   100 
       
   101 	RedrawWaypointSign(wp);
       
   102 }
       
   103 
       
   104 /* Set the default name for a waypoint */
       
   105 static void MakeDefaultWaypointName(Waypoint* wp)
       
   106 {
       
   107 	Waypoint *local_wp;
       
   108 	bool used_waypoint[MAX_WAYPOINTS_PER_TOWN];
       
   109 	int i;
       
   110 
       
   111 	wp->town_index = ClosestTownFromTile(wp->xy, (uint)-1)->index;
       
   112 
       
   113 	memset(used_waypoint, 0, sizeof(used_waypoint));
       
   114 
       
   115 	/* Find an unused waypoint number belonging to this town */
       
   116 	FOR_ALL_WAYPOINTS(local_wp) {
       
   117 		if (wp == local_wp) continue;
       
   118 
       
   119 		if (local_wp->xy && local_wp->string == STR_NULL && local_wp->town_index == wp->town_index)
       
   120 			used_waypoint[local_wp->town_cn] = true;
       
   121 	}
       
   122 
       
   123 	/* Find an empty spot */
       
   124 	for (i = 0; used_waypoint[i] && i < MAX_WAYPOINTS_PER_TOWN; i++) {}
       
   125 
       
   126 	wp->string = STR_NULL;
       
   127 	wp->town_cn = i;
       
   128 }
       
   129 
       
   130 /* Find a deleted waypoint close to a tile. */
       
   131 static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile)
       
   132 {
       
   133 	Waypoint *wp, *best = NULL;
       
   134 	uint thres = 8;
       
   135 
       
   136 	FOR_ALL_WAYPOINTS(wp) {
       
   137 		if (wp->deleted) {
       
   138 			uint cur_dist = DistanceManhattan(tile, wp->xy);
       
   139 
       
   140 			if (cur_dist < thres) {
       
   141 				thres = cur_dist;
       
   142 				best = wp;
       
   143 			}
       
   144 		}
       
   145 	}
       
   146 
       
   147 	return best;
       
   148 }
       
   149 
       
   150 /**
       
   151  * Update waypoint graphics id against saved GRFID/localidx.
       
   152  * This is to ensure the chosen graphics are correct if GRF files are changed.
       
   153  */
       
   154 void AfterLoadWaypoints(void)
       
   155 {
       
   156 	Waypoint *wp;
       
   157 
       
   158 	FOR_ALL_WAYPOINTS(wp) {
       
   159 		uint i;
       
   160 
       
   161 		if (wp->grfid == 0) continue;
       
   162 
       
   163 		for (i = 0; i < GetNumCustomStations(STAT_CLASS_WAYP); i++) {
       
   164 			const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, i);
       
   165 			if (statspec != NULL && statspec->grfid == wp->grfid && statspec->localidx == wp->localidx) {
       
   166 				wp->stat_id = i;
       
   167 				break;
       
   168 			}
       
   169 		}
       
   170 	}
       
   171 }
       
   172 
       
   173 /** Convert existing rail to waypoint. Eg build a waypoint station over
       
   174  * piece of rail
       
   175  * @param tile tile where waypoint will be built
       
   176  * @param p1 graphics for waypoint type, 0 indicates standard graphics
       
   177  * @param p2 unused
       
   178  *
       
   179  * @todo When checking for the tile slope,
       
   180  * distingush between "Flat land required" and "land sloped in wrong direction"
       
   181  */
       
   182 int32 CmdBuildTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
       
   183 {
       
   184 	Waypoint *wp;
       
   185 	Slope tileh;
       
   186 	Axis axis;
       
   187 
       
   188 	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
       
   189 
       
   190 	/* if custom gfx are used, make sure it is within bounds */
       
   191 	if (p1 >= GetNumCustomStations(STAT_CLASS_WAYP)) return CMD_ERROR;
       
   192 
       
   193 	if (!IsTileType(tile, MP_RAILWAY) ||
       
   194 			GetRailTileType(tile) != RAIL_TILE_NORMAL || (
       
   195 				(axis = AXIS_X, GetTrackBits(tile) != TRACK_BIT_X) &&
       
   196 				(axis = AXIS_Y, GetTrackBits(tile) != TRACK_BIT_Y)
       
   197 			)) {
       
   198 		return_cmd_error(STR_1005_NO_SUITABLE_RAILROAD_TRACK);
       
   199 	}
       
   200 
       
   201 	if (!CheckTileOwnership(tile)) return CMD_ERROR;
       
   202 	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
       
   203 
       
   204 	tileh = GetTileSlope(tile, NULL);
       
   205 	if (tileh != SLOPE_FLAT &&
       
   206 			(!_patches.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) {
       
   207 		return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
       
   208 	}
       
   209 
       
   210 	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
       
   211 
       
   212 	/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
       
   213 	wp = FindDeletedWaypointCloseTo(tile);
       
   214 	if (wp == NULL) {
       
   215 		wp = AllocateWaypoint();
       
   216 		if (wp == NULL) return CMD_ERROR;
       
   217 
       
   218 		wp->town_index = 0;
       
   219 		wp->string = STR_NULL;
       
   220 		wp->town_cn = 0;
       
   221 	}
       
   222 
       
   223 	if (flags & DC_EXEC) {
       
   224 		const StationSpec* statspec;
       
   225 
       
   226 		MakeRailWaypoint(tile, GetTileOwner(tile), axis, GetRailType(tile), wp->index);
       
   227 		MarkTileDirtyByTile(tile);
       
   228 
       
   229 		statspec = GetCustomStationSpec(STAT_CLASS_WAYP, p1);
       
   230 
       
   231 		if (statspec != NULL) {
       
   232 			wp->stat_id = p1;
       
   233 			wp->grfid = statspec->grfid;
       
   234 			wp->localidx = statspec->localidx;
       
   235 		} else {
       
   236 			// Specified custom graphics do not exist, so use default.
       
   237 			wp->stat_id = 0;
       
   238 			wp->grfid = 0;
       
   239 			wp->localidx = 0;
       
   240 		}
       
   241 
       
   242 		wp->deleted = 0;
       
   243 		wp->xy = tile;
       
   244 		wp->build_date = _date;
       
   245 
       
   246 		if (wp->town_index == 0) MakeDefaultWaypointName(wp);
       
   247 
       
   248 		UpdateWaypointSign(wp);
       
   249 		RedrawWaypointSign(wp);
       
   250 		YapfNotifyTrackLayoutChange(tile, AxisToTrack(axis));
       
   251 	}
       
   252 
       
   253 	return _price.build_train_depot;
       
   254 }
       
   255 
       
   256 /* Daily loop for waypoints */
       
   257 void WaypointsDailyLoop(void)
       
   258 {
       
   259 	Waypoint *wp;
       
   260 
       
   261 	/* Check if we need to delete a waypoint */
       
   262 	FOR_ALL_WAYPOINTS(wp) {
       
   263 		if (wp->deleted != 0 && --wp->deleted == 0) DeleteWaypoint(wp);
       
   264 	}
       
   265 }
       
   266 
       
   267 /* Remove a waypoint */
       
   268 int32 RemoveTrainWaypoint(TileIndex tile, uint32 flags, bool justremove)
       
   269 {
       
   270 	Waypoint *wp;
       
   271 
       
   272 	/* Make sure it's a waypoint */
       
   273 	if (!IsTileType(tile, MP_RAILWAY) ||
       
   274 			!IsRailWaypoint(tile) ||
       
   275 			(!CheckTileOwnership(tile) && _current_player != OWNER_WATER) ||
       
   276 			!EnsureNoVehicle(tile)) {
       
   277 		return CMD_ERROR;
       
   278 	}
       
   279 
       
   280 	if (flags & DC_EXEC) {
       
   281 		Track track = GetRailWaypointTrack(tile);
       
   282 		wp = GetWaypointByTile(tile);
       
   283 
       
   284 		wp->deleted = 30; // let it live for this many days before we do the actual deletion.
       
   285 		RedrawWaypointSign(wp);
       
   286 
       
   287 		if (justremove) {
       
   288 			MakeRailNormal(tile, GetTileOwner(tile), GetRailWaypointBits(tile), GetRailType(tile));
       
   289 			MarkTileDirtyByTile(tile);
       
   290 		} else {
       
   291 			DoClearSquare(tile);
       
   292 			SetSignalsOnBothDir(tile, track);
       
   293 		}
       
   294 		YapfNotifyTrackLayoutChange(tile, track);
       
   295 	}
       
   296 
       
   297 	return _price.remove_train_depot;
       
   298 }
       
   299 
       
   300 /** Delete a waypoint
       
   301  * @param tile tile where waypoint is to be deleted
       
   302  * @param p1 unused
       
   303  * @param p2 unused
       
   304  */
       
   305 int32 CmdRemoveTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
       
   306 {
       
   307 	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
       
   308 	return RemoveTrainWaypoint(tile, flags, true);
       
   309 }
       
   310 
       
   311 /** Rename a waypoint.
       
   312  * @param tile unused
       
   313  * @param p1 id of waypoint
       
   314  * @param p2 unused
       
   315  */
       
   316 int32 CmdRenameWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
       
   317 {
       
   318 	Waypoint *wp;
       
   319 
       
   320 	if (!IsValidWaypointID(p1)) return CMD_ERROR;
       
   321 
       
   322 	if (_cmd_text[0] != '\0') {
       
   323 		StringID str = AllocateNameUnique(_cmd_text, 0);
       
   324 
       
   325 		if (str == 0) return CMD_ERROR;
       
   326 
       
   327 		if (flags & DC_EXEC) {
       
   328 			wp = GetWaypoint(p1);
       
   329 			if (wp->string != STR_NULL) DeleteName(wp->string);
       
   330 
       
   331 			wp->string = str;
       
   332 			wp->town_cn = 0;
       
   333 
       
   334 			UpdateWaypointSign(wp);
       
   335 			MarkWholeScreenDirty();
       
   336 		} else {
       
   337 			DeleteName(str);
       
   338 		}
       
   339 	} else {
       
   340 		if (flags & DC_EXEC) {
       
   341 			wp = GetWaypoint(p1);
       
   342 			if (wp->string != STR_NULL) DeleteName(wp->string);
       
   343 
       
   344 			MakeDefaultWaypointName(wp);
       
   345 			UpdateWaypointSign(wp);
       
   346 			MarkWholeScreenDirty();
       
   347 		}
       
   348 	}
       
   349 	return 0;
       
   350 }
       
   351 
       
   352 /* This hacks together some dummy one-shot Station structure for a waypoint. */
       
   353 Station *ComposeWaypointStation(TileIndex tile)
       
   354 {
       
   355 	Waypoint *wp = GetWaypointByTile(tile);
       
   356 	static Station stat;
       
   357 
       
   358 	stat.train_tile = stat.xy = wp->xy;
       
   359 	stat.town = GetTown(wp->town_index);
       
   360 	stat.string_id = wp->string == STR_NULL ? /* FIXME? */ 0 : wp->string;
       
   361 	stat.build_date = wp->build_date;
       
   362 
       
   363 	return &stat;
       
   364 }
       
   365 
       
   366 /* Draw a waypoint */
       
   367 void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype)
       
   368 {
       
   369 	x += 33;
       
   370 	y += 17;
       
   371 
       
   372 	if (!DrawStationTile(x, y, railtype, AXIS_X, STAT_CLASS_WAYP, stat_id)) {
       
   373 		DrawDefaultWaypointSprite(x, y, railtype);
       
   374 	}
       
   375 }
       
   376 
       
   377 /* Fix savegames which stored waypoints in their old format */
       
   378 void FixOldWaypoints(void)
       
   379 {
       
   380 	Waypoint *wp;
       
   381 
       
   382 	/* Convert the old 'town_or_string', to 'string' / 'town' / 'town_cn' */
       
   383 	FOR_ALL_WAYPOINTS(wp) {
       
   384 		wp->town_index = ClosestTownFromTile(wp->xy, (uint)-1)->index;
       
   385 		wp->town_cn = 0;
       
   386 		if (wp->string & 0xC000) {
       
   387 			wp->town_cn = wp->string & 0x3F;
       
   388 			wp->string = STR_NULL;
       
   389 		}
       
   390 	}
       
   391 }
       
   392 
       
   393 void InitializeWaypoints(void)
       
   394 {
       
   395 	CleanPool(&_Waypoint_pool);
       
   396 	AddBlockToPool(&_Waypoint_pool);
       
   397 }
       
   398 
       
   399 static const SaveLoad _waypoint_desc[] = {
       
   400 	SLE_CONDVAR(Waypoint, xy,         SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
       
   401 	SLE_CONDVAR(Waypoint, xy,         SLE_UINT32,                  6, SL_MAX_VERSION),
       
   402 	SLE_CONDVAR(Waypoint, town_index, SLE_UINT16,                 12, SL_MAX_VERSION),
       
   403 	SLE_CONDVAR(Waypoint, town_cn,    SLE_UINT8,                  12, SL_MAX_VERSION),
       
   404 	    SLE_VAR(Waypoint, string,     SLE_UINT16),
       
   405 	    SLE_VAR(Waypoint, deleted,    SLE_UINT8),
       
   406 
       
   407 	SLE_CONDVAR(Waypoint, build_date, SLE_FILE_U16 | SLE_VAR_I32,  3, 30),
       
   408 	SLE_CONDVAR(Waypoint, build_date, SLE_INT32,                  31, SL_MAX_VERSION),
       
   409 	SLE_CONDVAR(Waypoint, localidx,   SLE_UINT8,                   3, SL_MAX_VERSION),
       
   410 	SLE_CONDVAR(Waypoint, grfid,      SLE_UINT32,                 17, SL_MAX_VERSION),
       
   411 
       
   412 	SLE_END()
       
   413 };
       
   414 
       
   415 static void Save_WAYP(void)
       
   416 {
       
   417 	Waypoint *wp;
       
   418 
       
   419 	FOR_ALL_WAYPOINTS(wp) {
       
   420 		SlSetArrayIndex(wp->index);
       
   421 		SlObject(wp, _waypoint_desc);
       
   422 	}
       
   423 }
       
   424 
       
   425 static void Load_WAYP(void)
       
   426 {
       
   427 	int index;
       
   428 
       
   429 	while ((index = SlIterateArray()) != -1) {
       
   430 		Waypoint *wp;
       
   431 
       
   432 		if (!AddBlockIfNeeded(&_Waypoint_pool, index))
       
   433 			error("Waypoints: failed loading savegame: too many waypoints");
       
   434 
       
   435 		wp = GetWaypoint(index);
       
   436 		SlObject(wp, _waypoint_desc);
       
   437 	}
       
   438 }
       
   439 
       
   440 const ChunkHandler _waypoint_chunk_handlers[] = {
       
   441 	{ 'CHKP', Save_WAYP, Load_WAYP, CH_ARRAY | CH_LAST},
       
   442 };