Changeset - r6134:aa2aee9f9144
[Not reviewed]
master
0 5 1
tron - 17 years ago 2007-02-24 09:42:39
tron@openttd.org
(svn r8876) -Fix

Replace tests with magic numbers by a simple extraction template for command parameters
6 files changed with 58 insertions and 28 deletions:
0 comments (0 inline, 0 general)
src/cmd_helper.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef CMD_HELPER_H
 
#define CMD_HELPER_H
 

	
 
#include "direction.h"
 
#include "macros.h"
 
#include "road.h"
 

	
 

	
 
template<uint N> static inline void ExtractValid();
 
template<> static inline void ExtractValid<1>() {}
 

	
 

	
 
template<typename T> struct ExtractBits;
 
template<> struct ExtractBits<Axis>          { static const uint Count =  1; };
 
template<> struct ExtractBits<DiagDirection> { static const uint Count =  2; };
 
template<> struct ExtractBits<RoadBits>      { static const uint Count =  4; };
 

	
 

	
 
template<typename T, uint N, typename U> static inline T Extract(U v)
 
{
 
	// Check if there are enough bits in v
 
	ExtractValid<N + ExtractBits<T>::Count <= sizeof(U) * 8>();
 
	return (T)GB(v, N, ExtractBits<T>::Count);
 
}
 

	
 
#endif
src/depot.h
Show inline comments
 
/* $Id$ */
 

	
 
/** @file depot.h Header files for depots (not hangars) */
 

	
 
#ifndef DEPOT_H
 
#define DEPOT_H
 

	
 
#include "direction.h"
 
#include "oldpool.h"
 
#include "tile.h"
 
#include "variables.h"
 

	
 
struct Depot {
 
	TileIndex xy;
 
	TownID town_index;
 
	DepotID index;
 
};
 

	
 
DECLARE_OLD_POOL(Depot, Depot, 3, 8000)
 

	
 
/**
 
 * Check if a depot really exists.
 
 */
 
static inline bool IsValidDepot(const Depot *depot)
 
{
 
	return depot != NULL && depot->xy != 0;
 
}
 

	
 
static inline bool IsValidDepotID(uint index)
 
{
 
	return index < GetDepotPoolSize() && IsValidDepot(GetDepot(index));
 
}
 

	
 
void DestroyDepot(Depot *depot);
 

	
 
static inline void DeleteDepot(Depot *depot)
 
{
 
	DestroyDepot(depot);
 
	depot->xy = 0;
 
}
 

	
 
void ShowDepotWindow(TileIndex tile, byte type);
 

	
 
#define FOR_ALL_DEPOTS_FROM(d, start) for (d = GetDepot(start); d != NULL; d = (d->index + 1U < GetDepotPoolSize()) ? GetDepot(d->index + 1U) : NULL) if (IsValidDepot(d))
 
#define FOR_ALL_DEPOTS(d) FOR_ALL_DEPOTS_FROM(d, 0)
 

	
 
#define MIN_SERVINT_PERCENT  5
 
#define MAX_SERVINT_PERCENT 90
 
#define MIN_SERVINT_DAYS    30
 
#define MAX_SERVINT_DAYS   800
 

	
 
/**
 
 * Get the service interval domain.
 
 * Get the new proposed service interval for the vehicle is indeed, clamped
 
 * within the given bounds. @see MIN_SERVINT_PERCENT ,etc.
 
 * @param index proposed service interval
 
 * @return service interval
 
 */
 
static inline Date GetServiceIntervalClamped(uint index)
 
{
 
	return (_patches.servint_ispercent) ? clamp(index, MIN_SERVINT_PERCENT, MAX_SERVINT_PERCENT) : clamp(index, MIN_SERVINT_DAYS, MAX_SERVINT_DAYS);
 
}
 

	
 
/**
 
 * Check if a tile is a depot of the given type.
 
 */
 
static inline bool IsTileDepotType(TileIndex tile, TransportType type)
 
{
 
	switch (type) {
 
		case TRANSPORT_RAIL:
 
			return IsTileType(tile, MP_RAILWAY) && (_m[tile].m5 & 0xFC) == 0xC0;
 

	
 
		case TRANSPORT_ROAD:
 
			return IsTileType(tile, MP_STREET) && (_m[tile].m5 & 0xF0) == 0x20;
 

	
 
		case TRANSPORT_WATER:
 
			return IsTileType(tile, MP_WATER) && (_m[tile].m5 & ~3) == 0x80;
 

	
 
		default:
 
			assert(0);
 
			return false;
 
	}
 
}
 

	
 

	
 
/**
 
 * Find out if the slope of the tile is suitable to build a depot of given direction
 
 * @param direction The direction in which the depot's exit points. Starts with 0 as NE and goes Clockwise
 
 * @param direction The direction in which the depot's exit points
 
 * @param tileh The slope of the tile in question
 
 * @return true if the construction is possible
 

	
 
 * This is checked by the ugly 0x4C >> direction magic, which does the following:
 
 * 0x4C is 0100 1100 and tileh has only bits 0..3 set (steep tiles are ruled out)
 
 * So: for direction (only the significant bits are shown)<p>
 
 * 00 (exit towards NE) we need either bit 2 or 3 set in tileh: 0x4C >> 0 = 1100<p>
 
 * 01 (exit towards SE) we need either bit 1 or 2 set in tileh: 0x4C >> 1 = 0110<p>
 
 * 02 (exit towards SW) we need either bit 0 or 1 set in tileh: 0x4C >> 2 = 0011<p>
 
 * 03 (exit towards NW) we need either bit 0 or 4 set in tileh: 0x4C >> 3 = 1001<p>
 
 * So ((0x4C >> direction) & tileh) determines whether the depot can be built on the current tileh
 
 */
 
static inline bool CanBuildDepotByTileh(uint32 direction, Slope tileh)
 
static inline bool CanBuildDepotByTileh(DiagDirection direction, Slope tileh)
 
{
 
	return ((0x4C >> direction) & tileh) != 0;
 
}
 

	
 
Depot *GetDepotByTile(TileIndex tile);
 
void InitializeDepots(void);
 
Depot *AllocateDepot(void);
 

	
 
void DeleteDepotHighlightOfVehicle(const Vehicle *v);
 

	
 
#endif /* DEPOT_H */
src/rail_cmd.cpp
Show inline comments
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "cmd_helper.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "rail_map.h"
 
#include "road_map.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "town_map.h"
 
#include "tunnel_map.h"
 
#include "vehicle.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "pathfind.h"
 
#include "engine.h"
 
#include "town.h"
 
#include "sound.h"
 
#include "station.h"
 
#include "sprite.h"
 
#include "depot.h"
 
#include "waypoint.h"
 
#include "window.h"
 
#include "rail.h"
 
#include "railtypes.h" // include table for railtypes
 
#include "newgrf.h"
 
#include "yapf/yapf.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_station.h"
 
#include "train.h"
 

	
 
const byte _track_sloped_sprites[14] = {
 
	14, 15, 22, 13,
 
	 0, 21, 17, 12,
 
	23,  0, 18, 20,
 
	19, 16
 
};
 

	
 

	
 
/*         4
 
 *     ---------
 
 *    |\       /|
 
 *    | \    1/ |
 
 *    |  \   /  |
 
 *    |   \ /   |
 
 *  16|    \    |32
 
 *    |   / \2  |
 
 *    |  /   \  |
 
 *    | /     \ |
 
 *    |/       \|
 
 *     ---------
 
 *         8
 
 */
 

	
 

	
 

	
 
/* MAP2 byte:    abcd???? => Signal On? Same coding as map3lo
 
 * MAP3LO byte:  abcd???? => Signal Exists?
 
 *               a and b are for diagonals, upper and left,
 
 *               one for each direction. (ie a == NE->SW, b ==
 
 *               SW->NE, or v.v., I don't know. b and c are
 
 *               similar for lower and right.
 
 * MAP2 byte:    ????abcd => Type of ground.
 
 * MAP3LO byte:  ????abcd => Type of rail.
 
 * MAP5:         00abcdef => rail
 
 *               01abcdef => rail w/ signals
 
 *               10uuuuuu => unused
 
 *               11uuuudd => rail depot
 
 */
 

	
 
static bool CheckTrackCombination(TileIndex tile, TrackBits to_build, uint flags)
 
{
 
	TrackBits current; /* The current track layout */
 
	TrackBits future; /* The track layout we want to build */
 
	_error_message = STR_1001_IMPOSSIBLE_TRACK_COMBINATION;
 

	
 
	if (!IsPlainRailTile(tile)) return false;
 

	
 
	/* So, we have a tile with tracks on it (and possibly signals). Let's see
 
	 * what tracks first */
 
	current = GetTrackBits(tile);
 
	future = current | to_build;
 

	
 
	/* Are we really building something new? */
 
	if (current == future) {
 
		/* Nothing new is being built */
 
		_error_message = STR_1007_ALREADY_BUILT;
 
		return false;
 
	}
 

	
 
	/* Let's see if we may build this */
 
	if (flags & DC_NO_RAIL_OVERLAP || HasSignals(tile)) {
 
		/* If we are not allowed to overlap (flag is on for ai players or we have
 
		 * signals on the tile), check that */
 
		return future == TRACK_BIT_HORZ || future == TRACK_BIT_VERT;
 
	} else {
 
		/* Normally, we may overlap and any combination is valid */
 
@@ -447,238 +448,239 @@ static int32 ValidateAutoDrag(Trackdir *
 
		(trdx <= 0 && dx > 0) ||
 
		(trdx >= 0 && dx < 0) ||
 
		(trdy <= 0 && dy > 0) ||
 
		(trdy >= 0 && dy < 0)
 
	) {
 
		if (!HASBIT(*trackdir, 3)) { // first direction is invalid, try the other
 
			SetBitT(*trackdir, 3); // reverse the direction
 
			trdx = -trdx;
 
			trdy = -trdy;
 
		} else { // other direction is invalid too, invalid drag
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	// (for diagonal tracks, this is already made sure of by above test), but:
 
	// for non-diagonal tracks, check if the start and end tile are on 1 line
 
	if (!IsDiagonalTrackdir(*trackdir)) {
 
		trdx = _trackdelta[*trackdir].x;
 
		trdy = _trackdelta[*trackdir].y;
 
		if (abs(dx) != abs(dy) && abs(dx) + abs(trdy) != abs(dy) + abs(trdx))
 
			return CMD_ERROR;
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Build a stretch of railroad tracks.
 
 * @param tile start tile of drag
 
 * @param p1 end tile of drag
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit 0-3) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev)
 
 * - p2 = (bit 4-6) - track-orientation, valid values: 0-5 (Track enum)
 
 * - p2 = (bit 7)   - 0 = build, 1 = remove tracks
 
 */
 
static int32 CmdRailTrackHelper(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 ret, total_cost = 0;
 
	Track track = (Track)GB(p2, 4, 3);
 
	Trackdir trackdir;
 
	byte mode = HASBIT(p2, 7);
 
	RailType railtype = (RailType)GB(p2, 0, 4);
 
	TileIndex end_tile;
 

	
 
	if (!ValParamRailtype(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR;
 
	if (p1 >= MapSize()) return CMD_ERROR;
 
	end_tile = p1;
 
	trackdir = TrackToTrackdir(track);
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (CmdFailed(ValidateAutoDrag(&trackdir, tile, end_tile))) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) SndPlayTileFx(SND_20_SPLAT_2, tile);
 

	
 
	for (;;) {
 
		ret = DoCommand(tile, railtype, TrackdirToTrack(trackdir), flags, (mode == 0) ? CMD_BUILD_SINGLE_RAIL : CMD_REMOVE_SINGLE_RAIL);
 

	
 
		if (CmdFailed(ret)) {
 
			if ((_error_message != STR_1007_ALREADY_BUILT) && (mode == 0)) break;
 
			_error_message = INVALID_STRING_ID;
 
		} else {
 
			total_cost += ret;
 
		}
 

	
 
		if (tile == end_tile) break;
 

	
 
		tile += ToTileIndexDiff(_trackdelta[trackdir]);
 

	
 
		// toggle railbit for the non-diagonal tracks
 
		if (!IsDiagonalTrackdir(trackdir)) ToggleBitT(trackdir, 0);
 
	}
 

	
 
	return (total_cost == 0) ? CMD_ERROR : total_cost;
 
}
 

	
 
/** Build rail on a stretch of track.
 
 * Stub for the unified rail builder/remover
 
 * @see CmdRailTrackHelper
 
 */
 
int32 CmdBuildRailroadTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	return CmdRailTrackHelper(tile, flags, p1, CLRBIT(p2, 7));
 
}
 

	
 
/** Build rail on a stretch of track.
 
 * Stub for the unified rail builder/remover
 
 * @see CmdRailTrackHelper
 
 */
 
int32 CmdRemoveRailroadTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	return CmdRailTrackHelper(tile, flags, p1, SETBIT(p2, 7));
 
}
 

	
 
/** Build a train depot
 
 * @param tile position of the train depot
 
 * @param p1 rail type
 
 * @param p2 entrance direction (DiagDirection)
 
 * @param p2 bit 0..1 entrance direction (DiagDirection)
 
 *
 
 * @todo When checking for the tile slope,
 
 * distingush between "Flat land required" and "land sloped in wrong direction"
 
 */
 
int32 CmdBuildTrainDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Depot *d;
 
	int32 cost, ret;
 
	Slope tileh;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* check railtype and valid direction for depot (0 through 3), 4 in total */
 
	if (!ValParamRailtype(p1) || p2 > 3) return CMD_ERROR;
 
	if (!ValParamRailtype(p1)) return CMD_ERROR;
 

	
 
	tileh = GetTileSlope(tile, NULL);
 

	
 
	DiagDirection dir = Extract<DiagDirection, 0>(p2);
 

	
 
	/* Prohibit construction if
 
	 * The tile is non-flat AND
 
	 * 1) The AI is "old-school"
 
	 * 2) build-on-slopes is disabled
 
	 * 3) the tile is steep i.e. spans two height levels
 
	 * 4) the exit points in the wrong direction
 
	 */
 

	
 
	if (tileh != SLOPE_FLAT && (
 
				_is_old_ai_player ||
 
				!_patches.build_on_slopes ||
 
				IsSteepSlope(tileh) ||
 
				!CanBuildDepotByTileh(p2, tileh)
 
				!CanBuildDepotByTileh(dir, tileh)
 
			)) {
 
		return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
 
	}
 

	
 
	ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(ret)) return CMD_ERROR;
 
	cost = ret;
 

	
 
	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 

	
 
	d = AllocateDepot();
 
	if (d == NULL) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		DiagDirection dir = (DiagDirection)p2;
 
		MakeRailDepot(tile, _current_player, dir, (RailType)p1);
 
		MarkTileDirtyByTile(tile);
 

	
 
		d->xy = tile;
 
		d->town_index = ClosestTownFromTile(tile, (uint)-1)->index;
 

	
 
		UpdateSignalsOnSegment(tile, dir);
 
		YapfNotifyTrackLayoutChange(tile, TrackdirToTrack(DiagdirToDiagTrackdir(dir)));
 
	}
 

	
 
	return cost + _price.build_train_depot;
 
}
 

	
 
/** Build signals, alternate between double/single, signal/semaphore,
 
 * pre/exit/combo-signals, and what-else not. If the rail piece does not
 
 * have any signals, bit 4 (cycle signal-type) is ignored
 
 * @param tile tile where to build the signals
 
 * @param p1 various bitstuffed elements
 
 * - p1 = (bit 0-2) - track-orientation, valid values: 0-5 (Track enum)
 
 * - p1 = (bit 3)   - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
 
 * - p1 = (bit 4)   - 0 = signals, 1 = semaphores
 
 * @param p2 used for CmdBuildManySignals() to copy direction of first signal
 
 * TODO: p2 should be replaced by two bits for "along" and "against" the track.
 
 */
 
int32 CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Track track = (Track)GB(p1, 0, 3);
 
	bool pre_signal = HASBIT(p1, 3);
 
	SignalVariant sigvar = (pre_signal ^ HASBIT(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC;
 
	int32 cost;
 

	
 
	if (!ValParamTrackOrientation(track) || !IsTileType(tile, MP_RAILWAY) || !EnsureNoVehicle(tile))
 
		return CMD_ERROR;
 

	
 
	/* Protect against invalid signal copying */
 
	if (p2 != 0 && (p2 & SignalOnTrack(track)) == 0) return CMD_ERROR;
 

	
 
	/* You can only build signals on plain rail tiles, and the selected track must exist */
 
	if (!IsPlainRailTile(tile) || !HasTrack(tile, track)) return CMD_ERROR;
 

	
 
	if (!CheckTileOwnership(tile)) return CMD_ERROR;
 

	
 
	_error_message = STR_1005_NO_SUITABLE_RAILROAD_TRACK;
 

	
 
	{
 
		/* See if this is a valid track combination for signals, (ie, no overlap) */
 
		TrackBits trackbits = GetTrackBits(tile);
 
		if (KILL_FIRST_BIT(trackbits) != 0 && /* More than one track present */
 
				trackbits != TRACK_BIT_HORZ &&
 
				trackbits != TRACK_BIT_VERT) {
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!HasSignalOnTrack(tile, track)) {
 
		// build new signals
 
		cost = _price.build_signals;
 
	} else {
 
		if (p2 != 0 && sigvar != GetSignalVariant(tile)) {
 
			// convert signals <-> semaphores
 
			cost = _price.build_signals + _price.remove_signals;
 
		} else {
 
			// it is free to change orientation/pre-exit-combo signals
 
			cost = 0;
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		if (!HasSignals(tile)) {
 
			// there are no signals at all on this tile yet
 
			_m[tile].m5 |= RAIL_TILE_SIGNALS; // change into signals
 
			_m[tile].m2 |= 0xF0;              // all signals are on
 
			_m[tile].m3 &= ~0xF0;          // no signals built by default
 
			SetSignalType(tile, SIGTYPE_NORMAL);
 
			SetSignalVariant(tile, sigvar);
 
		}
 

	
 
		if (p2 == 0) {
 
			if (!HasSignalOnTrack(tile, track)) {
 
				// build new signals
 
				_m[tile].m3 |= SignalOnTrack(track);
 
			} else {
 
				if (pre_signal) {
 
					// cycle between normal -> pre -> exit -> combo -> ...
 
					SignalType type = GetSignalType(tile);
 

	
 
					SetSignalType(tile, type == SIGTYPE_COMBO ? SIGTYPE_NORMAL : (SignalType)(type + 1));
 
				} else {
 
					CycleSignalSide(tile, track);
 
				}
 
			}
 
		} else {
 
			/* If CmdBuildManySignals is called with copying signals, just copy the
 
			 * direction of the first signal given as parameter by CmdBuildManySignals */
src/road_cmd.cpp
Show inline comments
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "cmd_helper.h"
 
#include "rail_map.h"
 
#include "road_map.h"
 
#include "sprite.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "town_map.h"
 
#include "vehicle.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "player.h"
 
#include "town.h"
 
#include "gfx.h"
 
#include "sound.h"
 
#include "yapf/yapf.h"
 
#include "depot.h"
 

	
 

	
 
static uint CountRoadBits(RoadBits r)
 
{
 
	uint count = 0;
 

	
 
	if (r & ROAD_NW) ++count;
 
	if (r & ROAD_SW) ++count;
 
	if (r & ROAD_SE) ++count;
 
	if (r & ROAD_NE) ++count;
 
	return count;
 
}
 

	
 

	
 
bool CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, bool *edge_road)
 
{
 
	RoadBits present;
 
	RoadBits n;
 
	*edge_road = true;
 

	
 
	if (_game_mode == GM_EDITOR) return true;
 

	
 
	// Only do the special processing for actual players.
 
	if (!IsValidPlayer(_current_player)) return true;
 

	
 
	// Only do the special processing if the road is owned
 
	// by a town
 
	if (owner != OWNER_TOWN) return (owner == OWNER_NONE) || CheckOwnership(owner);
 

	
 
	if (_cheats.magic_bulldozer.value) return true;
 

	
 
	// Get a bitmask of which neighbouring roads has a tile
 
	n = ROAD_NONE;
 
	present = GetAnyRoadBits(tile);
 
	if (present & ROAD_NE && GetAnyRoadBits(TILE_ADDXY(tile,-1, 0)) & ROAD_SW) n |= ROAD_NE;
 
	if (present & ROAD_SE && GetAnyRoadBits(TILE_ADDXY(tile, 0, 1)) & ROAD_NW) n |= ROAD_SE;
 
	if (present & ROAD_SW && GetAnyRoadBits(TILE_ADDXY(tile, 1, 0)) & ROAD_NE) n |= ROAD_SW;
 
	if (present & ROAD_NW && GetAnyRoadBits(TILE_ADDXY(tile, 0,-1)) & ROAD_SE) n |= ROAD_NW;
 

	
 
	// If 0 or 1 bits are set in n, or if no bits that match the bits to remove,
 
	// then allow it
 
	if ((n & (n - 1)) != 0 && (n & remove) != 0) {
 
		Town *t;
 
		*edge_road = false;
 
		// you can remove all kind of roads with extra dynamite
 
		if (_patches.extra_dynamite) return true;
 

	
 
		t = ClosestTownFromTile(tile, _patches.dist_local_authority);
 

	
 
		SetDParam(0, t->index);
 
		_error_message = STR_2009_LOCAL_AUTHORITY_REFUSES;
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
static bool CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, bool *edge_road)
 
{
 
	return CheckAllowRemoveRoad(tile, remove, IsLevelCrossingTile(tile) ? GetCrossingRoadOwner(tile) : GetTileOwner(tile), edge_road);
 
}
 

	
 
/** Delete a piece of road.
 
 * @param tile tile where to remove road from
 
 * @param p1 road piece flags
 
 * @param p1 bit 0..3 road pieces to remove (RoadBits)
 
 * @param p2 unused
 
 */
 
int32 CmdRemoveRoad(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	// cost for removing inner/edge -roads
 
	static const uint16 road_remove_cost[2] = {50, 18};
 

	
 
	Owner owner;
 
	Town *t;
 
	/* true if the roadpiece was always removeable,
 
	 * false if it was a center piece. Affects town ratings drop */
 
	bool edge_road;
 
	RoadBits pieces;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* Road pieces are max 4 bitset values (NE, NW, SE, SW) */
 
	if (p1 >> 4) return CMD_ERROR;
 
	pieces = (RoadBits)p1;
 

	
 
	if (!IsTileType(tile, MP_STREET)) return CMD_ERROR;
 

	
 
	owner = IsLevelCrossingTile(tile) ? GetCrossingRoadOwner(tile) : GetTileOwner(tile);
 

	
 
	if (owner == OWNER_TOWN && _game_mode != GM_EDITOR) {
 
		t = GetTownByTile(tile);
 
	} else {
 
		t = NULL;
 
	}
 

	
 
	RoadBits pieces = Extract<RoadBits, 0>(p1);
 

	
 
	if (!CheckAllowRemoveRoad(tile, pieces, &edge_road)) return CMD_ERROR;
 

	
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	// check if you're allowed to remove the street owned by a town
 
	// removal allowance depends on difficulty setting
 
	if (!CheckforTownRating(flags, t, ROAD_REMOVE)) return CMD_ERROR;
 

	
 
	switch (GetRoadTileType(tile)) {
 
		case ROAD_TILE_NORMAL: {
 
			RoadBits present = GetRoadBits(tile);
 
			RoadBits c = pieces;
 

	
 
			if (HasRoadWorks(tile)) return_cmd_error(STR_ROAD_WORKS_IN_PROGRESS);
 

	
 
			if (GetTileSlope(tile, NULL) != SLOPE_FLAT  &&
 
					(present == ROAD_Y || present == ROAD_X)) {
 
				c |= (RoadBits)((c & 0xC) >> 2);
 
				c |= (RoadBits)((c & 0x3) << 2);
 
			}
 

	
 
			// limit the bits to delete to the existing bits.
 
			c &= present;
 
			if (c == 0) return CMD_ERROR;
 

	
 
			if (flags & DC_EXEC) {
 
				ChangeTownRating(t, -road_remove_cost[(byte)edge_road], RATING_ROAD_MINIMUM);
 

	
 
				present ^= c;
 
				if (present == 0) {
 
					DoClearSquare(tile);
 
				} else {
 
					SetRoadBits(tile, present);
 
					MarkTileDirtyByTile(tile);
 
				}
 
			}
 
			return CountRoadBits(c) * _price.remove_road;
 
		}
 

	
 
		case ROAD_TILE_CROSSING: {
 
			if (pieces & ComplementRoadBits(GetCrossingRoadBits(tile))) {
 
				return CMD_ERROR;
 
			}
 

	
 
			if (flags & DC_EXEC) {
 
				ChangeTownRating(t, -road_remove_cost[(byte)edge_road], RATING_ROAD_MINIMUM);
 

	
 
				MakeRailNormal(tile, GetTileOwner(tile), GetCrossingRailBits(tile), GetRailTypeCrossing(tile));
 
				MarkTileDirtyByTile(tile);
 
				YapfNotifyTrackLayoutChange(tile, FindFirstTrack(GetTrackBits(tile)));
 
			}
 
			return _price.remove_road * 2;
 
		}
 

	
 
		default:
 
		case ROAD_TILE_DEPOT:
 
			return CMD_ERROR;
 
	}
 
}
 

	
 

	
 
static const RoadBits _valid_tileh_slopes_road[][15] = {
 
	// set of normal ones
 
	{
 
		ROAD_ALL, ROAD_NONE, ROAD_NONE,
 
		ROAD_X,   ROAD_NONE, ROAD_NONE,  // 3, 4, 5
 
		ROAD_Y,   ROAD_NONE, ROAD_NONE,
 
		ROAD_Y,   ROAD_NONE, ROAD_NONE,  // 9, 10, 11
 
		ROAD_X,   ROAD_NONE, ROAD_NONE
 
	},
 
	// allowed road for an evenly raised platform
 
	{
 
		ROAD_NONE,
 
		ROAD_SW | ROAD_NW,
 
		ROAD_SW | ROAD_SE,
 
		ROAD_Y  | ROAD_SW,
 

	
 
		ROAD_SE | ROAD_NE, // 4
 
		ROAD_ALL,
 
		ROAD_X  | ROAD_SE,
 
		ROAD_ALL,
 

	
 
		ROAD_NW | ROAD_NE, // 8
 
		ROAD_X  | ROAD_NW,
 
		ROAD_ALL,
 
		ROAD_ALL,
 

	
 
		ROAD_Y  | ROAD_NE, // 12
 
		ROAD_ALL,
 
		ROAD_ALL
 
	},
 
};
 

	
 

	
 
static uint32 CheckRoadSlope(Slope tileh, RoadBits* pieces, RoadBits existing)
 
{
 
	RoadBits road_bits;
 

	
 
	if (IsSteepSlope(tileh)) {
 
		if (existing == 0) {
 
			// force full pieces.
 
			*pieces |= (RoadBits)((*pieces & 0xC) >> 2);
 
			*pieces |= (RoadBits)((*pieces & 0x3) << 2);
 
			if (*pieces == ROAD_X || *pieces == ROAD_Y) return _price.terraform;
 
		}
 
		return CMD_ERROR;
 
	}
 
	road_bits = *pieces | existing;
 

	
 
	// no special foundation
 
	if ((~_valid_tileh_slopes_road[0][tileh] & road_bits) == 0) {
 
		// force that all bits are set when we have slopes
 
		if (tileh != SLOPE_FLAT) *pieces |= _valid_tileh_slopes_road[0][tileh];
 
		return 0; // no extra cost
 
	}
 

	
 
	// foundation is used. Whole tile is leveled up
 
	if ((~_valid_tileh_slopes_road[1][tileh] & road_bits) == 0) {
 
		return existing != 0 ? 0 : _price.terraform;
 
	}
 

	
 
	// partly leveled up tile, only if there's no road on that tile
 
	if (existing == 0 && (tileh == SLOPE_W || tileh == SLOPE_S || tileh == SLOPE_E || tileh == SLOPE_N)) {
 
		// force full pieces.
 
		*pieces |= (RoadBits)((*pieces & 0xC) >> 2);
 
		*pieces |= (RoadBits)((*pieces & 0x3) << 2);
 
		if (*pieces == ROAD_X || *pieces == ROAD_Y) return _price.terraform;
 
	}
 
	return CMD_ERROR;
 
}
 

	
 
/** Build a piece of road.
 
 * @param tile tile where to build road
 
 * @param p1 road piece flags
 
 * @param p1 bit 0..3 road pieces to build (RoadBits)
 
 * @param p2 the town that is building the road (0 if not applicable)
 
 */
 
int32 CmdBuildRoad(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 cost = 0;
 
	int32 ret;
 
	RoadBits existing = ROAD_NONE;
 
	RoadBits pieces;
 
	Slope tileh;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* Road pieces are max 4 bitset values (NE, NW, SE, SW) and town can only be non-zero
 
	 * if a non-player is building the road */
 
	if ((p1 >> 4) || (IsValidPlayer(_current_player) && p2 != 0) || (_current_player == OWNER_TOWN && !IsValidTownID(p2))) return CMD_ERROR;
 
	pieces = (RoadBits)p1;
 
	if ((IsValidPlayer(_current_player) && p2 != 0) || (_current_player == OWNER_TOWN && !IsValidTownID(p2))) return CMD_ERROR;
 

	
 
	RoadBits pieces = Extract<RoadBits, 0>(p1);
 

	
 
	tileh = GetTileSlope(tile, NULL);
 

	
 
	switch (GetTileType(tile)) {
 
		case MP_STREET:
 
			switch (GetRoadTileType(tile)) {
 
				case ROAD_TILE_NORMAL:
 
					if (HasRoadWorks(tile)) return_cmd_error(STR_ROAD_WORKS_IN_PROGRESS);
 

	
 
					existing = GetRoadBits(tile);
 
					if ((existing & pieces) == pieces) {
 
						return_cmd_error(STR_1007_ALREADY_BUILT);
 
					}
 
					if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 
					break;
 

	
 
				case ROAD_TILE_CROSSING:
 
					if (pieces != GetCrossingRoadBits(tile)) { // XXX is this correct?
 
						return_cmd_error(STR_1007_ALREADY_BUILT);
 
					}
 
					goto do_clear;
 

	
 
				default:
 
				case ROAD_TILE_DEPOT:
 
					goto do_clear;
 
			}
 
			break;
 

	
 
		case MP_RAILWAY: {
 
			Axis roaddir;
 

	
 
			if (IsSteepSlope(tileh)) {
 
				return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 
			}
 

	
 
#define M(x) (1 << (x))
 
			/* Level crossings may only be built on these slopes */
 
			if (!HASBIT(M(SLOPE_SEN) | M(SLOPE_ENW) | M(SLOPE_NWS) | M(SLOPE_NS) | M(SLOPE_WSE) | M(SLOPE_EW) | M(SLOPE_FLAT), tileh)) {
 
				return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 
			}
 
#undef M
 

	
 
			if (GetRailTileType(tile) != RAIL_TILE_NORMAL) goto do_clear;
 
			switch (GetTrackBits(tile)) {
 
				case TRACK_BIT_X:
 
					if (pieces & ROAD_X) goto do_clear;
 
					roaddir = AXIS_Y;
 
					break;
 

	
 
				case TRACK_BIT_Y:
 
					if (pieces & ROAD_Y) goto do_clear;
 
					roaddir = AXIS_X;
 
					break;
 

	
 
				default: goto do_clear;
 
			}
 

	
 
			if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
			if (flags & DC_EXEC) {
 
				YapfNotifyTrackLayoutChange(tile, FindFirstTrack(GetTrackBits(tile)));
 
				MakeRoadCrossing(tile, _current_player, GetTileOwner(tile), roaddir, GetRailType(tile), p2);
 
				MarkTileDirtyByTile(tile);
 
			}
 
			return _price.build_road * 2;
 
		}
 

	
 
		default:
 
do_clear:;
 
			ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
			if (CmdFailed(ret)) return ret;
 
			cost += ret;
 
	}
 

	
 
	ret = CheckRoadSlope(tileh, &pieces, existing);
 
	/* Return an error if we need to build a foundation (ret != 0) but the
 
	 * current patch-setting is turned off (or stupid AI@work) */
 
	if (CmdFailed(ret) || (ret != 0 && (!_patches.build_on_slopes || _is_old_ai_player)))
 
		return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 

	
 
	cost += ret;
 

	
 
	if (IsTileType(tile, MP_STREET)) {
 
		// Don't put the pieces that already exist
 
		pieces &= ComplementRoadBits(existing);
 
	}
 

	
 
	cost += CountRoadBits(pieces) * _price.build_road;
 

	
 
	if (flags & DC_EXEC) {
 
		if (IsTileType(tile, MP_STREET)) {
 
			SetRoadBits(tile, existing | pieces);
 
		} else {
 
			MakeRoadNormal(tile, _current_player, pieces, p2);
 
		}
 

	
 
@@ -409,230 +407,230 @@ int32 CmdBuildLongRoad(TileIndex end_til
 

	
 
	start_tile = p1;
 

	
 
	/* Only drag in X or Y direction dictated by the direction variable */
 
	if (!HASBIT(p2, 2) && TileY(start_tile) != TileY(end_tile)) return CMD_ERROR; // x-axis
 
	if (HASBIT(p2, 2)  && TileX(start_tile) != TileX(end_tile)) return CMD_ERROR; // y-axis
 

	
 
	/* Swap start and ending tile, also the half-tile drag var (bit 0 and 1) */
 
	if (start_tile > end_tile || (start_tile == end_tile && HASBIT(p2, 0))) {
 
		TileIndex t = start_tile;
 
		start_tile = end_tile;
 
		end_tile = t;
 
		p2 ^= IS_INT_INSIDE(p2&3, 1, 3) ? 3 : 0;
 
	}
 

	
 
	cost = 0;
 
	tile = start_tile;
 
	// Start tile is the small number.
 
	for (;;) {
 
		RoadBits bits = HASBIT(p2, 2) ? ROAD_Y : ROAD_X;
 

	
 
		if (tile == end_tile && !HASBIT(p2, 1)) bits &= ROAD_NW | ROAD_NE;
 
		if (tile == start_tile && HASBIT(p2, 0)) bits &= ROAD_SE | ROAD_SW;
 

	
 
		ret = DoCommand(tile, bits, 0, flags, CMD_BUILD_ROAD);
 
		if (CmdFailed(ret)) {
 
			if (_error_message != STR_1007_ALREADY_BUILT) return CMD_ERROR;
 
			_error_message = INVALID_STRING_ID;
 
		} else {
 
			cost += ret;
 
		}
 

	
 
		if (tile == end_tile) break;
 

	
 
		tile += HASBIT(p2, 2) ? TileDiffXY(0, 1) : TileDiffXY(1, 0);
 
	}
 

	
 
	return (cost == 0) ? CMD_ERROR : cost;
 
}
 

	
 
/** Remove a long piece of road.
 
 * @param end_tile end tile of drag
 
 * @param p1 start tile of drag
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit 0) - start tile starts in the 2nd half of tile (p2 & 1)
 
 * - p2 = (bit 1) - end tile starts in the 2nd half of tile (p2 & 2)
 
 * - p2 = (bit 2) - direction: 0 = along x-axis, 1 = along y-axis (p2 & 4)
 
 */
 
int32 CmdRemoveLongRoad(TileIndex end_tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	TileIndex start_tile, tile;
 
	int32 cost, ret;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (p1 >= MapSize()) return CMD_ERROR;
 

	
 
	start_tile = p1;
 

	
 
	/* Only drag in X or Y direction dictated by the direction variable */
 
	if (!HASBIT(p2, 2) && TileY(start_tile) != TileY(end_tile)) return CMD_ERROR; // x-axis
 
	if (HASBIT(p2, 2)  && TileX(start_tile) != TileX(end_tile)) return CMD_ERROR; // y-axis
 

	
 
	/* Swap start and ending tile, also the half-tile drag var (bit 0 and 1) */
 
	if (start_tile > end_tile || (start_tile == end_tile && HASBIT(p2, 0))) {
 
		TileIndex t = start_tile;
 
		start_tile = end_tile;
 
		end_tile = t;
 
		p2 ^= IS_INT_INSIDE(p2 & 3, 1, 3) ? 3 : 0;
 
	}
 

	
 
	cost = 0;
 
	tile = start_tile;
 
	// Start tile is the small number.
 
	for (;;) {
 
		RoadBits bits = HASBIT(p2, 2) ? ROAD_Y : ROAD_X;
 

	
 
		if (tile == end_tile && !HASBIT(p2, 1)) bits &= ROAD_NW | ROAD_NE;
 
		if (tile == start_tile && HASBIT(p2, 0)) bits &= ROAD_SE | ROAD_SW;
 

	
 
		// try to remove the halves.
 
		if (bits != 0) {
 
			ret = DoCommand(tile, bits, 0, flags, CMD_REMOVE_ROAD);
 
			if (!CmdFailed(ret)) cost += ret;
 
		}
 

	
 
		if (tile == end_tile) break;
 

	
 
		tile += HASBIT(p2, 2) ? TileDiffXY(0, 1) : TileDiffXY(1, 0);
 
	}
 

	
 
	return (cost == 0) ? CMD_ERROR : cost;
 
}
 

	
 
/** Build a road depot.
 
 * @param tile tile where to build the depot
 
 * @param p1 entrance direction (DiagDirection)
 
 * @param p1 bit 0..1 entrance direction (DiagDirection)
 
 * @param p2 unused
 
 *
 
 * @todo When checking for the tile slope,
 
 * distingush between "Flat land required" and "land sloped in wrong direction"
 
 */
 
int32 CmdBuildRoadDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 cost;
 
	Depot *dep;
 
	Slope tileh;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (p1 > 3) return CMD_ERROR; // check direction
 
	DiagDirection dir = Extract<DiagDirection, 0>(p1);
 

	
 
	tileh = GetTileSlope(tile, NULL);
 
	if (tileh != SLOPE_FLAT && (
 
				!_patches.build_on_slopes ||
 
				IsSteepSlope(tileh) ||
 
				!CanBuildDepotByTileh(p1, tileh)
 
				!CanBuildDepotByTileh(dir, tileh)
 
			)) {
 
		return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
 
	}
 

	
 
	cost = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(cost)) return CMD_ERROR;
 

	
 
	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 

	
 
	dep = AllocateDepot();
 
	if (dep == NULL) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		dep->xy = tile;
 
		dep->town_index = ClosestTownFromTile(tile, (uint)-1)->index;
 

	
 
		MakeRoadDepot(tile, _current_player, (DiagDirection)p1);
 
		MakeRoadDepot(tile, _current_player, dir);
 
		MarkTileDirtyByTile(tile);
 
	}
 
	return cost + _price.build_road_depot;
 
}
 

	
 
static int32 RemoveRoadDepot(TileIndex tile, uint32 flags)
 
{
 
	if (!CheckTileOwnership(tile) && _current_player != OWNER_WATER)
 
		return CMD_ERROR;
 

	
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) DeleteDepot(GetDepotByTile(tile));
 

	
 
	return _price.remove_road_depot;
 
}
 

	
 
static int32 ClearTile_Road(TileIndex tile, byte flags)
 
{
 
	switch (GetRoadTileType(tile)) {
 
		case ROAD_TILE_NORMAL: {
 
			RoadBits b = GetRoadBits(tile);
 

	
 
#define M(x) (1 << (x))
 
			/* Clear the road if only one piece is on the tile OR the AI tries
 
			 * to clear town road OR we are not using the DC_AUTO flag */
 
			if ((M(b) & (M(ROAD_NW) | M(ROAD_SW) | M(ROAD_SE) | M(ROAD_NE))) ||
 
			    ((flags & DC_AI_BUILDING) && IsTileOwner(tile, OWNER_TOWN)) ||
 
			    !(flags & DC_AUTO)
 
				) {
 
				return DoCommand(tile, b, 0, flags, CMD_REMOVE_ROAD);
 
			}
 
			return_cmd_error(STR_1801_MUST_REMOVE_ROAD_FIRST);
 
		}
 
#undef M
 

	
 
		case ROAD_TILE_CROSSING: {
 
			int32 ret;
 

	
 
			if (flags & DC_AUTO) return_cmd_error(STR_1801_MUST_REMOVE_ROAD_FIRST);
 

	
 
			ret = DoCommand(tile, GetCrossingRoadBits(tile), 0, flags, CMD_REMOVE_ROAD);
 
			if (CmdFailed(ret)) return CMD_ERROR;
 

	
 
			if (flags & DC_EXEC) {
 
				DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
			}
 
			return ret;
 
		}
 

	
 
		default:
 
		case ROAD_TILE_DEPOT:
 
			if (flags & DC_AUTO) {
 
				return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);
 
			}
 
			return RemoveRoadDepot(tile, flags);
 
	}
 
}
 

	
 

	
 
typedef struct DrawRoadTileStruct {
 
	uint16 image;
 
	byte subcoord_x;
 
	byte subcoord_y;
 
} DrawRoadTileStruct;
 

	
 
#include "table/road_land.h"
 

	
 

	
 
uint GetRoadFoundation(Slope tileh, RoadBits bits)
 
{
 
	uint i;
 

	
 
	// normal level sloped building
 
	if (!IsSteepSlope(tileh) &&
 
			(~_valid_tileh_slopes_road[1][tileh] & bits) == 0) {
 
		return tileh;
 
	}
 

	
 
	// inclined sloped building
 
	switch (bits) {
 
		case ROAD_X: i = 0; break;
 
		case ROAD_Y: i = 1; break;
 
		default:     return 0;
 
	}
 
	switch (tileh) {
 
		case SLOPE_W:
 
		case SLOPE_STEEP_W: i += 0; break;
 
		case SLOPE_S:
 
		case SLOPE_STEEP_S: i += 2; break;
 
		case SLOPE_E:
 
		case SLOPE_STEEP_E: i += 4; break;
 
		case SLOPE_N:
 
		case SLOPE_STEEP_N: i += 6; break;
 
		default: return 0;
 
	}
src/station_cmd.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/** @file station_cmd.cpp */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "aircraft.h"
 
#include "bridge_map.h"
 
#include "cmd_helper.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "station_map.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "station.h"
 
#include "gfx.h"
 
#include "window.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "town.h"
 
#include "vehicle.h"
 
#include "news.h"
 
#include "saveload.h"
 
#include "economy.h"
 
#include "player.h"
 
#include "airport.h"
 
#include "sprite.h"
 
#include "depot.h"
 
#include "train.h"
 
#include "water_map.h"
 
#include "industry_map.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_station.h"
 
#include "yapf/yapf.h"
 
#include "date.h"
 
#include "helpers.hpp"
 
#include "misc/autoptr.hpp"
 
#include "road.h"
 
#include "cargotype.h"
 

	
 
/**
 
 * Called if a new block is added to the station-pool
 
 */
 
static void StationPoolNewBlock(uint start_item)
 
{
 
	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
 
	 *  TODO - This is just a temporary stage, this will be removed. */
 
	for (Station *st = GetStation(start_item); st != NULL; st = (st->index + 1U < GetStationPoolSize()) ? GetStation(st->index + 1U) : NULL) st->index = start_item++;
 
}
 

	
 
static void StationPoolCleanBlock(uint start_item, uint end_item)
 
{
 
	for (uint i = start_item; i <= end_item; i++) {
 
		Station *st = GetStation(i);
 
		if (st->IsValid()) st->~Station();
 
	}
 
}
 

	
 
/**
 
 * Called if a new block is added to the roadstop-pool
 
 */
 
static void RoadStopPoolNewBlock(uint start_item)
 
{
 
	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
 
	 * TODO - This is just a temporary stage, this will be removed. */
 
	for (RoadStop *rs = GetRoadStop(start_item); rs != NULL; rs = (rs->index + 1U < GetRoadStopPoolSize()) ? GetRoadStop(rs->index + 1U) : NULL) {
 
		rs->xy    = INVALID_TILE;
 
		rs->index = start_item++;
 
	}
 
}
 

	
 
DEFINE_OLD_POOL(Station, Station, StationPoolNewBlock, StationPoolCleanBlock)
 
DEFINE_OLD_POOL(RoadStop, RoadStop, RoadStopPoolNewBlock, NULL)
 

	
 

	
 
RoadStop* GetRoadStopByTile(TileIndex tile, RoadStop::Type type)
 
{
 
	const Station* st = GetStationByTile(tile);
 

	
 
	for (RoadStop *rs = st->GetPrimaryRoadStop(type);; rs = rs->next) {
 
		if (rs->xy == tile) return rs;
 
		assert(rs->next != NULL);
 
	}
 
}
 

	
 

	
 
static uint GetNumRoadStopsInStation(const Station* st, RoadStop::Type type)
 
{
 
	uint num = 0;
 

	
 
	assert(st != NULL);
 
	for (const RoadStop *rs = st->GetPrimaryRoadStop(type); rs != NULL; rs = rs->next) {
 
		num++;
 
	}
 

	
 
	return num;
 
}
 

	
 

	
 
/* Calculate the radius of the station. Basicly it is the biggest
 
 *  radius that is available within the station */
 
static uint FindCatchmentRadius(const Station* st)
 
{
 
@@ -694,213 +695,213 @@ static bool CanExpandRailroadStation(con
 
	uint h = fin[2];
 

	
 
	if (_patches.nonuniform_stations) {
 
		// determine new size of train station region..
 
		int x = min(TileX(st->train_tile), TileX(tile));
 
		int y = min(TileY(st->train_tile), TileY(tile));
 
		curw = max(TileX(st->train_tile) + curw, TileX(tile) + w) - x;
 
		curh = max(TileY(st->train_tile) + curh, TileY(tile) + h) - y;
 
		tile = TileXY(x, y);
 
	} else {
 
		// check so the orientation is the same
 
		if (GetRailStationAxis(st->train_tile) != axis) {
 
			_error_message = STR_306D_NONUNIFORM_STATIONS_DISALLOWED;
 
			return false;
 
		}
 

	
 
		// check if the new station adjoins the old station in either direction
 
		if (curw == w && st->train_tile == tile + TileDiffXY(0, h)) {
 
			// above
 
			curh += h;
 
		} else if (curw == w && st->train_tile == tile - TileDiffXY(0, curh)) {
 
			// below
 
			tile -= TileDiffXY(0, curh);
 
			curh += h;
 
		} else if (curh == h && st->train_tile == tile + TileDiffXY(w, 0)) {
 
			// to the left
 
			curw += w;
 
		} else if (curh == h && st->train_tile == tile - TileDiffXY(curw, 0)) {
 
			// to the right
 
			tile -= TileDiffXY(curw, 0);
 
			curw += w;
 
		} else {
 
			_error_message = STR_306D_NONUNIFORM_STATIONS_DISALLOWED;
 
			return false;
 
		}
 
	}
 
	// make sure the final size is not too big.
 
	if (curw > _patches.station_spread || curh > _patches.station_spread) {
 
		_error_message = STR_306C_STATION_TOO_SPREAD_OUT;
 
		return false;
 
	}
 

	
 
	// now tile contains the new value for st->train_tile
 
	// curw, curh contain the new value for width and height
 
	fin[0] = tile;
 
	fin[1] = curw;
 
	fin[2] = curh;
 
	return true;
 
}
 

	
 
static inline byte *CreateSingle(byte *layout, int n)
 
{
 
	int i = n;
 
	do *layout++ = 0; while (--i);
 
	layout[((n-1) >> 1)-n] = 2;
 
	return layout;
 
}
 

	
 
static inline byte *CreateMulti(byte *layout, int n, byte b)
 
{
 
	int i = n;
 
	do *layout++ = b; while (--i);
 
	if (n > 4) {
 
		layout[0-n] = 0;
 
		layout[n-1-n] = 0;
 
	}
 
	return layout;
 
}
 

	
 
static void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec)
 
{
 
	if (statspec != NULL && statspec->lengths >= plat_len &&
 
			statspec->platforms[plat_len - 1] >= numtracks &&
 
			statspec->layouts[plat_len - 1][numtracks - 1]) {
 
		/* Custom layout defined, follow it. */
 
		memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1],
 
			plat_len * numtracks);
 
		return;
 
	}
 

	
 
	if (plat_len == 1) {
 
		CreateSingle(layout, numtracks);
 
	} else {
 
		if (numtracks & 1) layout = CreateSingle(layout, plat_len);
 
		numtracks >>= 1;
 

	
 
		while (--numtracks >= 0) {
 
			layout = CreateMulti(layout, plat_len, 4);
 
			layout = CreateMulti(layout, plat_len, 6);
 
		}
 
	}
 
}
 

	
 
/** Build railroad station
 
 * @param tile_org starting position of station dragging/placement
 
 * @param p1 various bitstuffed elements
 
 * - p1 = (bit  0)    - orientation (p1 & 1)
 
 * - p1 = (bit  0)    - orientation (Axis)
 
 * - p1 = (bit  8-15) - number of tracks
 
 * - p1 = (bit 16-23) - platform length
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit  0- 3) - railtype (p2 & 0xF)
 
 * - p2 = (bit  8-15) - custom station class
 
 * - p2 = (bit 16-23) - custom station id
 
 */
 
int32 CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int w_org, h_org;
 
	int32 ret;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* Does the authority allow this? */
 
	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile_org)) return CMD_ERROR;
 
	if (!ValParamRailtype(p2 & 0xF)) return CMD_ERROR;
 

	
 
	/* unpack parameters */
 
	Axis axis = (Axis)GB(p1, 0, 1);
 
	Axis axis = Extract<Axis, 0>(p1);
 
	uint numtracks = GB(p1,  8, 8);
 
	uint plat_len  = GB(p1, 16, 8);
 
	if (axis == AXIS_X) {
 
		w_org = plat_len;
 
		h_org = numtracks;
 
	} else {
 
		h_org = plat_len;
 
		w_org = numtracks;
 
	}
 

	
 
	if (h_org > _patches.station_spread || w_org > _patches.station_spread) return CMD_ERROR;
 

	
 
	// these values are those that will be stored in train_tile and station_platforms
 
	uint finalvalues[3];
 
	finalvalues[0] = tile_org;
 
	finalvalues[1] = w_org;
 
	finalvalues[2] = h_org;
 

	
 
	// Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station)
 
	StationID est = INVALID_STATION;
 
	// If DC_EXEC is in flag, do not want to pass it to CheckFlatLandBelow, because of a nice bug
 
	//  for detail info, see: https://sourceforge.net/tracker/index.php?func=detail&aid=1029064&group_id=103924&atid=636365
 
	ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags & ~DC_EXEC, 5 << axis, _patches.nonuniform_stations ? &est : NULL);
 
	if (CmdFailed(ret)) return ret;
 
	int32 cost = ret + (numtracks * _price.train_station_track + _price.train_station_length) * plat_len;
 

	
 
	// Make sure there are no similar stations around us.
 
	Station *st = GetStationAround(tile_org, w_org, h_org, est);
 
	if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 

	
 
	// See if there is a deleted station close to us.
 
	if (st == NULL) st = GetClosestStationFromTile(tile_org);
 

	
 
	/* In case of new station if DC_EXEC is NOT set we still need to create the station
 
	 * to test if everything is OK. In this case we need to delete it before return. */
 
	AutoPtrT<Station> st_auto_delete;
 

	
 
	if (st != NULL) {
 
		// Reuse an existing station.
 
		if (st->owner != _current_player)
 
			return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
 

	
 
		if (st->train_tile != 0) {
 
			// check if we want to expanding an already existing station?
 
			if (_is_old_ai_player || !_patches.join_stations)
 
				return_cmd_error(STR_3005_TOO_CLOSE_TO_ANOTHER_RAILROAD);
 
			if (!CanExpandRailroadStation(st, finalvalues, axis))
 
				return CMD_ERROR;
 
		}
 

	
 
		//XXX can't we pack this in the "else" part of the if above?
 
		if (!st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TEST)) return CMD_ERROR;
 
	} else {
 
		/* allocate and initialize new station */
 
		st = new Station(tile_org);
 
		if (st == NULL) return CMD_ERROR;
 

	
 
		/* ensure that in case of error (or no DC_EXEC) the station gets deleted upon return */
 
		st_auto_delete = st;
 

	
 
		st->town = ClosestTownFromTile(tile_org, (uint)-1);
 
		if (!GenerateStationName(st, tile_org, 0)) return CMD_ERROR;
 

	
 
		if (IsValidPlayer(_current_player) && (flags & DC_EXEC) != 0) {
 
			SETBIT(st->town->have_ratings, _current_player);
 
		}
 
	}
 

	
 
	/* Check if the given station class is valid */
 
	if (GB(p2, 8, 8) >= STAT_CLASS_MAX) return CMD_ERROR;
 

	
 
	/* Check if we can allocate a custom stationspec to this station */
 
	const StationSpec *statspec = GetCustomStationSpec((StationClassID)GB(p2, 8, 8), GB(p2, 16, 8));
 
	int specindex = AllocateSpecToStation(statspec, st, flags & DC_EXEC);
 
	if (specindex == -1) return CMD_ERROR;
 

	
 
	if (statspec != NULL) {
 
		/* Perform NewStation checks */
 

	
 
		/* Check if the station size is permitted */
 
		if (HASBIT(statspec->disallowed_platforms, numtracks - 1) || HASBIT(statspec->disallowed_lengths, plat_len - 1)) {
 
			return CMD_ERROR;
 
		}
 

	
 
		/* Check if the station is buildable */
 
		if (HASBIT(statspec->callbackmask, CBM_STATION_AVAIL) && GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE) == 0) {
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		TileIndexDiff tile_delta;
 
		byte *layout_ptr;
 
		byte numtracks_orig;
 
		Track track;
 

	
src/water_cmd.cpp
Show inline comments
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "cmd_helper.h"
 
#include "station_map.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "vehicle.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "town.h"
 
#include "news.h"
 
#include "sound.h"
 
#include "depot.h"
 
#include "vehicle_gui.h"
 
#include "train.h"
 
#include "water_map.h"
 
#include "newgrf.h"
 

	
 
static const SpriteID _water_shore_sprites[] = {
 
	0,
 
	SPR_SHORE_TILEH_1,
 
	SPR_SHORE_TILEH_2,
 
	SPR_SHORE_TILEH_3,
 
	SPR_SHORE_TILEH_4,
 
	0,
 
	SPR_SHORE_TILEH_6,
 
	0,
 
	SPR_SHORE_TILEH_8,
 
	SPR_SHORE_TILEH_9,
 
	0,
 
	0,
 
	SPR_SHORE_TILEH_12,
 
	0,
 
	0
 
};
 

	
 

	
 
static Vehicle *FindFloodableVehicleOnTile(TileIndex tile);
 
static void FloodVehicle(Vehicle *v);
 

	
 
/** Build a ship depot.
 
 * @param tile tile where ship depot is built
 
 * @param p1 depot direction (0 == X or 1 == Y)
 
 * @param p1 bit 0 depot orientation (Axis)
 
 * @param p2 unused
 
 */
 
int32 CmdBuildShipDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	TileIndex tile2;
 

	
 
	int32 cost, ret;
 
	Depot *depot;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (p1 > 1) return CMD_ERROR;
 

	
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	tile2 = tile + (p1 ? TileDiffXY(0, 1) : TileDiffXY(1, 0));
 
	Axis axis = Extract<Axis, 0>(p1);
 

	
 
	tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
 
	if (!EnsureNoVehicle(tile2)) return CMD_ERROR;
 

	
 
	if (!IsClearWaterTile(tile) || !IsClearWaterTile(tile2))
 
		return_cmd_error(STR_3801_MUST_BE_BUILT_ON_WATER);
 

	
 
	if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 

	
 
	ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(ret)) return CMD_ERROR;
 
	ret = DoCommand(tile2, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(ret)) return CMD_ERROR;
 

	
 
	// pretend that we're not making land from the water even though we actually are.
 
	cost = 0;
 

	
 
	depot = AllocateDepot();
 
	if (depot == NULL) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		depot->xy = tile;
 
		depot->town_index = ClosestTownFromTile(tile, (uint)-1)->index;
 

	
 
		MakeShipDepot(tile, _current_player, DEPOT_NORTH, (Axis)p1);
 
		MakeShipDepot(tile2, _current_player, DEPOT_SOUTH, (Axis)p1);
 
		MakeShipDepot(tile,  _current_player, DEPOT_NORTH, axis);
 
		MakeShipDepot(tile2, _current_player, DEPOT_SOUTH, axis);
 
		MarkTileDirtyByTile(tile);
 
		MarkTileDirtyByTile(tile2);
 
	}
 

	
 
	return cost + _price.build_ship_depot;
 
}
 

	
 
static int32 RemoveShipDepot(TileIndex tile, uint32 flags)
 
{
 
	TileIndex tile2;
 

	
 
	if (!IsShipDepot(tile)) return CMD_ERROR;
 
	if (!CheckTileOwnership(tile)) return CMD_ERROR;
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	tile2 = GetOtherShipDepotTile(tile);
 

	
 
	if (!EnsureNoVehicle(tile2)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		/* Kill the depot, which is registered at the northernmost tile. Use that one */
 
		DeleteDepot(GetDepotByTile(tile2 < tile ? tile2 : tile));
 

	
 
		MakeWater(tile);
 
		MakeWater(tile2);
 
		MarkTileDirtyByTile(tile);
 
		MarkTileDirtyByTile(tile2);
 
	}
 

	
 
	return _price.remove_ship_depot;
 
}
 

	
 
// build a shiplift
 
static int32 DoBuildShiplift(TileIndex tile, DiagDirection dir, uint32 flags)
 
{
 
	int32 ret;
 
	int delta;
 

	
 
	// middle tile
 
	ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(ret)) return CMD_ERROR;
 

	
 
	delta = TileOffsByDiagDir(dir);
 
	// lower tile
 
	ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(ret)) return CMD_ERROR;
 
	if (GetTileSlope(tile - delta, NULL) != SLOPE_FLAT) {
 
		return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 
	}
 

	
 
	// upper tile
 
	ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(ret)) return CMD_ERROR;
 
	if (GetTileSlope(tile + delta, NULL) != SLOPE_FLAT) {
 
		return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 
	}
 

	
 
	if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) ||
 
	    (MayHaveBridgeAbove(tile - delta) && IsBridgeAbove(tile - delta)) ||
 
	    (MayHaveBridgeAbove(tile + delta) && IsBridgeAbove(tile + delta))) {
 
		return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		MakeLock(tile, _current_player, dir);
 
		MarkTileDirtyByTile(tile);
 
		MarkTileDirtyByTile(tile - delta);
 
		MarkTileDirtyByTile(tile + delta);
 
	}
 

	
 
	return _price.clear_water * 22 >> 3;
 
}
 

	
 
static int32 RemoveShiplift(TileIndex tile, uint32 flags)
 
{
 
	TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
 

	
 
	if (!CheckTileOwnership(tile)) return CMD_ERROR;
 

	
 
	// make sure no vehicle is on the tile.
 
	if (!EnsureNoVehicle(tile) || !EnsureNoVehicle(tile + delta) || !EnsureNoVehicle(tile - delta))
 
		return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		DoClearSquare(tile);
 
		DoClearSquare(tile + delta);
 
		DoClearSquare(tile - delta);
 
	}
 

	
 
	return _price.clear_water * 2;
 
}
 

	
 
static void MarkTilesAroundDirty(TileIndex tile)
 
{
 
	MarkTileDirtyByTile(TILE_ADDXY(tile, 0, 1));
 
	MarkTileDirtyByTile(TILE_ADDXY(tile, 0, -1));
0 comments (0 inline, 0 general)