Changeset - r5584:545d748cc681
[Not reviewed]
master
155 0 155
KUDr - 17 years ago 2007-01-10 18:12:09
kudr@openttd.org
(svn r8033) [cpp] - Prepare for merge from branches/cpp (all .c files renamed to .cpp)
310 files changed with 116775 insertions and 116775 deletions:
src/bmp.c
378
src/gfx.c
2031
src/map.c
246
src/md5.c
384
src/npf.c
894
src/os2.c
266
src/sdl.c
139
src/tgp.c
829
0 comments (0 inline, 0 general)
src/ai/ai.c
Show inline comments
 
deleted file
src/ai/ai.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "../variables.h"
 
#include "../command.h"
 
#include "../network/network.h"
 
#include "ai.h"
 
#include "default/default.h"
 

	
 
/**
 
 * Dequeues commands put in the queue via AI_PutCommandInQueue.
 
 */
 
static void AI_DequeueCommands(PlayerID player)
 
{
 
	AICommand *com, *entry_com;
 

	
 
	entry_com = _ai_player[player].queue;
 

	
 
	/* It happens that DoCommandP issues a new DoCommandAI which adds a new command
 
	 *  to this very same queue (don't argue about this, if it currently doesn't
 
	 *  happen I can tell you it will happen with AIScript -- TrueLight). If we
 
	 *  do not make the queue NULL, that commands will be dequeued immediatly.
 
	 *  Therefor we safe the entry-point to entry_com, and make the queue NULL, so
 
	 *  the new queue can be safely built up. */
 
	_ai_player[player].queue = NULL;
 
	_ai_player[player].queue_tail = NULL;
 

	
 
	/* Dequeue all commands */
 
	while ((com = entry_com) != NULL) {
 
		_current_player = player;
 

	
 
		_cmd_text = com->text;
 
		DoCommandP(com->tile, com->p1, com->p2, com->callback, com->procc);
 

	
 
		/* Free item */
 
		entry_com = com->next;
 
		free(com->text);
 
		free(com);
 
	}
 
}
 

	
 
/**
 
 * Needed for SP; we need to delay DoCommand with 1 tick, because else events
 
 *  will make infinite loops (AIScript).
 
 */
 
static void AI_PutCommandInQueue(PlayerID player, TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCallback* callback)
 
{
 
	AICommand *com;
 

	
 
	if (_ai_player[player].queue_tail == NULL) {
 
		/* There is no item in the queue yet, create the queue */
 
		_ai_player[player].queue = malloc(sizeof(AICommand));
 
		_ai_player[player].queue_tail = _ai_player[player].queue;
 
	} else {
 
		/* Add an item at the end */
 
		_ai_player[player].queue_tail->next = malloc(sizeof(AICommand));
 
		_ai_player[player].queue_tail = _ai_player[player].queue_tail->next;
 
	}
 

	
 
	/* This is our new item */
 
	com = _ai_player[player].queue_tail;
 

	
 
	/* Assign the info */
 
	com->tile  = tile;
 
	com->p1    = p1;
 
	com->p2    = p2;
 
	com->procc = procc;
 
	com->callback = callback;
 
	com->next  = NULL;
 
	com->text  = NULL;
 

	
 
	/* Copy the cmd_text, if needed */
 
	if (_cmd_text != NULL) {
 
		com->text = strdup(_cmd_text);
 
		_cmd_text = NULL;
 
	}
 
}
 

	
 
/**
 
 * Executes a raw DoCommand for the AI.
 
 */
 
int32 AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, CommandCallback* callback)
 
{
 
	PlayerID old_lp;
 
	int32 res = 0;
 
	const char* tmp_cmdtext;
 

	
 
	/* If you enable DC_EXEC with DC_QUERY_COST you are a really strange
 
	 *   person.. should we check for those funny jokes?
 
	 */
 

	
 
	/* The test already resets _cmd_text, so backup the pointer */
 
	tmp_cmdtext = _cmd_text;
 

	
 
	/* First, do a test-run to see if we can do this */
 
	res = DoCommand(tile, p1, p2, flags & ~DC_EXEC, procc);
 
	/* The command failed, or you didn't want to execute, or you are quering, return */
 
	if (CmdFailed(res) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) {
 
		return res;
 
	}
 

	
 
	/* Restore _cmd_text */
 
	_cmd_text = tmp_cmdtext;
 

	
 
	/* If we did a DC_EXEC, and the command did not return an error, execute it
 
	 * over the network */
 
	if (flags & DC_AUTO)     procc |= CMD_AUTO;
 
	if (flags & DC_NO_WATER) procc |= CMD_NO_WATER;
 

	
 
	/* NetworkSend_Command needs _local_player to be set correctly, so
 
	 * adjust it, and put it back right after the function */
 
	old_lp = _local_player;
 
	_local_player = _current_player;
 

	
 
#ifdef ENABLE_NETWORK
 
	/* Send the command */
 
	if (_networking) {
 
		/* Network is easy, send it to his handler */
 
		NetworkSend_Command(tile, p1, p2, procc, callback);
 
	} else {
 
#else
 
	{
 
#endif
 
		/* If we execute BuildCommands directly in SP, we have a big problem with events
 
		 *  so we need to delay is for 1 tick */
 
		AI_PutCommandInQueue(_current_player, tile, p1, p2, procc, callback);
 
	}
 

	
 
	/* Set _local_player back */
 
	_local_player = old_lp;
 

	
 
	return res;
 
}
 

	
 

	
 
int32 AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
 
{
 
	return AI_DoCommandCc(tile, p1, p2, flags, procc, NULL);
 
}
 

	
 

	
 
/**
 
 * Run 1 tick of the AI. Don't overdo it, keep it realistic.
 
 */
 
static void AI_RunTick(PlayerID player)
 
{
 
	extern void AiNewDoGameLoop(Player *p);
 

	
 
	Player *p = GetPlayer(player);
 
	_current_player = player;
 

	
 
	if (_patches.ainew_active) {
 
		AiNewDoGameLoop(p);
 
	} else {
 
		/* Enable all kind of cheats the old AI needs in order to operate correctly... */
 
		_is_old_ai_player = true;
 
		AiDoGameLoop(p);
 
		_is_old_ai_player = false;
 
	}
 
}
 

	
 

	
 
/**
 
 * The gameloop for AIs.
 
 *  Handles one tick for all the AIs.
 
 */
 
void AI_RunGameLoop(void)
 
{
 
	/* Don't do anything if ai is disabled */
 
	if (!_ai.enabled) return;
 

	
 
	/* Don't do anything if we are a network-client, or the AI has been disabled */
 
	if (_networking && (!_network_server || !_patches.ai_in_multiplayer)) return;
 

	
 
	/* New tick */
 
	_ai.tick++;
 

	
 
	/* Make sure the AI follows the difficulty rule.. */
 
	assert(_opt.diff.competitor_speed <= 4);
 
	if ((_ai.tick & ((1 << (4 - _opt.diff.competitor_speed)) - 1)) != 0) return;
 

	
 
	/* Check for AI-client (so joining a network with an AI) */
 
	if (!_networking || _network_server) {
 
		/* Check if we want to run AIs (server or SP only) */
 
		const Player* p;
 

	
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active && p->is_ai) {
 
				/* This should always be true, else something went wrong... */
 
				assert(_ai_player[p->index].active);
 

	
 
				/* Run the script */
 
				AI_DequeueCommands(p->index);
 
				AI_RunTick(p->index);
 
			}
 
		}
 
	}
 

	
 
	_current_player = OWNER_NONE;
 
}
 

	
 
/**
 
 * A new AI sees the day of light. You can do here what ever you think is needed.
 
 */
 
void AI_StartNewAI(PlayerID player)
 
{
 
	assert(IsValidPlayer(player));
 

	
 
	/* Called if a new AI is booted */
 
	_ai_player[player].active = true;
 
}
 

	
 
/**
 
 * This AI player died. Give it some chance to make a final puf.
 
 */
 
void AI_PlayerDied(PlayerID player)
 
{
 
	/* Called if this AI died */
 
	_ai_player[player].active = false;
 
}
 

	
 
/**
 
 * Initialize some AI-related stuff.
 
 */
 
void AI_Initialize(void)
 
{
 
	/* First, make sure all AIs are DEAD! */
 
	AI_Uninitialize();
 

	
 
	memset(&_ai, 0, sizeof(_ai));
 
	memset(&_ai_player, 0, sizeof(_ai_player));
 

	
 
	_ai.enabled = true;
 
}
 

	
 
/**
 
 * Deinitializer for AI-related stuff.
 
 */
 
void AI_Uninitialize(void)
 
{
 
	const Player* p;
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active && p->is_ai) AI_PlayerDied(p->index);
 
	}
 
}
src/ai/default/default.c
Show inline comments
 
deleted file
src/ai/default/default.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../../stdafx.h"
 
#include "../../openttd.h"
 
#include "../../aircraft.h"
 
#include "../../bridge_map.h"
 
#include "../../functions.h"
 
#include "../../map.h"
 
#include "../../rail_map.h"
 
#include "../../road_map.h"
 
#include "../../roadveh.h"
 
#include "../../station_map.h"
 
#include "../../tile.h"
 
#include "../../player.h"
 
#include "../../tunnel_map.h"
 
#include "../../vehicle.h"
 
#include "../../engine.h"
 
#include "../../command.h"
 
#include "../../town.h"
 
#include "../../industry.h"
 
#include "../../station.h"
 
#include "../../pathfind.h"
 
#include "../../economy.h"
 
#include "../../airport.h"
 
#include "../../depot.h"
 
#include "../../variables.h"
 
#include "../../bridge.h"
 
#include "../../date.h"
 
#include "default.h"
 

	
 
// remove some day perhaps?
 
static uint _ai_service_interval;
 

	
 
typedef void AiStateAction(Player *p);
 

	
 
enum {
 
	AIS_0                            =  0,
 
	AIS_1                            =  1,
 
	AIS_VEH_LOOP                     =  2,
 
	AIS_VEH_CHECK_REPLACE_VEHICLE    =  3,
 
	AIS_VEH_DO_REPLACE_VEHICLE       =  4,
 
	AIS_WANT_NEW_ROUTE               =  5,
 
	AIS_BUILD_DEFAULT_RAIL_BLOCKS    =  6,
 
	AIS_BUILD_RAIL                   =  7,
 
	AIS_BUILD_RAIL_VEH               =  8,
 
	AIS_DELETE_RAIL_BLOCKS           =  9,
 
	AIS_BUILD_DEFAULT_ROAD_BLOCKS    = 10,
 
	AIS_BUILD_ROAD                   = 11,
 
	AIS_BUILD_ROAD_VEHICLES          = 12,
 
	AIS_DELETE_ROAD_BLOCKS           = 13,
 
	AIS_AIRPORT_STUFF                = 14,
 
	AIS_BUILD_DEFAULT_AIRPORT_BLOCKS = 15,
 
	AIS_BUILD_AIRCRAFT_VEHICLES      = 16,
 
	AIS_CHECK_SHIP_STUFF             = 17,
 
	AIS_BUILD_DEFAULT_SHIP_BLOCKS    = 18,
 
	AIS_DO_SHIP_STUFF                = 19,
 
	AIS_SELL_VEHICLE                 = 20,
 
	AIS_REMOVE_STATION               = 21,
 
	AIS_REMOVE_TRACK                 = 22,
 
	AIS_REMOVE_SINGLE_RAIL_TILE      = 23
 
};
 

	
 

	
 
#include "../../table/ai_rail.h"
 

	
 
static byte GetRailTrackStatus(TileIndex tile)
 
{
 
	uint32 r = GetTileTrackStatus(tile, TRANSPORT_RAIL);
 
	return (byte) (r | r >> 8);
 
}
 

	
 

	
 
static void AiCase0(Player *p)
 
{
 
	p->ai.state = AIS_REMOVE_TRACK;
 
	p->ai.state_counter = 0;
 
}
 

	
 
static void AiCase1(Player *p)
 
{
 
	p->ai.cur_veh = NULL;
 
	p->ai.state = AIS_VEH_LOOP;
 
}
 

	
 
static void AiStateVehLoop(Player *p)
 
{
 
	Vehicle *v;
 
	uint index;
 

	
 
	index = (p->ai.cur_veh == NULL) ? 0 : p->ai.cur_veh->index + 1;
 

	
 
	FOR_ALL_VEHICLES_FROM(v, index) {
 
		if (v->owner != _current_player) continue;
 

	
 
		if ((v->type == VEH_Train && v->subtype == 0) ||
 
				v->type == VEH_Road ||
 
				(v->type == VEH_Aircraft && v->subtype <= 2) ||
 
				v->type == VEH_Ship) {
 
			/* replace engine? */
 
			if (v->type == VEH_Train && v->engine_type < 3 &&
 
					(_price.build_railvehicle >> 3) < p->player_money) {
 
				p->ai.state = AIS_VEH_CHECK_REPLACE_VEHICLE;
 
				p->ai.cur_veh = v;
 
				return;
 
			}
 

	
 
			/* not profitable? */
 
			if (v->age >= 730 &&
 
					v->profit_last_year < _price.station_value * 5 &&
 
					v->profit_this_year < _price.station_value * 5) {
 
				p->ai.state_counter = 0;
 
				p->ai.state = AIS_SELL_VEHICLE;
 
				p->ai.cur_veh = v;
 
				return;
 
			}
 

	
 
			/* not reliable? */
 
			if (v->age >= v->max_age || (
 
						v->age != 0 &&
 
						GetEngine(v->engine_type)->reliability < 35389
 
					)) {
 
				p->ai.state = AIS_VEH_CHECK_REPLACE_VEHICLE;
 
				p->ai.cur_veh = v;
 
				return;
 
			}
 
		}
 
	}
 

	
 
	p->ai.state = AIS_WANT_NEW_ROUTE;
 
	p->ai.state_counter = 0;
 
}
 

	
 
static EngineID AiChooseTrainToBuild(RailType railtype, int32 money, byte flag, TileIndex tile)
 
{
 
	EngineID best_veh_index = INVALID_ENGINE;
 
	byte best_veh_score = 0;
 
	int32 ret;
 
	EngineID i;
 

	
 
	for (i = 0; i < NUM_TRAIN_ENGINES; i++) {
 
		const RailVehicleInfo *rvi = RailVehInfo(i);
 
		const Engine* e = GetEngine(i);
 

	
 
		if (!IsCompatibleRail(e->railtype, railtype) ||
 
				rvi->flags & RVI_WAGON ||
 
				(rvi->flags & RVI_MULTIHEAD && flag & 1) ||
 
				!HASBIT(e->player_avail, _current_player) ||
 
				e->reliability < 0x8A3D) {
 
			continue;
 
		}
 

	
 
		ret = DoCommand(tile, i, 0, 0, CMD_BUILD_RAIL_VEHICLE);
 
		if (!CmdFailed(ret) && ret <= money && rvi->ai_rank >= best_veh_score) {
 
			best_veh_score = rvi->ai_rank;
 
			best_veh_index = i;
 
		}
 
	}
 

	
 
	return best_veh_index;
 
}
 

	
 
static EngineID AiChooseRoadVehToBuild(CargoID cargo, int32 money, TileIndex tile)
 
{
 
	EngineID best_veh_index = INVALID_ENGINE;
 
	int32 best_veh_rating = 0;
 
	EngineID i = ROAD_ENGINES_INDEX;
 
	EngineID end = i + NUM_ROAD_ENGINES;
 

	
 
	for (; i != end; i++) {
 
		const RoadVehicleInfo *rvi = RoadVehInfo(i);
 
		const Engine* e = GetEngine(i);
 
		int32 rating;
 
		int32 ret;
 

	
 
		if (!HASBIT(e->player_avail, _current_player) || e->reliability < 0x8A3D) {
 
			continue;
 
		}
 

	
 
		/* Skip vehicles which can't take our cargo type */
 
		if (rvi->cargo_type != cargo && !CanRefitTo(i, cargo)) continue;
 

	
 
		/* Rate and compare the engine by speed & capacity */
 
		rating = rvi->max_speed * rvi->capacity;
 
		if (rating <= best_veh_rating) continue;
 

	
 
		ret = DoCommand(tile, i, 0, 0, CMD_BUILD_ROAD_VEH);
 
		if (CmdFailed(ret)) continue;
 

	
 
		/* Add the cost of refitting */
 
		if (rvi->cargo_type != cargo) ret += GetRefitCost(i);
 
		if (ret > money) continue;
 

	
 
		best_veh_rating = rating;
 
		best_veh_index = i;
 
	}
 

	
 
	return best_veh_index;
 
}
 

	
 
static EngineID AiChooseAircraftToBuild(int32 money, byte flag)
 
{
 
	EngineID best_veh_index = INVALID_ENGINE;
 
	int32 best_veh_cost = 0;
 
	EngineID i;
 

	
 
	for (i = AIRCRAFT_ENGINES_INDEX; i != AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES; i++) {
 
		const Engine* e = GetEngine(i);
 
		int32 ret;
 

	
 
		if (!HASBIT(e->player_avail, _current_player) || e->reliability < 0x8A3D) {
 
			continue;
 
		}
 

	
 
		if ((AircraftVehInfo(i)->subtype & AIR_CTOL) != flag) continue;
 

	
 
		ret = DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_AIRCRAFT);
 
		if (!CmdFailed(ret) && ret <= money && ret >= best_veh_cost) {
 
			best_veh_cost = ret;
 
			best_veh_index = i;
 
		}
 
	}
 

	
 
	return best_veh_index;
 
}
 

	
 
static int32 AiGetBasePrice(const Player* p)
 
{
 
	int32 base = _price.station_value;
 

	
 
	// adjust base price when more expensive vehicles are available
 
	switch (p->ai.railtype_to_use) {
 
		default: NOT_REACHED();
 
		case RAILTYPE_RAIL:     break;
 
		case RAILTYPE_ELECTRIC: break;
 
		case RAILTYPE_MONO:     base = (base * 3) >> 1; break;
 
		case RAILTYPE_MAGLEV:   base *= 2; break;
 
	}
 

	
 
	return base;
 
}
 

	
 
#if 0
 
static EngineID AiChooseShipToBuild(byte cargo, int32 money)
 
{
 
	// XXX: not done
 
	return INVALID_ENGINE;
 
}
 
#endif
 

	
 
static EngineID AiChooseRoadVehToReplaceWith(const Player* p, const Vehicle* v)
 
{
 
	int32 avail_money = p->player_money + v->value;
 
	return AiChooseRoadVehToBuild(v->cargo_type, avail_money, v->tile);
 
}
 

	
 
static EngineID AiChooseAircraftToReplaceWith(const Player* p, const Vehicle* v)
 
{
 
	int32 avail_money = p->player_money + v->value;
 
	return AiChooseAircraftToBuild(
 
		avail_money, AircraftVehInfo(v->engine_type)->subtype & AIR_CTOL
 
	);
 
}
 

	
 
static EngineID AiChooseTrainToReplaceWith(const Player* p, const Vehicle* v)
 
{
 
	int32 avail_money = p->player_money + v->value;
 
	const Vehicle* u = v;
 
	int num = 0;
 

	
 
	while (++num, u->next != NULL) {
 
		u = u->next;
 
	}
 

	
 
	// XXX: check if a wagon
 
	return AiChooseTrainToBuild(v->u.rail.railtype, avail_money, 0, v->tile);
 
}
 

	
 
static EngineID AiChooseShipToReplaceWith(const Player* p, const Vehicle* v)
 
{
 
	error("!AiChooseShipToReplaceWith");
 

	
 
	/* maybe useless, but avoids compiler warning this way */
 
	return INVALID_ENGINE;
 
}
 

	
 
static void AiHandleGotoDepot(Player *p, int cmd)
 
{
 
	if (p->ai.cur_veh->current_order.type != OT_GOTO_DEPOT)
 
		DoCommand(0, p->ai.cur_veh->index, 0, DC_EXEC, cmd);
 

	
 
	if (++p->ai.state_counter <= 1387) {
 
		p->ai.state = AIS_VEH_DO_REPLACE_VEHICLE;
 
		return;
 
	}
 

	
 
	if (p->ai.cur_veh->current_order.type == OT_GOTO_DEPOT) {
 
		p->ai.cur_veh->current_order.type = OT_DUMMY;
 
		p->ai.cur_veh->current_order.flags = 0;
 
		InvalidateWindow(WC_VEHICLE_VIEW, p->ai.cur_veh->index);
 
	}
 
}
 

	
 
static void AiRestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak)
 
{
 
	uint i;
 

	
 
	for (i = 0; bak->order[i].type != OT_NOTHING; i++) {
 
		if (!DoCommandP(0, v->index + (i << 16), PackOrder(&bak->order[i]), NULL, CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK))
 
			break;
 
	}
 
}
 

	
 
static void AiHandleReplaceTrain(Player *p)
 
{
 
	const Vehicle* v = p->ai.cur_veh;
 
	BackuppedOrders orderbak[1];
 
	EngineID veh;
 

	
 
	// wait until the vehicle reaches the depot.
 
	if (!IsTileDepotType(v->tile, TRANSPORT_RAIL) || v->u.rail.track != 0x80 || !(v->vehstatus&VS_STOPPED)) {
 
		AiHandleGotoDepot(p, CMD_SEND_TRAIN_TO_DEPOT);
 
		return;
 
	}
 

	
 
	veh = AiChooseTrainToReplaceWith(p, v);
 
	if (veh != INVALID_ENGINE) {
 
		TileIndex tile;
 

	
 
		BackupVehicleOrders(v, orderbak);
 
		tile = v->tile;
 

	
 
		if (!CmdFailed(DoCommand(0, v->index, 2, DC_EXEC, CMD_SELL_RAIL_WAGON)) &&
 
				!CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE))) {
 
			VehicleID veh = _new_vehicle_id;
 
			AiRestoreVehicleOrders(GetVehicle(veh), orderbak);
 
			DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_TRAIN);
 

	
 
			DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
 
		}
 
	}
 
}
 

	
 
static void AiHandleReplaceRoadVeh(Player *p)
 
{
 
	const Vehicle* v = p->ai.cur_veh;
 
	BackuppedOrders orderbak[1];
 
	EngineID veh;
 

	
 
	if (!IsRoadVehInDepotStopped(v)) {
 
		AiHandleGotoDepot(p, CMD_SEND_ROADVEH_TO_DEPOT);
 
		return;
 
	}
 

	
 
	veh = AiChooseRoadVehToReplaceWith(p, v);
 
	if (veh != INVALID_ENGINE) {
 
		TileIndex tile;
 

	
 
		BackupVehicleOrders(v, orderbak);
 
		tile = v->tile;
 

	
 
		if (!CmdFailed(DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH)) &&
 
				!CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_ROAD_VEH))) {
 
			VehicleID veh = _new_vehicle_id;
 

	
 
			AiRestoreVehicleOrders(GetVehicle(veh), orderbak);
 
			DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_ROADVEH);
 
			DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
 
		}
 
	}
 
}
 

	
 
static void AiHandleReplaceAircraft(Player *p)
 
{
 
	const Vehicle* v = p->ai.cur_veh;
 
	BackuppedOrders orderbak[1];
 
	EngineID veh;
 

	
 
	if (!IsAircraftInHangarStopped(v)) {
 
		AiHandleGotoDepot(p, CMD_SEND_AIRCRAFT_TO_HANGAR);
 
		return;
 
	}
 

	
 
	veh = AiChooseAircraftToReplaceWith(p, v);
 
	if (veh != INVALID_ENGINE) {
 
		TileIndex tile;
 

	
 
		BackupVehicleOrders(v, orderbak);
 
		tile = v->tile;
 

	
 
		if (!CmdFailed(DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_AIRCRAFT)) &&
 
				!CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_AIRCRAFT))) {
 
			VehicleID veh = _new_vehicle_id;
 
			AiRestoreVehicleOrders(GetVehicle(veh), orderbak);
 
			DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_AIRCRAFT);
 

	
 
			DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
 
		}
 
	}
 
}
 

	
 
static void AiHandleReplaceShip(Player *p)
 
{
 
	error("!AiHandleReplaceShip");
 
}
 

	
 
typedef EngineID CheckReplaceProc(const Player* p, const Vehicle* v);
 

	
 
static CheckReplaceProc* const _veh_check_replace_proc[] = {
 
	AiChooseTrainToReplaceWith,
 
	AiChooseRoadVehToReplaceWith,
 
	AiChooseShipToReplaceWith,
 
	AiChooseAircraftToReplaceWith,
 
};
 

	
 
typedef void DoReplaceProc(Player *p);
 
static DoReplaceProc* const _veh_do_replace_proc[] = {
 
	AiHandleReplaceTrain,
 
	AiHandleReplaceRoadVeh,
 
	AiHandleReplaceShip,
 
	AiHandleReplaceAircraft
 
};
 

	
 
static void AiStateCheckReplaceVehicle(Player *p)
 
{
 
	const Vehicle* v = p->ai.cur_veh;
 

	
 
	if (!IsValidVehicle(v) ||
 
			v->owner != _current_player ||
 
			v->type > VEH_Ship ||
 
			_veh_check_replace_proc[v->type - VEH_Train](p, v) == INVALID_ENGINE) {
 
		p->ai.state = AIS_VEH_LOOP;
 
	} else {
 
		p->ai.state_counter = 0;
 
		p->ai.state = AIS_VEH_DO_REPLACE_VEHICLE;
 
	}
 
}
 

	
 
static void AiStateDoReplaceVehicle(Player *p)
 
{
 
	const Vehicle* v = p->ai.cur_veh;
 

	
 
	p->ai.state = AIS_VEH_LOOP;
 
	// vehicle is not owned by the player anymore, something went very wrong.
 
	if (!IsValidVehicle(v) || v->owner != _current_player) return;
 
	_veh_do_replace_proc[v->type - VEH_Train](p);
 
}
 

	
 
typedef struct FoundRoute {
 
	int distance;
 
	CargoID cargo;
 
	void *from;
 
	void *to;
 
} FoundRoute;
 

	
 
static Town *AiFindRandomTown(void)
 
{
 
	return GetRandomTown();
 
}
 

	
 
static Industry *AiFindRandomIndustry(void)
 
{
 
	return GetRandomIndustry();
 
}
 

	
 
static void AiFindSubsidyIndustryRoute(FoundRoute *fr)
 
{
 
	uint i;
 
	CargoID cargo;
 
	const Subsidy* s;
 
	Industry* from;
 
	TileIndex to_xy;
 

	
 
	// initially error
 
	fr->distance = -1;
 

	
 
	// Randomize subsidy index..
 
	i = RandomRange(lengthof(_subsidies) * 3);
 
	if (i >= lengthof(_subsidies)) return;
 

	
 
	s = &_subsidies[i];
 

	
 
	// Don't want passengers or mail
 
	cargo = s->cargo_type;
 
	if (cargo == CT_INVALID ||
 
			cargo == CT_PASSENGERS ||
 
			cargo == CT_MAIL ||
 
			s->age > 7) {
 
		return;
 
	}
 
	fr->cargo = cargo;
 

	
 
	fr->from = from = GetIndustry(s->from);
 

	
 
	if (cargo == CT_GOODS || cargo == CT_FOOD) {
 
		Town* to_tow = GetTown(s->to);
 

	
 
		if (to_tow->population < (cargo == CT_FOOD ? 200U : 900U)) return; // error
 
		fr->to = to_tow;
 
		to_xy = to_tow->xy;
 
	} else {
 
		Industry* to_ind = GetIndustry(s->to);
 

	
 
		fr->to = to_ind;
 
		to_xy = to_ind->xy;
 
	}
 

	
 
	fr->distance = DistanceManhattan(from->xy, to_xy);
 
}
 

	
 
static void AiFindSubsidyPassengerRoute(FoundRoute *fr)
 
{
 
	uint i;
 
	const Subsidy* s;
 
	Town *from,*to;
 

	
 
	// initially error
 
	fr->distance = -1;
 

	
 
	// Randomize subsidy index..
 
	i = RandomRange(lengthof(_subsidies) * 3);
 
	if (i >= lengthof(_subsidies)) return;
 

	
 
	s = &_subsidies[i];
 

	
 
	// Only want passengers
 
	if (s->cargo_type != CT_PASSENGERS || s->age > 7) return;
 
	fr->cargo = s->cargo_type;
 

	
 
	fr->from = from = GetTown(s->from);
 
	fr->to = to = GetTown(s->to);
 

	
 
	// They must be big enough
 
	if (from->population < 400 || to->population < 400) return;
 

	
 
	fr->distance = DistanceManhattan(from->xy, to->xy);
 
}
 

	
 
static void AiFindRandomIndustryRoute(FoundRoute *fr)
 
{
 
	Industry* i;
 
	uint32 r;
 
	CargoID cargo;
 

	
 
	// initially error
 
	fr->distance = -1;
 

	
 
	r = Random();
 

	
 
	// pick a source
 
	fr->from = i = AiFindRandomIndustry();
 
	if (i == NULL) return;
 

	
 
	// pick a random produced cargo
 
	cargo = i->produced_cargo[0];
 
	if (r & 1 && i->produced_cargo[1] != CT_INVALID) cargo = i->produced_cargo[1];
 

	
 
	fr->cargo = cargo;
 

	
 
	// don't allow passengers
 
	if (cargo == CT_INVALID || cargo == CT_PASSENGERS) return;
 

	
 
	if (cargo != CT_GOODS && cargo != CT_FOOD) {
 
		// pick a dest, and see if it can receive
 
		Industry* i2 = AiFindRandomIndustry();
 

	
 
		if (i2 == NULL || i == i2 || (
 
					i2->accepts_cargo[0] != cargo &&
 
					i2->accepts_cargo[1] != cargo &&
 
					i2->accepts_cargo[2] != cargo)
 
				) {
 
			return;
 
		}
 

	
 
		fr->to = i2;
 
		fr->distance = DistanceManhattan(i->xy, i2->xy);
 
	} else {
 
		// pick a dest town, and see if it's big enough
 
		Town* t = AiFindRandomTown();
 

	
 
		if (t == NULL || t->population < (cargo == CT_FOOD ? 200U : 900U)) return;
 

	
 
		fr->to = t;
 
		fr->distance = DistanceManhattan(i->xy, t->xy);
 
	}
 
}
 

	
 
static void AiFindRandomPassengerRoute(FoundRoute *fr)
 
{
 
	Town* source;
 
	Town* dest;
 

	
 
	// initially error
 
	fr->distance = -1;
 

	
 
	fr->from = source = AiFindRandomTown();
 
	if (source == NULL || source->population < 400) return;
 

	
 
	fr->to = dest = AiFindRandomTown();
 
	if (dest == NULL || source == dest || dest->population < 400) return;
 

	
 
	fr->distance = DistanceManhattan(source->xy, dest->xy);
 
}
 

	
 
// Warn: depends on 'xy' being the first element in both Town and Industry
 
#define GET_TOWN_OR_INDUSTRY_TILE(p) (((Town*)(p))->xy)
 

	
 
static bool AiCheckIfRouteIsGood(Player *p, FoundRoute *fr, byte bitmask)
 
{
 
	TileIndex from_tile, to_tile;
 
	Station *st;
 
	int dist;
 
	uint same_station = 0;
 

	
 
	// Make sure distance to closest station is < 37 pixels.
 
	from_tile = GET_TOWN_OR_INDUSTRY_TILE(fr->from);
 
	to_tile = GET_TOWN_OR_INDUSTRY_TILE(fr->to);
 

	
 
	dist = 0xFFFF;
 
	FOR_ALL_STATIONS(st) {
 
		int cur;
 

	
 
		if (st->owner != _current_player) continue;
 
		cur = DistanceMax(from_tile, st->xy);
 
		if (cur < dist) dist = cur;
 
		cur = DistanceMax(to_tile, st->xy);
 
		if (cur < dist) dist = cur;
 
		if (to_tile == from_tile && st->xy == to_tile) same_station++;
 
	}
 

	
 
	// To prevent the AI from building ten busstations in the same town, do some calculations
 
	//  For each road or airport station, we want 350 of population!
 
	if ((bitmask == 2 || bitmask == 4) &&
 
			same_station > 2 &&
 
			((Town*)fr->from)->population < same_station * 350) {
 
		return false;
 
	}
 

	
 
	if (dist != 0xFFFF && dist > 37) return false;
 

	
 
	if (p->ai.route_type_mask != 0 &&
 
			!(p->ai.route_type_mask & bitmask) &&
 
			!CHANCE16(1, 5)) {
 
		return false;
 
	}
 

	
 
	if (fr->cargo == CT_PASSENGERS || fr->cargo == CT_MAIL) {
 
		const Town* from = fr->from;
 
		const Town* to   = fr->to;
 

	
 
		if (from->pct_pass_transported > 0x99 ||
 
				to->pct_pass_transported > 0x99) {
 
			return false;
 
		}
 

	
 
		// Make sure it has a reasonably good rating
 
		if (from->ratings[_current_player] < -100 ||
 
				to->ratings[_current_player] < -100) {
 
			return false;
 
		}
 
	} else {
 
		const Industry* i = (const Industry*)fr->from;
 

	
 
		if (i->pct_transported[fr->cargo != i->produced_cargo[0]] > 0x99 ||
 
				i->total_production[fr->cargo != i->produced_cargo[0]] == 0) {
 
			return false;
 
		}
 
	}
 

	
 
	p->ai.route_type_mask |= bitmask;
 
	return true;
 
}
 

	
 
static byte AiGetDirectionBetweenTiles(TileIndex a, TileIndex b)
 
{
 
	byte i = (TileX(a) < TileX(b)) ? 1 : 0;
 
	if (TileY(a) >= TileY(b)) i ^= 3;
 
	return i;
 
}
 

	
 
static TileIndex AiGetPctTileBetween(TileIndex a, TileIndex b, byte pct)
 
{
 
	return TileXY(
 
		TileX(a) + ((TileX(b) - TileX(a)) * pct >> 8),
 
		TileY(a) + ((TileY(b) - TileY(a)) * pct >> 8)
 
	);
 
}
 

	
 
static void AiWantLongIndustryRoute(Player *p)
 
{
 
	int i;
 
	FoundRoute fr;
 

	
 
	i = 60;
 
	for (;;) {
 
		// look for one from the subsidy list
 
		AiFindSubsidyIndustryRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 60, 90 + 1)) break;
 

	
 
		// try a random one
 
		AiFindRandomIndustryRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 60, 90 + 1)) break;
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	if (!AiCheckIfRouteIsGood(p, &fr, 1)) return;
 

	
 
	// Fill the source field
 
	p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
 
	p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
 

	
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 9;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.unk6 = 1;
 
	p->ai.src.unk7 = 0;
 
	p->ai.src.buildcmd_a = 0x24;
 
	p->ai.src.buildcmd_b = 0xFF;
 
	p->ai.src.direction = AiGetDirectionBetweenTiles(
 
		p->ai.src.spec_tile,
 
		p->ai.dst.spec_tile
 
	);
 
	p->ai.src.cargo = fr.cargo | 0x80;
 

	
 
	// Fill the dest field
 

	
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 9;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.unk6 = 1;
 
	p->ai.dst.unk7 = 0;
 
	p->ai.dst.buildcmd_a = 0x34;
 
	p->ai.dst.buildcmd_b = 0xFF;
 
	p->ai.dst.direction = AiGetDirectionBetweenTiles(
 
		p->ai.dst.spec_tile,
 
		p->ai.src.spec_tile
 
	);
 
	p->ai.dst.cargo = fr.cargo;
 

	
 
	// Fill middle field 1
 
	p->ai.mid1.spec_tile = AiGetPctTileBetween(
 
		p->ai.src.spec_tile,
 
		p->ai.dst.spec_tile,
 
		0x55
 
	);
 
	p->ai.mid1.use_tile = 0;
 
	p->ai.mid1.rand_rng = 6;
 
	p->ai.mid1.cur_building_rule = 0xFF;
 
	p->ai.mid1.unk6 = 2;
 
	p->ai.mid1.unk7 = 1;
 
	p->ai.mid1.buildcmd_a = 0x30;
 
	p->ai.mid1.buildcmd_b = 0xFF;
 
	p->ai.mid1.direction = p->ai.src.direction;
 
	p->ai.mid1.cargo = fr.cargo;
 

	
 
	// Fill middle field 2
 
	p->ai.mid2.spec_tile = AiGetPctTileBetween(
 
		p->ai.src.spec_tile,
 
		p->ai.dst.spec_tile,
 
		0xAA
 
	);
 
	p->ai.mid2.use_tile = 0;
 
	p->ai.mid2.rand_rng = 6;
 
	p->ai.mid2.cur_building_rule = 0xFF;
 
	p->ai.mid2.unk6 = 2;
 
	p->ai.mid2.unk7 = 1;
 
	p->ai.mid2.buildcmd_a = 0xFF;
 
	p->ai.mid2.buildcmd_b = 0xFF;
 
	p->ai.mid2.direction = p->ai.dst.direction;
 
	p->ai.mid2.cargo = fr.cargo;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = fr.cargo;
 
	p->ai.num_wagons = 3;
 
	p->ai.build_kind = 2;
 
	p->ai.num_build_rec = 4;
 
	p->ai.num_loco_to_build = 2;
 
	p->ai.num_want_fullload = 2;
 
	p->ai.wagon_list[0] = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 

	
 
	p->ai.state = AIS_BUILD_DEFAULT_RAIL_BLOCKS;
 
	p->ai.state_mode = -1;
 
	p->ai.state_counter = 0;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantMediumIndustryRoute(Player *p)
 
{
 
	int i;
 
	FoundRoute fr;
 

	
 
	i = 60;
 
	for (;;) {
 
		// look for one from the subsidy list
 
		AiFindSubsidyIndustryRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 40, 60 + 1)) break;
 

	
 
		// try a random one
 
		AiFindRandomIndustryRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 40, 60 + 1)) break;
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	if (!AiCheckIfRouteIsGood(p, &fr, 1)) return;
 

	
 
	// Fill the source field
 
	p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 9;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.unk6 = 1;
 
	p->ai.src.unk7 = 0;
 
	p->ai.src.buildcmd_a = 0x10;
 
	p->ai.src.buildcmd_b = 0xFF;
 
	p->ai.src.direction = AiGetDirectionBetweenTiles(
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.from),
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.to)
 
	);
 
	p->ai.src.cargo = fr.cargo | 0x80;
 

	
 
	// Fill the dest field
 
	p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 9;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.unk6 = 1;
 
	p->ai.dst.unk7 = 0;
 
	p->ai.dst.buildcmd_a = 0xFF;
 
	p->ai.dst.buildcmd_b = 0xFF;
 
	p->ai.dst.direction = AiGetDirectionBetweenTiles(
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.to),
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.from)
 
	);
 
	p->ai.dst.cargo = fr.cargo;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = fr.cargo;
 
	p->ai.num_wagons = 3;
 
	p->ai.build_kind = 1;
 
	p->ai.num_build_rec = 2;
 
	p->ai.num_loco_to_build = 1;
 
	p->ai.num_want_fullload = 1;
 
	p->ai.wagon_list[0] = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 
	p->ai.state = AIS_BUILD_DEFAULT_RAIL_BLOCKS;
 
	p->ai.state_mode = -1;
 
	p->ai.state_counter = 0;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantShortIndustryRoute(Player *p)
 
{
 
	int i;
 
	FoundRoute fr;
 

	
 
	i = 60;
 
	for (;;) {
 
		// look for one from the subsidy list
 
		AiFindSubsidyIndustryRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 15, 40 + 1)) break;
 

	
 
		// try a random one
 
		AiFindRandomIndustryRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 15, 40 + 1)) break;
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	if (!AiCheckIfRouteIsGood(p, &fr, 1)) return;
 

	
 
	// Fill the source field
 
	p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 9;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.unk6 = 1;
 
	p->ai.src.unk7 = 0;
 
	p->ai.src.buildcmd_a = 0x10;
 
	p->ai.src.buildcmd_b = 0xFF;
 
	p->ai.src.direction = AiGetDirectionBetweenTiles(
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.from),
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.to)
 
	);
 
	p->ai.src.cargo = fr.cargo | 0x80;
 

	
 
	// Fill the dest field
 
	p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 9;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.unk6 = 1;
 
	p->ai.dst.unk7 = 0;
 
	p->ai.dst.buildcmd_a = 0xFF;
 
	p->ai.dst.buildcmd_b = 0xFF;
 
	p->ai.dst.direction = AiGetDirectionBetweenTiles(
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.to),
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.from)
 
	);
 
	p->ai.dst.cargo = fr.cargo;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = fr.cargo;
 
	p->ai.num_wagons = 2;
 
	p->ai.build_kind = 1;
 
	p->ai.num_build_rec = 2;
 
	p->ai.num_loco_to_build = 1;
 
	p->ai.num_want_fullload = 1;
 
	p->ai.wagon_list[0] = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 
	p->ai.state = AIS_BUILD_DEFAULT_RAIL_BLOCKS;
 
	p->ai.state_mode = -1;
 
	p->ai.state_counter = 0;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantMailRoute(Player *p)
 
{
 
	int i;
 
	FoundRoute fr;
 

	
 
	i = 60;
 
	for (;;) {
 
		// look for one from the subsidy list
 
		AiFindSubsidyPassengerRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 60, 110 + 1)) break;
 

	
 
		// try a random one
 
		AiFindRandomPassengerRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 60, 110 + 1)) break;
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	fr.cargo = CT_MAIL;
 
	if (!AiCheckIfRouteIsGood(p, &fr, 1)) return;
 

	
 
	// Fill the source field
 
	p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 7;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.unk6 = 1;
 
	p->ai.src.unk7 = 0;
 
	p->ai.src.buildcmd_a = 0x24;
 
	p->ai.src.buildcmd_b = 0xFF;
 
	p->ai.src.direction = AiGetDirectionBetweenTiles(
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.from),
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.to)
 
	);
 
	p->ai.src.cargo = fr.cargo;
 

	
 
	// Fill the dest field
 
	p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 7;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.unk6 = 1;
 
	p->ai.dst.unk7 = 0;
 
	p->ai.dst.buildcmd_a = 0x34;
 
	p->ai.dst.buildcmd_b = 0xFF;
 
	p->ai.dst.direction = AiGetDirectionBetweenTiles(
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.to),
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.from)
 
	);
 
	p->ai.dst.cargo = fr.cargo;
 

	
 
	// Fill middle field 1
 
	p->ai.mid1.spec_tile = AiGetPctTileBetween(
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.from),
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.to),
 
		0x55
 
	);
 
	p->ai.mid1.use_tile = 0;
 
	p->ai.mid1.rand_rng = 6;
 
	p->ai.mid1.cur_building_rule = 0xFF;
 
	p->ai.mid1.unk6 = 2;
 
	p->ai.mid1.unk7 = 1;
 
	p->ai.mid1.buildcmd_a = 0x30;
 
	p->ai.mid1.buildcmd_b = 0xFF;
 
	p->ai.mid1.direction = p->ai.src.direction;
 
	p->ai.mid1.cargo = fr.cargo;
 

	
 
	// Fill middle field 2
 
	p->ai.mid2.spec_tile = AiGetPctTileBetween(
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.from),
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.to),
 
		0xAA
 
	);
 
	p->ai.mid2.use_tile = 0;
 
	p->ai.mid2.rand_rng = 6;
 
	p->ai.mid2.cur_building_rule = 0xFF;
 
	p->ai.mid2.unk6 = 2;
 
	p->ai.mid2.unk7 = 1;
 
	p->ai.mid2.buildcmd_a = 0xFF;
 
	p->ai.mid2.buildcmd_b = 0xFF;
 
	p->ai.mid2.direction = p->ai.dst.direction;
 
	p->ai.mid2.cargo = fr.cargo;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = fr.cargo;
 
	p->ai.num_wagons = 3;
 
	p->ai.build_kind = 2;
 
	p->ai.num_build_rec = 4;
 
	p->ai.num_loco_to_build = 2;
 
	p->ai.num_want_fullload = 0;
 
	p->ai.wagon_list[0] = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 
	p->ai.state = AIS_BUILD_DEFAULT_RAIL_BLOCKS;
 
	p->ai.state_mode = -1;
 
	p->ai.state_counter = 0;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantPassengerRoute(Player *p)
 
{
 
	int i;
 
	FoundRoute fr;
 

	
 
	i = 60;
 
	for (;;) {
 
		// look for one from the subsidy list
 
		AiFindSubsidyPassengerRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 0, 55 + 1)) break;
 

	
 
		// try a random one
 
		AiFindRandomPassengerRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 0, 55 + 1)) break;
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	fr.cargo = CT_PASSENGERS;
 
	if (!AiCheckIfRouteIsGood(p, &fr, 1)) return;
 

	
 
	// Fill the source field
 
	p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 7;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.unk6 = 1;
 
	p->ai.src.unk7 = 0;
 
	p->ai.src.buildcmd_a = 0x10;
 
	p->ai.src.buildcmd_b = 0xFF;
 
	p->ai.src.direction = AiGetDirectionBetweenTiles(
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.from),
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.to)
 
	);
 
	p->ai.src.cargo = fr.cargo;
 

	
 
	// Fill the dest field
 
	p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 7;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.unk6 = 1;
 
	p->ai.dst.unk7 = 0;
 
	p->ai.dst.buildcmd_a = 0xFF;
 
	p->ai.dst.buildcmd_b = 0xFF;
 
	p->ai.dst.direction = AiGetDirectionBetweenTiles(
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.to),
 
		GET_TOWN_OR_INDUSTRY_TILE(fr.from)
 
	);
 
	p->ai.dst.cargo = fr.cargo;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = fr.cargo;
 
	p->ai.num_wagons = 2;
 
	p->ai.build_kind = 1;
 
	p->ai.num_build_rec = 2;
 
	p->ai.num_loco_to_build = 1;
 
	p->ai.num_want_fullload = 0;
 
	p->ai.wagon_list[0] = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 
	p->ai.state = AIS_BUILD_DEFAULT_RAIL_BLOCKS;
 
	p->ai.state_mode = -1;
 
	p->ai.state_counter = 0;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantTrainRoute(Player *p)
 
{
 
	uint16 r = GB(Random(), 0, 16);
 

	
 
	p->ai.railtype_to_use = GetBestRailtype(p);
 

	
 
	if (r > 0xD000) {
 
		AiWantLongIndustryRoute(p);
 
	} else if (r > 0x6000) {
 
		AiWantMediumIndustryRoute(p);
 
	} else if (r > 0x1000) {
 
		AiWantShortIndustryRoute(p);
 
	} else if (r > 0x800) {
 
		AiWantPassengerRoute(p);
 
	} else {
 
		AiWantMailRoute(p);
 
	}
 
}
 

	
 
static void AiWantLongRoadIndustryRoute(Player *p)
 
{
 
	int i;
 
	FoundRoute fr;
 

	
 
	i = 60;
 
	for (;;) {
 
		// look for one from the subsidy list
 
		AiFindSubsidyIndustryRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 35, 55 + 1)) break;
 

	
 
		// try a random one
 
		AiFindRandomIndustryRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 35, 55 + 1)) break;
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	if (!AiCheckIfRouteIsGood(p, &fr, 2)) return;
 

	
 
	// Fill the source field
 
	p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 9;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.buildcmd_a = 1;
 
	p->ai.src.direction = 0;
 
	p->ai.src.cargo = fr.cargo | 0x80;
 

	
 
	// Fill the dest field
 
	p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 9;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.buildcmd_a = 0xFF;
 
	p->ai.dst.direction = 0;
 
	p->ai.dst.cargo = fr.cargo;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = fr.cargo;
 
	p->ai.num_build_rec = 2;
 
	p->ai.num_loco_to_build = 5;
 
	p->ai.num_want_fullload = 5;
 

	
 
//	p->ai.loco_id = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 

	
 
	p->ai.state = AIS_BUILD_DEFAULT_ROAD_BLOCKS;
 
	p->ai.state_mode = -1;
 
	p->ai.state_counter = 0;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantMediumRoadIndustryRoute(Player *p)
 
{
 
	int i;
 
	FoundRoute fr;
 

	
 
	i = 60;
 
	for (;;) {
 
		// look for one from the subsidy list
 
		AiFindSubsidyIndustryRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 15, 40 + 1)) break;
 

	
 
		// try a random one
 
		AiFindRandomIndustryRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 15, 40 + 1)) break;
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	if (!AiCheckIfRouteIsGood(p, &fr, 2)) return;
 

	
 
	// Fill the source field
 
	p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 9;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.buildcmd_a = 1;
 
	p->ai.src.direction = 0;
 
	p->ai.src.cargo = fr.cargo | 0x80;
 

	
 
	// Fill the dest field
 
	p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 9;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.buildcmd_a = 0xFF;
 
	p->ai.dst.direction = 0;
 
	p->ai.dst.cargo = fr.cargo;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = fr.cargo;
 
	p->ai.num_build_rec = 2;
 
	p->ai.num_loco_to_build = 3;
 
	p->ai.num_want_fullload = 3;
 

	
 
//	p->ai.loco_id = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 

	
 
	p->ai.state = AIS_BUILD_DEFAULT_ROAD_BLOCKS;
 
	p->ai.state_mode = -1;
 
	p->ai.state_counter = 0;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantLongRoadPassengerRoute(Player *p)
 
{
 
	int i;
 
	FoundRoute fr;
 

	
 
	i = 60;
 
	for (;;) {
 
		// look for one from the subsidy list
 
		AiFindSubsidyPassengerRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 55, 180 + 1)) break;
 

	
 
		// try a random one
 
		AiFindRandomPassengerRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 55, 180 + 1)) break;
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	fr.cargo = CT_PASSENGERS;
 

	
 
	if (!AiCheckIfRouteIsGood(p, &fr, 2)) return;
 

	
 
	// Fill the source field
 
	p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 10;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.buildcmd_a = 1;
 
	p->ai.src.direction = 0;
 
	p->ai.src.cargo = CT_PASSENGERS;
 

	
 
	// Fill the dest field
 
	p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 10;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.buildcmd_a = 0xFF;
 
	p->ai.dst.direction = 0;
 
	p->ai.dst.cargo = CT_PASSENGERS;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = CT_PASSENGERS;
 
	p->ai.num_build_rec = 2;
 
	p->ai.num_loco_to_build = 4;
 
	p->ai.num_want_fullload = 0;
 

	
 
//	p->ai.loco_id = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 

	
 
	p->ai.state = AIS_BUILD_DEFAULT_ROAD_BLOCKS;
 
	p->ai.state_mode = -1;
 
	p->ai.state_counter = 0;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantPassengerRouteInsideTown(Player *p)
 
{
 
	int i;
 
	FoundRoute fr;
 
	Town *t;
 

	
 
	i = 60;
 
	for (;;) {
 
		// Find a town big enough
 
		t = AiFindRandomTown();
 
		if (t != NULL && t->population >= 700) break;
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	fr.cargo = CT_PASSENGERS;
 
	fr.from = fr.to = t;
 

	
 
	if (!AiCheckIfRouteIsGood(p, &fr, 2)) return;
 

	
 
	// Fill the source field
 
	p->ai.src.spec_tile = t->xy;
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 10;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.buildcmd_a = 1;
 
	p->ai.src.direction = 0;
 
	p->ai.src.cargo = CT_PASSENGERS;
 

	
 
	// Fill the dest field
 
	p->ai.dst.spec_tile = t->xy;
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 10;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.buildcmd_a = 0xFF;
 
	p->ai.dst.direction = 0;
 
	p->ai.dst.cargo = CT_PASSENGERS;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = CT_PASSENGERS;
 
	p->ai.num_build_rec = 2;
 
	p->ai.num_loco_to_build = 2;
 
	p->ai.num_want_fullload = 0;
 

	
 
//	p->ai.loco_id = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 

	
 
	p->ai.state = AIS_BUILD_DEFAULT_ROAD_BLOCKS;
 
	p->ai.state_mode = -1;
 
	p->ai.state_counter = 0;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantRoadRoute(Player *p)
 
{
 
	uint16 r = GB(Random(), 0, 16);
 

	
 
	if (r > 0x4000) {
 
		AiWantLongRoadIndustryRoute(p);
 
	} else if (r > 0x2000) {
 
		AiWantMediumRoadIndustryRoute(p);
 
	} else if (r > 0x1000) {
 
		AiWantLongRoadPassengerRoute(p);
 
	} else {
 
		AiWantPassengerRouteInsideTown(p);
 
	}
 
}
 

	
 
static void AiWantPassengerAircraftRoute(Player *p)
 
{
 
	FoundRoute fr;
 
	int i;
 

	
 
	i = 60;
 
	for (;;) {
 
		// look for one from the subsidy list
 
		AiFindSubsidyPassengerRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 0, 95 + 1)) break;
 

	
 
		// try a random one
 
		AiFindRandomPassengerRoute(&fr);
 
		if (IS_INT_INSIDE(fr.distance, 0, 95 + 1)) break;
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	fr.cargo = CT_PASSENGERS;
 
	if (!AiCheckIfRouteIsGood(p, &fr, 4)) return;
 

	
 

	
 
	// Fill the source field
 
	p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 12;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.cargo = fr.cargo;
 

	
 
	// Fill the dest field
 
	p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 12;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.cargo = fr.cargo;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = fr.cargo;
 
	p->ai.build_kind = 0;
 
	p->ai.num_build_rec = 2;
 
	p->ai.num_loco_to_build = 1;
 
	p->ai.num_want_fullload = 1;
 
//	p->ai.loco_id = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 

	
 
	p->ai.state = AIS_AIRPORT_STUFF;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantOilRigAircraftRoute(Player *p)
 
{
 
	int i;
 
	FoundRoute fr;
 
	Town *t;
 
	Industry *in;
 

	
 
	i = 60;
 
	for (;;) {
 
		// Find a town
 
		t = AiFindRandomTown();
 
		if (t != NULL) {
 
			// Find a random oil rig industry
 
			in = AiFindRandomIndustry();
 
			if (in != NULL && in->type == IT_OIL_RIG) {
 
				if (DistanceManhattan(t->xy, in->xy) < 60)
 
					break;
 
			}
 
		}
 

	
 
		// only test 60 times
 
		if (--i == 0) return;
 
	}
 

	
 
	fr.cargo = CT_PASSENGERS;
 
	fr.from = fr.to = t;
 

	
 
	if (!AiCheckIfRouteIsGood(p, &fr, 4)) return;
 

	
 
	// Fill the source field
 
	p->ai.src.spec_tile = t->xy;
 
	p->ai.src.use_tile = 0;
 
	p->ai.src.rand_rng = 12;
 
	p->ai.src.cur_building_rule = 0xFF;
 
	p->ai.src.cargo = CT_PASSENGERS;
 

	
 
	// Fill the dest field
 
	p->ai.dst.spec_tile = in->xy;
 
	p->ai.dst.use_tile = 0;
 
	p->ai.dst.rand_rng = 5;
 
	p->ai.dst.cur_building_rule = 0xFF;
 
	p->ai.dst.cargo = CT_PASSENGERS;
 

	
 
	// Fill common fields
 
	p->ai.cargo_type = CT_PASSENGERS;
 
	p->ai.build_kind = 1;
 
	p->ai.num_build_rec = 2;
 
	p->ai.num_loco_to_build = 1;
 
	p->ai.num_want_fullload = 0;
 
//	p->ai.loco_id = INVALID_VEHICLE;
 
	p->ai.order_list_blocks[0] = 0;
 
	p->ai.order_list_blocks[1] = 1;
 
	p->ai.order_list_blocks[2] = 255;
 

	
 
	p->ai.state = AIS_AIRPORT_STUFF;
 
	p->ai.timeout_counter = 0;
 
}
 

	
 
static void AiWantAircraftRoute(Player *p)
 
{
 
	uint16 r = (uint16)Random();
 

	
 
	if (r >= 0x2AAA || _date < 0x3912 + DAYS_TILL_ORIGINAL_BASE_YEAR) {
 
		AiWantPassengerAircraftRoute(p);
 
	} else {
 
		AiWantOilRigAircraftRoute(p);
 
	}
 
}
 

	
 
static void AiWantShipRoute(Player *p)
 
{
 
	// XXX
 
//	error("AiWaitShipRoute");
 
}
 

	
 

	
 

	
 
static void AiStateWantNewRoute(Player *p)
 
{
 
	uint16 r;
 
	int i;
 

	
 
	if (p->player_money < AiGetBasePrice(p) * 500) {
 
		p->ai.state = AIS_0;
 
		return;
 
	}
 

	
 
	i = 200;
 
	for (;;) {
 
		r = (uint16)Random();
 

	
 
		if (_patches.ai_disable_veh_train &&
 
				_patches.ai_disable_veh_roadveh &&
 
				_patches.ai_disable_veh_aircraft &&
 
				_patches.ai_disable_veh_ship) {
 
			return;
 
		}
 

	
 
		if (r < 0x7626) {
 
			if (_patches.ai_disable_veh_train) continue;
 
			AiWantTrainRoute(p);
 
		} else if (r < 0xC4EA) {
 
			if (_patches.ai_disable_veh_roadveh) continue;
 
			AiWantRoadRoute(p);
 
		} else if (r < 0xD89B) {
 
			if (_patches.ai_disable_veh_aircraft) continue;
 
			AiWantAircraftRoute(p);
 
		} else {
 
			if (_patches.ai_disable_veh_ship) continue;
 
			AiWantShipRoute(p);
 
		}
 

	
 
		// got a route?
 
		if (p->ai.state != AIS_WANT_NEW_ROUTE) break;
 

	
 
		// time out?
 
		if (--i == 0) {
 
			if (++p->ai.state_counter == 556) p->ai.state = AIS_0;
 
			break;
 
		}
 
	}
 
}
 

	
 
static bool AiCheckTrackResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo)
 
{
 
	uint rad = (_patches.modified_catchment) ? CA_TRAIN : 4;
 

	
 
	for (; p->mode != 4; p++) {
 
		AcceptedCargo values;
 
		TileIndex tile2;
 
		uint w;
 
		uint h;
 

	
 
		if (p->mode != 1) continue;
 

	
 
		tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs));
 
		w = GB(p->attr, 1, 3);
 
		h = GB(p->attr, 4, 3);
 

	
 
		if (p->attr & 1) uintswap(w, h);
 

	
 
		if (cargo & 0x80) {
 
			GetProductionAroundTiles(values, tile2, w, h, rad);
 
			return values[cargo & 0x7F] != 0;
 
		} else {
 
			GetAcceptanceAroundTiles(values, tile2, w, h, rad);
 
			if (!(values[cargo] & ~7))
 
				return false;
 
			if (cargo != CT_MAIL)
 
				return true;
 
			return !!((values[cargo]>>1) & ~7);
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
static int32 AiDoBuildDefaultRailTrack(TileIndex tile, const AiDefaultBlockData* p, RailType railtype, byte flag)
 
{
 
	int32 ret;
 
	int32 total_cost = 0;
 
	Town *t = NULL;
 
	int rating = 0;
 
	int i,j,k;
 

	
 
	for (;;) {
 
		// This will seldomly overflow for valid reasons. Mask it to be on the safe side.
 
		uint c = TILE_MASK(tile + ToTileIndexDiff(p->tileoffs));
 

	
 
		_cleared_town = NULL;
 

	
 
		if (p->mode < 2) {
 
			if (p->mode == 0) {
 
				// Depot
 
				ret = DoCommand(c, railtype, p->attr, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_TRAIN_DEPOT);
 
			} else {
 
				// Station
 
				ret = DoCommand(c, (p->attr&1) | (p->attr>>4)<<8 | (p->attr>>1&7)<<16, railtype, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_RAILROAD_STATION);
 
			}
 

	
 
			if (CmdFailed(ret)) return CMD_ERROR;
 
			total_cost += ret;
 

	
 
clear_town_stuff:;
 
			if (_cleared_town != NULL) {
 
				if (t != NULL && t != _cleared_town)
 
					return CMD_ERROR;
 
				t = _cleared_town;
 
				rating += _cleared_town_rating;
 
			}
 
		} else if (p->mode == 2) {
 
			// Rail
 
			if (IsTileType(c, MP_RAILWAY)) return CMD_ERROR;
 

	
 
			j = p->attr;
 
			k = 0;
 

	
 
			// Build the rail
 
			for (i = 0; i != 6; i++, j >>= 1) {
 
				if (j&1) {
 
					k = i;
 
					ret = DoCommand(c, railtype, i, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL);
 
					if (CmdFailed(ret)) return CMD_ERROR;
 
					total_cost += ret;
 
				}
 
			}
 

	
 
			/* signals too? */
 
			if (j & 3) {
 
				// Can't build signals on a road.
 
				if (IsTileType(c, MP_STREET)) return CMD_ERROR;
 

	
 
				if (flag & DC_EXEC) {
 
					j = 4 - j;
 
					do {
 
						ret = DoCommand(c, k, 0, flag, CMD_BUILD_SIGNALS);
 
					} while (--j);
 
				} else {
 
					ret = _price.build_signals;
 
				}
 
				if (CmdFailed(ret)) return CMD_ERROR;
 
				total_cost += ret;
 
			}
 
		} else if (p->mode == 3) {
 
			//Clear stuff and then build single rail.
 
			if (GetTileSlope(c, NULL) != SLOPE_FLAT) return CMD_ERROR;
 
			ret = DoCommand(c, 0, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_LANDSCAPE_CLEAR);
 
			if (CmdFailed(ret)) return CMD_ERROR;
 
			total_cost += ret + _price.build_rail;
 

	
 
			if (flag & DC_EXEC) {
 
				DoCommand(c, railtype, p->attr&1, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_SINGLE_RAIL);
 
			}
 

	
 
			goto clear_town_stuff;
 
		} else {
 
			// Unk
 
			break;
 
		}
 

	
 
		p++;
 
	}
 

	
 
	if (!(flag & DC_EXEC)) {
 
		if (t != NULL && rating > t->ratings[_current_player]) {
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	return total_cost;
 
}
 

	
 
// Returns rule and cost
 
static int AiBuildDefaultRailTrack(TileIndex tile, byte p0, byte p1, byte p2, byte p3, byte dir, byte cargo, RailType railtype, int32* cost)
 
{
 
	int i;
 
	const AiDefaultRailBlock *p;
 

	
 
	for (i = 0; (p = _default_rail_track_data[i]) != NULL; i++) {
 
		if (p->p0 == p0 && p->p1 == p1 && p->p2 == p2 && p->p3 == p3 &&
 
				(p->dir == 0xFF || p->dir == dir || ((p->dir - 1) & 3) == dir)) {
 
			*cost = AiDoBuildDefaultRailTrack(tile, p->data, railtype, DC_NO_TOWN_RATING);
 
			if (!CmdFailed(*cost) && AiCheckTrackResources(tile, p->data, cargo))
 
				return i;
 
		}
 
	}
 

	
 
	return -1;
 
}
 

	
 
static const byte _terraform_up_flags[] = {
 
	14, 13, 12, 11,
 
	10,  9,  8,  7,
 
	 6,  5,  4,  3,
 
	 2,  1,  0,  1,
 
	 2,  1,  4,  1,
 
	 2,  1,  8,  1,
 
	 2,  1,  4,  2,
 
	 2,  1
 
};
 

	
 
static const byte _terraform_down_flags[] = {
 
	1,  2, 3,  4,
 
	5,  6, 1,  8,
 
	9, 10, 8, 12,
 
	4,  2, 0,  0,
 
	1,  2, 3,  4,
 
	5,  6, 2,  8,
 
	9, 10, 1, 12,
 
	8,  4
 
};
 

	
 
static void AiDoTerraformLand(TileIndex tile, int dir, int unk, int mode)
 
{
 
	PlayerID old_player;
 
	uint32 r;
 
	Slope slope;
 
	uint h;
 

	
 
	old_player = _current_player;
 
	_current_player = OWNER_NONE;
 

	
 
	r = Random();
 

	
 
	unk &= (int)r;
 

	
 
	do {
 
		tile = TILE_MASK(tile + TileOffsByDiagDir(dir));
 

	
 
		r >>= 2;
 
		if (r & 2) {
 
			dir++;
 
			if (r & 1) dir -= 2;
 
		}
 
		dir &= 3;
 
	} while (--unk >= 0);
 

	
 
	slope = GetTileSlope(tile, &h);
 

	
 
	if (slope != SLOPE_FLAT) {
 
		if (mode > 0 || (mode == 0 && !(r & 0xC))) {
 
			// Terraform up
 
			DoCommand(tile, _terraform_up_flags[slope - 1], 1,
 
				DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND);
 
		} else if (h != 0) {
 
			// Terraform down
 
			DoCommand(tile, _terraform_down_flags[slope - 1], 0,
 
				DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND);
 
		}
 
	}
 

	
 
	_current_player = old_player;
 
}
 

	
 
static void AiStateBuildDefaultRailBlocks(Player *p)
 
{
 
	uint i;
 
	int j;
 
	AiBuildRec *aib;
 
	int rule;
 
	int32 cost;
 

	
 
	// time out?
 
	if (++p->ai.timeout_counter == 1388) {
 
		p->ai.state = AIS_DELETE_RAIL_BLOCKS;
 
		return;
 
	}
 

	
 
	// do the following 8 times
 
	for (i = 0; i < 8; i++) {
 
		// check if we can build the default track
 
		aib = &p->ai.src;
 
		j = p->ai.num_build_rec;
 
		do {
 
			// this item has already been built?
 
			if (aib->cur_building_rule != 255) continue;
 

	
 
			// adjust the coordinate randomly,
 
			// to make sure that we find a position.
 
			aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng);
 

	
 
			// check if the track can be build there.
 
			rule = AiBuildDefaultRailTrack(aib->use_tile,
 
				p->ai.build_kind, p->ai.num_wagons,
 
				aib->unk6, aib->unk7,
 
				aib->direction, aib->cargo,
 
				p->ai.railtype_to_use,
 
				&cost
 
			);
 

	
 
			if (rule == -1) {
 
				// cannot build, terraform after a while
 
				if (p->ai.state_counter >= 600) {
 
					AiDoTerraformLand(aib->use_tile, Random()&3, 3, (int8)p->ai.state_mode);
 
				}
 
				// also try the other terraform direction
 
				if (++p->ai.state_counter >= 1000) {
 
					p->ai.state_counter = 0;
 
					p->ai.state_mode = -p->ai.state_mode;
 
				}
 
			} else if (CheckPlayerHasMoney(cost)) {
 
				int32 r;
 
				// player has money, build it.
 
				aib->cur_building_rule = rule;
 

	
 
				r = AiDoBuildDefaultRailTrack(
 
					aib->use_tile,
 
					_default_rail_track_data[rule]->data,
 
					p->ai.railtype_to_use,
 
					DC_EXEC | DC_NO_TOWN_RATING
 
				);
 
				assert(!CmdFailed(r));
 
			}
 
		} while (++aib,--j);
 
	}
 

	
 
	// check if we're done with all of them
 
	aib = &p->ai.src;
 
	j = p->ai.num_build_rec;
 
	do {
 
		if (aib->cur_building_rule == 255) return;
 
	} while (++aib,--j);
 

	
 
	// yep, all are done. switch state to the rail building state.
 
	p->ai.state = AIS_BUILD_RAIL;
 
	p->ai.state_mode = 255;
 
}
 

	
 
static TileIndex AiGetEdgeOfDefaultRailBlock(byte rule, TileIndex tile, byte cmd, int *dir)
 
{
 
	const AiDefaultBlockData *p = _default_rail_track_data[rule]->data;
 

	
 
	while (p->mode != 3 || !((--cmd) & 0x80)) p++;
 

	
 
	return tile + ToTileIndexDiff(p->tileoffs) - TileOffsByDiagDir(*dir = p->attr);
 
}
 

	
 
typedef struct AiRailPathFindData {
 
	TileIndex tile;
 
	TileIndex tile2;
 
	int count;
 
	bool flag;
 
} AiRailPathFindData;
 

	
 
static bool AiEnumFollowTrack(TileIndex tile, AiRailPathFindData *a, int track, uint length, byte *state)
 
{
 
	if (a->flag) return true;
 

	
 
	if (length > 20 || tile == a->tile) {
 
		a->flag = true;
 
		return true;
 
	}
 

	
 
	if (DistanceMax(tile, a->tile2) < 4) a->count++;
 

	
 
	return false;
 
}
 

	
 
static bool AiDoFollowTrack(const Player* p)
 
{
 
	AiRailPathFindData arpfd;
 

	
 
	arpfd.tile = p->ai.start_tile_a;
 
	arpfd.tile2 = p->ai.cur_tile_a;
 
	arpfd.flag = false;
 
	arpfd.count = 0;
 
	FollowTrack(p->ai.cur_tile_a + TileOffsByDiagDir(p->ai.cur_dir_a), 0x2000 | TRANSPORT_RAIL, p->ai.cur_dir_a^2,
 
		(TPFEnumProc*)AiEnumFollowTrack, NULL, &arpfd);
 
	return arpfd.count > 8;
 
}
 

	
 
typedef struct AiRailFinder {
 
	TileIndex final_tile;
 
	byte final_dir;
 
	byte depth;
 
	byte recursive_mode;
 
	byte cur_best_dir;
 
	byte best_dir;
 
	byte cur_best_depth;
 
	byte best_depth;
 
	uint cur_best_dist;
 
	const byte *best_ptr;
 
	uint best_dist;
 
	TileIndex cur_best_tile, best_tile;
 
	TileIndex bridge_end_tile;
 
	Player *player;
 
} AiRailFinder;
 

	
 
static const byte _ai_table_15[4][8] = {
 
	{0, 0, 4, 3, 3, 1, 128 + 0, 64},
 
	{1, 1, 2, 0, 4, 2, 128 + 1, 65},
 
	{0, 2, 2, 3, 5, 1, 128 + 2, 66},
 
	{1, 3, 5, 0, 3, 2, 128 + 3, 67}
 
};
 

	
 
static const byte _dir_table_1[] = { 3, 9, 12, 6};
 
static const byte _dir_table_2[] = {12, 6,  3, 9};
 

	
 

	
 
static bool AiIsTileBanned(const Player* p, TileIndex tile, byte val)
 
{
 
	int i;
 

	
 
	for (i = 0; i != p->ai.banned_tile_count; i++) {
 
		if (p->ai.banned_tiles[i] == tile && p->ai.banned_val[i] == val) {
 
			return true;
 
		}
 
	}
 
	return false;
 
}
 

	
 
static void AiBanTile(Player* p, TileIndex tile, byte val)
 
{
 
	uint i;
 

	
 
	for (i = lengthof(p->ai.banned_tiles) - 1; i != 0; i--) {
 
		p->ai.banned_tiles[i] = p->ai.banned_tiles[i - 1];
 
		p->ai.banned_val[i] = p->ai.banned_val[i - 1];
 
	}
 

	
 
	p->ai.banned_tiles[0] = tile;
 
	p->ai.banned_val[0] = val;
 

	
 
	if (p->ai.banned_tile_count != lengthof(p->ai.banned_tiles)) {
 
		p->ai.banned_tile_count++;
 
	}
 
}
 

	
 
static void AiBuildRailRecursive(AiRailFinder *arf, TileIndex tile, int dir);
 

	
 
static bool AiCheckRailPathBetter(AiRailFinder *arf, const byte *p)
 
{
 
	bool better = false;
 

	
 
	if (arf->recursive_mode < 1) {
 
		// Mode is 0. This means destination has not been found yet.
 
		// If the found path is shorter than the current one, remember it.
 
		if (arf->cur_best_dist < arf->best_dist) {
 
			arf->best_dir = arf->cur_best_dir;
 
			arf->best_dist = arf->cur_best_dist;
 
			arf->best_ptr = p;
 
			arf->best_tile = arf->cur_best_tile;
 
			better = true;
 
		}
 
	} else if (arf->recursive_mode > 1) {
 
		// Mode is 2.
 
		if (arf->best_dist != 0 || arf->cur_best_depth < arf->best_depth) {
 
			arf->best_depth = arf->cur_best_depth;
 
			arf->best_dist = 0;
 
			arf->best_ptr = p;
 
			arf->best_tile = 0;
 
			better = true;
 
		}
 
	}
 
	arf->recursive_mode = 0;
 
	arf->cur_best_dist = (uint)-1;
 
	arf->cur_best_depth = 0xff;
 

	
 
	return better;
 
}
 

	
 
static inline void AiCheckBuildRailBridgeHere(AiRailFinder *arf, TileIndex tile, const byte *p)
 
{
 
	Slope tileh;
 
	uint z;
 
	bool flag;
 

	
 
	int dir2 = p[0] & 3;
 

	
 
	tileh = GetTileSlope(tile, &z);
 
	if (tileh == _dir_table_1[dir2] || (tileh == SLOPE_FLAT && z != 0)) {
 
		TileIndex tile_new = tile;
 

	
 
		// Allow bridges directly over bottom tiles
 
		flag = z == 0;
 
		for (;;) {
 
			TileType type;
 

	
 
			if ((TileIndexDiff)tile_new < -TileOffsByDiagDir(dir2)) return; // Wraping around map, no bridge possible!
 
			tile_new = TILE_MASK(tile_new + TileOffsByDiagDir(dir2));
 
			type = GetTileType(tile_new);
 

	
 
			if (type == MP_CLEAR || type == MP_TREES || GetTileSlope(tile_new, NULL) != SLOPE_FLAT) {
 
				if (!flag) return;
 
				break;
 
			}
 
			if (type != MP_WATER && type != MP_RAILWAY && type != MP_STREET) return;
 
			flag = true;
 
		}
 

	
 
		// Is building a (rail)bridge possible at this place (type doesn't matter)?
 
		if (CmdFailed(DoCommand(tile_new, tile, 0 | arf->player->ai.railtype_to_use << 8, DC_AUTO, CMD_BUILD_BRIDGE))) {
 
			return;
 
		}
 
		AiBuildRailRecursive(arf, tile_new, dir2);
 

	
 
		// At the bottom depth, check if the new path is better than the old one.
 
		if (arf->depth == 1) {
 
			if (AiCheckRailPathBetter(arf, p)) arf->bridge_end_tile = tile_new;
 
		}
 
	}
 
}
 

	
 
static inline void AiCheckBuildRailTunnelHere(AiRailFinder *arf, TileIndex tile, const byte *p)
 
{
 
	uint z;
 

	
 
	if (GetTileSlope(tile, &z) == _dir_table_2[p[0] & 3] && z != 0) {
 
		int32 cost = DoCommand(tile, arf->player->ai.railtype_to_use, 0, DC_AUTO, CMD_BUILD_TUNNEL);
 

	
 
		if (!CmdFailed(cost) && cost <= (arf->player->player_money>>4)) {
 
			AiBuildRailRecursive(arf, _build_tunnel_endtile, p[0]&3);
 
			if (arf->depth == 1) AiCheckRailPathBetter(arf, p);
 
		}
 
	}
 
}
 

	
 

	
 
static void AiBuildRailRecursive(AiRailFinder *arf, TileIndex tile, int dir)
 
{
 
	const byte *p;
 

	
 
	tile = TILE_MASK(tile + TileOffsByDiagDir(dir));
 

	
 
	// Reached destination?
 
	if (tile == arf->final_tile) {
 
		if (arf->final_dir != (dir^2)) {
 
			if (arf->recursive_mode != 2) arf->recursive_mode = 1;
 
		} else if (arf->recursive_mode != 2) {
 
			arf->recursive_mode = 2;
 
			arf->cur_best_depth = arf->depth;
 
		} else {
 
			if (arf->depth < arf->cur_best_depth) arf->cur_best_depth = arf->depth;
 
		}
 
		return;
 
	}
 

	
 
	// Depth too deep?
 
	if (arf->depth >= 4) {
 
		uint dist = DistanceMaxPlusManhattan(tile, arf->final_tile);
 

	
 
		if (dist < arf->cur_best_dist) {
 
			// Store the tile that is closest to the final position.
 
			arf->cur_best_depth = arf->depth;
 
			arf->cur_best_dist = dist;
 
			arf->cur_best_tile = tile;
 
			arf->cur_best_dir = dir;
 
		}
 
		return;
 
	}
 

	
 
	// Increase recursion depth
 
	arf->depth++;
 

	
 
	// Grab pointer to list of stuff that is possible to build
 
	p = _ai_table_15[dir];
 

	
 
	// Try to build a single rail in all directions.
 
	if (GetTileZ(tile) == 0) {
 
		p += 6;
 
	} else {
 
		do {
 
			// Make sure the tile is not in the list of banned tiles and that a rail can be built here.
 
			if (!AiIsTileBanned(arf->player, tile, p[0]) &&
 
					!CmdFailed(DoCommand(tile, arf->player->ai.railtype_to_use, p[0], DC_AUTO | DC_NO_WATER | DC_NO_RAIL_OVERLAP, CMD_BUILD_SINGLE_RAIL))) {
 
				AiBuildRailRecursive(arf, tile, p[1]);
 
			}
 

	
 
			// At the bottom depth?
 
			if (arf->depth == 1) AiCheckRailPathBetter(arf, p);
 

	
 
			p += 2;
 
		} while (!(p[0]&0x80));
 
	}
 

	
 
	AiCheckBuildRailBridgeHere(arf, tile, p);
 
	AiCheckBuildRailTunnelHere(arf, tile, p+1);
 

	
 
	arf->depth--;
 
}
 

	
 

	
 
static const byte _dir_table_3[]= {0x25, 0x2A, 0x19, 0x16};
 

	
 
static void AiBuildRailConstruct(Player *p)
 
{
 
	AiRailFinder arf;
 
	int i;
 

	
 
	// Check too much lookahead?
 
	if (AiDoFollowTrack(p)) {
 
		p->ai.state_counter = (Random()&0xE)+6; // Destruct this amount of blocks
 
		p->ai.state_mode = 1; // Start destruct
 

	
 
		// Ban this tile and don't reach it for a while.
 
		AiBanTile(p, p->ai.cur_tile_a, FindFirstBit(GetRailTrackStatus(p->ai.cur_tile_a)));
 
		return;
 
	}
 

	
 
	// Setup recursive finder and call it.
 
	arf.player = p;
 
	arf.final_tile = p->ai.cur_tile_b;
 
	arf.final_dir = p->ai.cur_dir_b;
 
	arf.depth = 0;
 
	arf.recursive_mode = 0;
 
	arf.best_ptr = NULL;
 
	arf.cur_best_dist = (uint)-1;
 
	arf.cur_best_depth = 0xff;
 
	arf.best_dist = (uint)-1;
 
	arf.best_depth = 0xff;
 
	arf.cur_best_tile = 0;
 
	arf.best_tile = 0;
 
	AiBuildRailRecursive(&arf, p->ai.cur_tile_a, p->ai.cur_dir_a);
 

	
 
	// Reached destination?
 
	if (arf.recursive_mode == 2 && arf.cur_best_depth == 0) {
 
		p->ai.state_mode = 255;
 
		return;
 
	}
 

	
 
	// Didn't find anything to build?
 
	if (arf.best_ptr == NULL) {
 
		// Terraform some
 
		for (i = 0; i != 5; i++) {
 
			AiDoTerraformLand(p->ai.cur_tile_a, p->ai.cur_dir_a, 3, 0);
 
		}
 

	
 
		if (++p->ai.state_counter == 21) {
 
			p->ai.state_counter = 40;
 
			p->ai.state_mode = 1;
 

	
 
			// Ban this tile
 
			AiBanTile(p, p->ai.cur_tile_a, FindFirstBit(GetRailTrackStatus(p->ai.cur_tile_a)));
 
		}
 
		return;
 
	}
 

	
 
	p->ai.cur_tile_a += TileOffsByDiagDir(p->ai.cur_dir_a);
 

	
 
	if (arf.best_ptr[0] & 0x80) {
 
		int i;
 
		int32 bridge_len = GetBridgeLength(arf.bridge_end_tile, p->ai.cur_tile_a);
 

	
 
		/* Figure out which (rail)bridge type to build
 
		 * start with best bridge, then go down to worse and worse bridges
 
		 * unnecessary to check for worst bridge (i=0), since AI will always build
 
		 * that. AI is so fucked up that fixing this small thing will probably not
 
		 * solve a thing
 
		 */
 
		for (i = MAX_BRIDGES - 1; i != 0; i--) {
 
			if (CheckBridge_Stuff(i, bridge_len)) {
 
				int32 cost = DoCommand(arf.bridge_end_tile, p->ai.cur_tile_a, i | (p->ai.railtype_to_use << 8), DC_AUTO, CMD_BUILD_BRIDGE);
 
				if (!CmdFailed(cost) && cost < (p->player_money >> 5)) break;
 
			}
 
		}
 

	
 
		// Build it
 
		DoCommand(arf.bridge_end_tile, p->ai.cur_tile_a, i | (p->ai.railtype_to_use << 8), DC_AUTO | DC_EXEC, CMD_BUILD_BRIDGE);
 

	
 
		p->ai.cur_tile_a = arf.bridge_end_tile;
 
		p->ai.state_counter = 0;
 
	} else if (arf.best_ptr[0] & 0x40) {
 
		// tunnel
 
		DoCommand(p->ai.cur_tile_a, p->ai.railtype_to_use, 0, DC_AUTO | DC_EXEC, CMD_BUILD_TUNNEL);
 
		p->ai.cur_tile_a = _build_tunnel_endtile;
 
		p->ai.state_counter = 0;
 
	} else {
 
		// rail
 
		p->ai.cur_dir_a = arf.best_ptr[1];
 
		DoCommand(p->ai.cur_tile_a, p->ai.railtype_to_use, arf.best_ptr[0],
 
			DC_EXEC | DC_AUTO | DC_NO_WATER | DC_NO_RAIL_OVERLAP, CMD_BUILD_SINGLE_RAIL);
 
		p->ai.state_counter = 0;
 
	}
 

	
 
	if (arf.best_tile != 0) {
 
		for (i = 0; i != 2; i++) {
 
			AiDoTerraformLand(arf.best_tile, arf.best_dir, 3, 0);
 
		}
 
	}
 
}
 

	
 
static bool AiRemoveTileAndGoForward(Player *p)
 
{
 
	byte b;
 
	int bit;
 
	const byte *ptr;
 
	TileIndex tile = p->ai.cur_tile_a;
 
	TileIndex tilenew;
 

	
 
	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
		if (IsTunnel(tile)) {
 
			// Clear the tunnel and continue at the other side of it.
 
			if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR)))
 
				return false;
 
			p->ai.cur_tile_a = TILE_MASK(_build_tunnel_endtile - TileOffsByDiagDir(p->ai.cur_dir_a));
 
			return true;
 
		} else {
 
			// Check if the bridge points in the right direction.
 
			// This is not really needed the first place AiRemoveTileAndGoForward is called.
 
			if (DiagDirToAxis(GetBridgeRampDirection(tile)) != (p->ai.cur_dir_a & 1U)) return false;
 

	
 
			tile = GetOtherBridgeEnd(tile);
 

	
 
			tilenew = TILE_MASK(tile - TileOffsByDiagDir(p->ai.cur_dir_a));
 
			// And clear the bridge.
 
			if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR)))
 
				return false;
 
			p->ai.cur_tile_a = tilenew;
 
			return true;
 
		}
 
	}
 

	
 
	// Find the railtype at the position. Quit if no rail there.
 
	b = GetRailTrackStatus(tile) & _dir_table_3[p->ai.cur_dir_a];
 
	if (b == 0) return false;
 

	
 
	// Convert into a bit position that CMD_REMOVE_SINGLE_RAIL expects.
 
	bit = FindFirstBit(b);
 

	
 
	// Then remove and signals if there are any.
 
	if (IsTileType(tile, MP_RAILWAY) &&
 
			GetRailTileType(tile) == RAIL_TILE_SIGNALS) {
 
		DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_SIGNALS);
 
	}
 

	
 
	// And also remove the rail.
 
	if (CmdFailed(DoCommand(tile, 0, bit, DC_EXEC, CMD_REMOVE_SINGLE_RAIL)))
 
		return false;
 

	
 
	// Find the direction at the other edge of the rail.
 
	ptr = _ai_table_15[p->ai.cur_dir_a ^ 2];
 
	while (ptr[0] != bit) ptr += 2;
 
	p->ai.cur_dir_a = ptr[1] ^ 2;
 

	
 
	// And then also switch tile.
 
	p->ai.cur_tile_a = TILE_MASK(p->ai.cur_tile_a - TileOffsByDiagDir(p->ai.cur_dir_a));
 

	
 
	return true;
 
}
 

	
 

	
 
static void AiBuildRailDestruct(Player *p)
 
{
 
	// Decrease timeout.
 
	if (!--p->ai.state_counter) {
 
		p->ai.state_mode = 2;
 
		p->ai.state_counter = 0;
 
	}
 

	
 
	// Don't do anything if the destination is already reached.
 
	if (p->ai.cur_tile_a == p->ai.start_tile_a) return;
 

	
 
	AiRemoveTileAndGoForward(p);
 
}
 

	
 

	
 
static void AiBuildRail(Player *p)
 
{
 
	switch (p->ai.state_mode) {
 
		case 0: // Construct mode, build new rail.
 
			AiBuildRailConstruct(p);
 
			break;
 

	
 
		case 1: // Destruct mode, destroy the rail currently built.
 
			AiBuildRailDestruct(p);
 
			break;
 

	
 
		case 2: {
 
			uint i;
 

	
 
			// Terraform some and then try building again.
 
			for (i = 0; i != 4; i++) {
 
				AiDoTerraformLand(p->ai.cur_tile_a, p->ai.cur_dir_a, 3, 0);
 
			}
 

	
 
			if (++p->ai.state_counter == 4) {
 
				p->ai.state_counter = 0;
 
				p->ai.state_mode = 0;
 
			}
 
		}
 

	
 
		default: break;
 
	}
 
}
 

	
 
static void AiStateBuildRail(Player *p)
 
{
 
	int num;
 
	AiBuildRec *aib;
 
	byte cmd;
 
	TileIndex tile;
 
	int dir;
 

	
 
	// time out?
 
	if (++p->ai.timeout_counter == 1388) {
 
		p->ai.state = AIS_DELETE_RAIL_BLOCKS;
 
		return;
 
	}
 

	
 
	// Currently building a rail between two points?
 
	if (p->ai.state_mode != 255) {
 
		AiBuildRail(p);
 

	
 
		// Alternate between edges
 
		swap_tile(&p->ai.start_tile_a, &p->ai.start_tile_b);
 
		swap_tile(&p->ai.cur_tile_a, &p->ai.cur_tile_b);
 
		swap_byte(&p->ai.start_dir_a, &p->ai.start_dir_b);
 
		swap_byte(&p->ai.cur_dir_a, &p->ai.cur_dir_b);
 
		return;
 
	}
 

	
 
	// Now, find two new points to build between
 
	num = p->ai.num_build_rec;
 
	aib = &p->ai.src;
 

	
 
	for (;;) {
 
		cmd = aib->buildcmd_a;
 
		aib->buildcmd_a = 255;
 
		if (cmd != 255) break;
 

	
 
		cmd = aib->buildcmd_b;
 
		aib->buildcmd_b = 255;
 
		if (cmd != 255) break;
 

	
 
		aib++;
 
		if (--num == 0) {
 
			p->ai.state = AIS_BUILD_RAIL_VEH;
 
			p->ai.state_counter = 0; // timeout
 
			return;
 
		}
 
	}
 

	
 
	// Find first edge to build from.
 
	tile = AiGetEdgeOfDefaultRailBlock(aib->cur_building_rule, aib->use_tile, cmd&3, &dir);
 
	p->ai.start_tile_a = tile;
 
	p->ai.cur_tile_a = tile;
 
	p->ai.start_dir_a = dir;
 
	p->ai.cur_dir_a = dir;
 
	DoCommand(TILE_MASK(tile + TileOffsByDiagDir(dir)), 0, (dir&1)?1:0, DC_EXEC, CMD_REMOVE_SINGLE_RAIL);
 

	
 
	assert(TILE_MASK(tile) != 0xFF00);
 

	
 
	// Find second edge to build to
 
	aib = (&p->ai.src) + ((cmd >> 4)&0xF);
 
	tile = AiGetEdgeOfDefaultRailBlock(aib->cur_building_rule, aib->use_tile, (cmd>>2)&3, &dir);
 
	p->ai.start_tile_b = tile;
 
	p->ai.cur_tile_b = tile;
 
	p->ai.start_dir_b = dir;
 
	p->ai.cur_dir_b = dir;
 
	DoCommand(TILE_MASK(tile + TileOffsByDiagDir(dir)), 0, (dir&1)?1:0, DC_EXEC, CMD_REMOVE_SINGLE_RAIL);
 

	
 
	assert(TILE_MASK(tile) != 0xFF00);
 

	
 
	// And setup state.
 
	p->ai.state_mode = 2;
 
	p->ai.state_counter = 0;
 
	p->ai.banned_tile_count = 0;
 
}
 

	
 
static StationID AiGetStationIdByDef(TileIndex tile, int id)
 
{
 
	const AiDefaultBlockData *p = _default_rail_track_data[id]->data;
 
	while (p->mode != 1) p++;
 
	return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)));
 
}
 

	
 
static EngineID AiFindBestWagon(CargoID cargo, RailType railtype)
 
{
 
	EngineID best_veh_index = INVALID_ENGINE;
 
	EngineID i;
 
	uint16 best_capacity = 0;
 
	uint16 best_speed    = 0;
 
	uint speed;
 

	
 
	for (i = 0; i < NUM_TRAIN_ENGINES; i++) {
 
		const RailVehicleInfo *rvi = RailVehInfo(i);
 
		const Engine* e = GetEngine(i);
 

	
 
		if (!IsCompatibleRail(e->railtype, railtype) ||
 
				!(rvi->flags & RVI_WAGON) ||
 
				!HASBIT(e->player_avail, _current_player)) {
 
			continue;
 
		}
 

	
 
		if (rvi->cargo_type != cargo) continue;
 

	
 
		/* max_speed of 0 indicates no speed limit */
 
		speed = rvi->max_speed == 0 ? 0xFFFF : rvi->max_speed;
 

	
 
		if (rvi->capacity >= best_capacity && speed >= best_speed) {
 
			best_capacity = rvi->capacity;
 
			best_speed    = best_speed;
 
			best_veh_index = i;
 
		}
 
	}
 

	
 
	return best_veh_index;
 
}
 

	
 
static void AiStateBuildRailVeh(Player *p)
 
{
 
	const AiDefaultBlockData *ptr;
 
	TileIndex tile;
 
	EngineID veh;
 
	int i;
 
	CargoID cargo;
 
	int32 cost;
 
	Vehicle *v;
 
	VehicleID loco_id;
 

	
 
	ptr = _default_rail_track_data[p->ai.src.cur_building_rule]->data;
 
	while (ptr->mode != 0) ptr++;
 

	
 
	tile = TILE_ADD(p->ai.src.use_tile, ToTileIndexDiff(ptr->tileoffs));
 

	
 

	
 
	cargo = p->ai.cargo_type;
 
	for (i = 0;;) {
 
		if (p->ai.wagon_list[i] == INVALID_VEHICLE) {
 
			veh = AiFindBestWagon(cargo, p->ai.railtype_to_use);
 
			/* veh will return INVALID_ENGINE if no suitable wagon is available.
 
			 * We shall treat this in the same way as having no money */
 
			if (veh == INVALID_ENGINE) goto handle_nocash;
 
			cost = DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE);
 
			if (CmdFailed(cost)) goto handle_nocash;
 
			p->ai.wagon_list[i] = _new_vehicle_id;
 
			p->ai.wagon_list[i + 1] = INVALID_VEHICLE;
 
			return;
 
		}
 
		if (cargo == CT_MAIL) cargo = CT_PASSENGERS;
 
		if (++i == p->ai.num_wagons * 2 - 1) break;
 
	}
 

	
 
	// Which locomotive to build?
 
	veh = AiChooseTrainToBuild(p->ai.railtype_to_use, p->player_money, cargo != CT_PASSENGERS ? 1 : 0, tile);
 
	if (veh == INVALID_ENGINE) {
 
handle_nocash:
 
		// after a while, if AI still doesn't have cash, get out of this block by selling the wagons.
 
		if (++p->ai.state_counter == 1000) {
 
			for (i = 0; p->ai.wagon_list[i] != INVALID_VEHICLE; i++) {
 
				cost = DoCommand(tile, p->ai.wagon_list[i], 0, DC_EXEC, CMD_SELL_RAIL_WAGON);
 
				assert(!CmdFailed(cost));
 
			}
 
			p->ai.state = AIS_0;
 
		}
 
		return;
 
	}
 

	
 
	// Try to build the locomotive
 
	cost = DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE);
 
	assert(!CmdFailed(cost));
 
	loco_id = _new_vehicle_id;
 

	
 
	// Sell a vehicle if the train is double headed.
 
	v = GetVehicle(loco_id);
 
	if (v->next != NULL) {
 
		i = p->ai.wagon_list[p->ai.num_wagons * 2 - 2];
 
		p->ai.wagon_list[p->ai.num_wagons * 2 - 2] = INVALID_VEHICLE;
 
		DoCommand(tile, i, 0, DC_EXEC, CMD_SELL_RAIL_WAGON);
 
	}
 

	
 
	// Move the wagons onto the train
 
	for (i = 0; p->ai.wagon_list[i] != INVALID_VEHICLE; i++) {
 
		DoCommand(tile, p->ai.wagon_list[i] | (loco_id << 16), 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 
	}
 

	
 
	for (i = 0; p->ai.order_list_blocks[i] != 0xFF; i++) {
 
		const AiBuildRec* aib = &p->ai.src + p->ai.order_list_blocks[i];
 
		bool is_pass = (
 
			p->ai.cargo_type == CT_PASSENGERS ||
 
			p->ai.cargo_type == CT_MAIL ||
 
			(_opt.landscape == LT_NORMAL && p->ai.cargo_type == CT_VALUABLES)
 
		);
 
		Order order;
 

	
 
		order.type = OT_GOTO_STATION;
 
		order.flags = 0;
 
		order.dest = AiGetStationIdByDef(aib->use_tile, aib->cur_building_rule);
 

	
 
		if (!is_pass && i == 1) order.flags |= OF_UNLOAD;
 
		if (p->ai.num_want_fullload != 0 && (is_pass || i == 0))
 
			order.flags |= OF_FULL_LOAD;
 

	
 
		DoCommand(0, loco_id + (i << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER);
 
	}
 

	
 
	DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_TRAIN);
 

	
 
	DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
 

	
 
	if (p->ai.num_want_fullload != 0) p->ai.num_want_fullload--;
 

	
 
	if (--p->ai.num_loco_to_build != 0) {
 
//		p->ai.loco_id = INVALID_VEHICLE;
 
		p->ai.wagon_list[0] = INVALID_VEHICLE;
 
	} else {
 
		p->ai.state = AIS_0;
 
	}
 
}
 

	
 
static void AiStateDeleteRailBlocks(Player *p)
 
{
 
	const AiBuildRec* aib = &p->ai.src;
 
	uint num = p->ai.num_build_rec;
 

	
 
	do {
 
		const AiDefaultBlockData* b;
 

	
 
		if (aib->cur_building_rule == 255) continue;
 
		for (b = _default_rail_track_data[aib->cur_building_rule]->data; b->mode != 4; b++) {
 
			DoCommand(TILE_ADD(aib->use_tile, ToTileIndexDiff(b->tileoffs)), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
		}
 
	} while (++aib,--num);
 

	
 
	p->ai.state = AIS_0;
 
}
 

	
 
static bool AiCheckRoadResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo)
 
{
 
	uint values[NUM_CARGO];
 
	int rad;
 

	
 
	if (_patches.modified_catchment) {
 
		rad = CA_TRUCK; // Same as CA_BUS at the moment?
 
	} else { // change that at some point?
 
		rad = 4;
 
	}
 

	
 
	for (;; p++) {
 
		if (p->mode == 4) {
 
			return true;
 
		} else if (p->mode == 1) {
 
			TileIndex tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs));
 

	
 
			if (cargo & 0x80) {
 
				GetProductionAroundTiles(values, tile2, 1, 1, rad);
 
				return values[cargo & 0x7F] != 0;
 
			} else {
 
				GetAcceptanceAroundTiles(values, tile2, 1, 1, rad);
 
				return (values[cargo]&~7) != 0;
 
			}
 
		}
 
	}
 
}
 

	
 
static bool _want_road_truck_station;
 
static int32 AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBlockData *p, byte flag);
 

	
 
// Returns rule and cost
 
static int AiFindBestDefaultRoadBlock(TileIndex tile, byte direction, byte cargo, int32 *cost)
 
{
 
	int i;
 
	const AiDefaultRoadBlock *p;
 

	
 
	_want_road_truck_station = (cargo & 0x7F) != CT_PASSENGERS;
 

	
 
	for (i = 0; (p = _road_default_block_data[i]) != NULL; i++) {
 
		if (p->dir == direction) {
 
			*cost = AiDoBuildDefaultRoadBlock(tile, p->data, 0);
 
			if (!CmdFailed(*cost) && AiCheckRoadResources(tile, p->data, cargo))
 
				return i;
 
		}
 
	}
 

	
 
	return -1;
 
}
 

	
 
static int32 AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBlockData *p, byte flag)
 
{
 
	int32 ret;
 
	int32 total_cost = 0;
 
	Town *t = NULL;
 
	int rating = 0;
 
	int roadflag = 0;
 

	
 
	for (;p->mode != 4;p++) {
 
		TileIndex c = TILE_MASK(tile + ToTileIndexDiff(p->tileoffs));
 

	
 
		_cleared_town = NULL;
 

	
 
		if (p->mode == 2) {
 
			if (IsTileType(c, MP_STREET) &&
 
					GetRoadTileType(c) == ROAD_TILE_NORMAL &&
 
					(GetRoadBits(c) & p->attr) != 0) {
 
				roadflag |= 2;
 

	
 
				// all bits are already built?
 
				if ((GetRoadBits(c) & p->attr) == p->attr) continue;
 
			}
 

	
 
			ret = DoCommand(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
 
			if (CmdFailed(ret)) return CMD_ERROR;
 
			total_cost += ret;
 

	
 
			continue;
 
		}
 

	
 
		if (p->mode == 0) {
 
			// Depot
 
			ret = DoCommand(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_DEPOT);
 
			goto clear_town_stuff;
 
		} else if (p->mode == 1) {
 
			if (_want_road_truck_station) {
 
				// Truck station
 
				ret = DoCommand(c, p->attr, RS_TRUCK, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP);
 
			} else {
 
				// Bus station
 
				ret = DoCommand(c, p->attr, RS_BUS, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP);
 
			}
 
clear_town_stuff:;
 

	
 
			if (CmdFailed(ret)) return CMD_ERROR;
 
			total_cost += ret;
 

	
 
			if (_cleared_town != NULL) {
 
				if (t != NULL && t != _cleared_town) return CMD_ERROR;
 
				t = _cleared_town;
 
				rating += _cleared_town_rating;
 
			}
 
		} else if (p->mode == 3) {
 
			if (flag & DC_EXEC) continue;
 

	
 
			if (GetTileSlope(c, NULL) != SLOPE_FLAT) return CMD_ERROR;
 

	
 
			if (!IsTileType(c, MP_STREET) || GetRoadTileType(c) != ROAD_TILE_NORMAL) {
 
				ret = DoCommand(c, 0, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_LANDSCAPE_CLEAR);
 
				if (CmdFailed(ret)) return CMD_ERROR;
 
			}
 

	
 
		}
 
	}
 

	
 
	if (!_want_road_truck_station && !(roadflag & 2)) return CMD_ERROR;
 

	
 
	if (!(flag & DC_EXEC)) {
 
		if (t != NULL && rating > t->ratings[_current_player]) return CMD_ERROR;
 
	}
 
	return total_cost;
 
}
 

	
 
// Make sure the blocks are not too close to each other
 
static bool AiCheckBlockDistances(Player *p, TileIndex tile)
 
{
 
	const AiBuildRec* aib = &p->ai.src;
 
	uint num = p->ai.num_build_rec;
 

	
 
	do {
 
		if (aib->cur_building_rule != 255) {
 
			if (DistanceManhattan(aib->use_tile, tile) < 9) return false;
 
		}
 
	} while (++aib, --num);
 

	
 
	return true;
 
}
 

	
 

	
 
static void AiStateBuildDefaultRoadBlocks(Player *p)
 
{
 
	uint i;
 
	int j;
 
	AiBuildRec *aib;
 
	int rule;
 
	int32 cost;
 

	
 
	// time out?
 
	if (++p->ai.timeout_counter == 1388) {
 
		p->ai.state = AIS_DELETE_RAIL_BLOCKS;
 
		return;
 
	}
 

	
 
	// do the following 8 times
 
	for (i = 0; i != 8; i++) {
 
		// check if we can build the default track
 
		aib = &p->ai.src;
 
		j = p->ai.num_build_rec;
 
		do {
 
			// this item has already been built?
 
			if (aib->cur_building_rule != 255) continue;
 

	
 
			// adjust the coordinate randomly,
 
			// to make sure that we find a position.
 
			aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng);
 

	
 
			// check if the road can be built there.
 
			rule = AiFindBestDefaultRoadBlock(
 
				aib->use_tile, aib->direction, aib->cargo, &cost
 
			);
 

	
 
			if (rule == -1) {
 
				// cannot build, terraform after a while
 
				if (p->ai.state_counter >= 600) {
 
					AiDoTerraformLand(aib->use_tile, Random()&3, 3, (int8)p->ai.state_mode);
 
				}
 
				// also try the other terraform direction
 
				if (++p->ai.state_counter >= 1000) {
 
					p->ai.state_counter = 0;
 
					p->ai.state_mode = -p->ai.state_mode;
 
				}
 
			} else if (CheckPlayerHasMoney(cost) && AiCheckBlockDistances(p,aib->use_tile)) {
 
				int32 r;
 

	
 
				// player has money, build it.
 
				aib->cur_building_rule = rule;
 

	
 
				r = AiDoBuildDefaultRoadBlock(
 
					aib->use_tile,
 
					_road_default_block_data[rule]->data,
 
					DC_EXEC | DC_NO_TOWN_RATING
 
				);
 
				assert(!CmdFailed(r));
 
			}
 
		} while (++aib,--j);
 
	}
 

	
 
	// check if we're done with all of them
 
	aib = &p->ai.src;
 
	j = p->ai.num_build_rec;
 
	do {
 
		if (aib->cur_building_rule == 255) return;
 
	} while (++aib,--j);
 

	
 
	// yep, all are done. switch state to the rail building state.
 
	p->ai.state = AIS_BUILD_ROAD;
 
	p->ai.state_mode = 255;
 
}
 

	
 
typedef struct {
 
	TileIndex final_tile;
 
	byte final_dir;
 
	byte depth;
 
	byte recursive_mode;
 
	byte cur_best_dir;
 
	byte best_dir;
 
	byte cur_best_depth;
 
	byte best_depth;
 
	uint cur_best_dist;
 
	const byte *best_ptr;
 
	uint best_dist;
 
	TileIndex cur_best_tile, best_tile;
 
	TileIndex bridge_end_tile;
 
	Player *player;
 
} AiRoadFinder;
 

	
 
typedef struct AiRoadEnum {
 
	TileIndex dest;
 
	TileIndex best_tile;
 
	int best_track;
 
	uint best_dist;
 
} AiRoadEnum;
 

	
 
static const byte _dir_by_track[] = {
 
	0, 1, 0, 1, 2, 1,
 
	0, 0,
 
	2, 3, 3, 2, 3, 0,
 
};
 

	
 
static void AiBuildRoadRecursive(AiRoadFinder *arf, TileIndex tile, int dir);
 

	
 
static bool AiCheckRoadPathBetter(AiRoadFinder *arf, const byte *p)
 
{
 
	bool better = false;
 

	
 
	if (arf->recursive_mode < 1) {
 
		// Mode is 0. This means destination has not been found yet.
 
		// If the found path is shorter than the current one, remember it.
 
		if (arf->cur_best_dist < arf->best_dist ||
 
			(arf->cur_best_dist == arf->best_dist && arf->cur_best_depth < arf->best_depth)) {
 
			arf->best_depth = arf->cur_best_depth;
 
			arf->best_dist = arf->cur_best_dist;
 
			arf->best_dir = arf->cur_best_dir;
 
			arf->best_ptr = p;
 
			arf->best_tile = arf->cur_best_tile;
 
			better = true;
 
		}
 
	} else if (arf->recursive_mode > 1) {
 
		// Mode is 2.
 
		if (arf->best_dist != 0 || arf->cur_best_depth < arf->best_depth) {
 
			arf->best_depth = arf->cur_best_depth;
 
			arf->best_dist = 0;
 
			arf->best_ptr = p;
 
			arf->best_tile = 0;
 
			better = true;
 
		}
 
	}
 
	arf->recursive_mode = 0;
 
	arf->cur_best_dist = (uint)-1;
 
	arf->cur_best_depth = 0xff;
 

	
 
	return better;
 
}
 

	
 

	
 
static bool AiEnumFollowRoad(TileIndex tile, AiRoadEnum *a, int track, uint length, byte *state)
 
{
 
	uint dist = DistanceManhattan(tile, a->dest);
 

	
 
	if (dist <= a->best_dist) {
 
		TileIndex tile2 = TILE_MASK(tile + TileOffsByDiagDir(_dir_by_track[track]));
 

	
 
		if (IsTileType(tile2, MP_STREET) && GetRoadTileType(tile2) == ROAD_TILE_NORMAL) {
 
			a->best_dist = dist;
 
			a->best_tile = tile;
 
			a->best_track = track;
 
		}
 
	}
 

	
 
	return false;
 
}
 

	
 
static const uint16 _ai_road_table_and[4] = {
 
	0x1009,
 
	0x16,
 
	0x520,
 
	0x2A00,
 
};
 

	
 
static bool AiCheckRoadFinished(Player *p)
 
{
 
	AiRoadEnum are;
 
	TileIndex tile;
 
	int dir = p->ai.cur_dir_a;
 
	uint32 bits;
 
	int i;
 

	
 
	are.dest = p->ai.cur_tile_b;
 
	tile = TILE_MASK(p->ai.cur_tile_a + TileOffsByDiagDir(dir));
 

	
 
	if (IsRoadStopTile(tile) || IsTileDepotType(tile, TRANSPORT_ROAD)) return false;
 
	bits = GetTileTrackStatus(tile, TRANSPORT_ROAD) & _ai_road_table_and[dir];
 
	if (bits == 0) return false;
 

	
 
	are.best_dist = (uint)-1;
 

	
 
	for_each_bit(i, bits) {
 
		FollowTrack(tile, 0x3000 | TRANSPORT_ROAD, _dir_by_track[i], (TPFEnumProc*)AiEnumFollowRoad, NULL, &are);
 
	}
 

	
 
	if (DistanceManhattan(tile, are.dest) <= are.best_dist) return false;
 

	
 
	if (are.best_dist == 0) return true;
 

	
 
	p->ai.cur_tile_a = are.best_tile;
 
	p->ai.cur_dir_a = _dir_by_track[are.best_track];
 
	return false;
 
}
 

	
 

	
 
static bool AiBuildRoadHelper(TileIndex tile, int flags, int type)
 
{
 
	static const RoadBits _road_bits[] = {
 
		ROAD_X,
 
		ROAD_Y,
 
		ROAD_NW | ROAD_NE,
 
		ROAD_SW | ROAD_SE,
 
		ROAD_NW | ROAD_SW,
 
		ROAD_SE | ROAD_NE
 
	};
 
	return !CmdFailed(DoCommand(tile, _road_bits[type], 0, flags, CMD_BUILD_ROAD));
 
}
 

	
 
static inline void AiCheckBuildRoadBridgeHere(AiRoadFinder *arf, TileIndex tile, const byte *p)
 
{
 
	Slope tileh;
 
	uint z;
 
	bool flag;
 

	
 
	int dir2 = p[0] & 3;
 

	
 
	tileh = GetTileSlope(tile, &z);
 
	if (tileh == _dir_table_1[dir2] || (tileh == SLOPE_FLAT && z != 0)) {
 
		TileIndex tile_new = tile;
 

	
 
		// Allow bridges directly over bottom tiles
 
		flag = z == 0;
 
		for (;;) {
 
			TileType type;
 

	
 
			if ((TileIndexDiff)tile_new < -TileOffsByDiagDir(dir2)) return; // Wraping around map, no bridge possible!
 
			tile_new = TILE_MASK(tile_new + TileOffsByDiagDir(dir2));
 
			type = GetTileType(tile_new);
 

	
 
			if (type == MP_CLEAR || type == MP_TREES || GetTileSlope(tile, NULL) != SLOPE_FLAT) {
 
				// Allow a bridge if either we have a tile that's water, rail or street,
 
				// or if we found an up tile.
 
				if (!flag) return;
 
				break;
 
			}
 
			if (type != MP_WATER && type != MP_RAILWAY && type != MP_STREET) return;
 
			flag = true;
 
		}
 

	
 
		// Is building a (rail)bridge possible at this place (type doesn't matter)?
 
		if (CmdFailed(DoCommand(tile_new, tile, 0x8000, DC_AUTO, CMD_BUILD_BRIDGE)))
 
			return;
 
		AiBuildRoadRecursive(arf, tile_new, dir2);
 

	
 
		// At the bottom depth, check if the new path is better than the old one.
 
		if (arf->depth == 1) {
 
			if (AiCheckRoadPathBetter(arf, p)) arf->bridge_end_tile = tile_new;
 
		}
 
	}
 
}
 

	
 
static inline void AiCheckBuildRoadTunnelHere(AiRoadFinder *arf, TileIndex tile, const byte *p)
 
{
 
	uint z;
 

	
 
	if (GetTileSlope(tile, &z) == _dir_table_2[p[0] & 3] && z != 0) {
 
		int32 cost = DoCommand(tile, 0x200, 0, DC_AUTO, CMD_BUILD_TUNNEL);
 

	
 
		if (!CmdFailed(cost) && cost <= (arf->player->player_money>>4)) {
 
			AiBuildRoadRecursive(arf, _build_tunnel_endtile, p[0]&3);
 
			if (arf->depth == 1)  AiCheckRoadPathBetter(arf, p);
 
		}
 
	}
 
}
 

	
 

	
 

	
 
static void AiBuildRoadRecursive(AiRoadFinder *arf, TileIndex tile, int dir)
 
{
 
	const byte *p;
 

	
 
	tile = TILE_MASK(tile + TileOffsByDiagDir(dir));
 

	
 
	// Reached destination?
 
	if (tile == arf->final_tile) {
 
		if ((arf->final_dir^2) == dir) {
 
			arf->recursive_mode = 2;
 
			arf->cur_best_depth = arf->depth;
 
		}
 
		return;
 
	}
 

	
 
	// Depth too deep?
 
	if (arf->depth >= 4) {
 
		uint dist = DistanceMaxPlusManhattan(tile, arf->final_tile);
 
		if (dist < arf->cur_best_dist) {
 
			// Store the tile that is closest to the final position.
 
			arf->cur_best_dist = dist;
 
			arf->cur_best_tile = tile;
 
			arf->cur_best_dir = dir;
 
			arf->cur_best_depth = arf->depth;
 
		}
 
		return;
 
	}
 

	
 
	// Increase recursion depth
 
	arf->depth++;
 

	
 
	// Grab pointer to list of stuff that is possible to build
 
	p = _ai_table_15[dir];
 

	
 
	// Try to build a single rail in all directions.
 
	if (GetTileZ(tile) == 0) {
 
		p += 6;
 
	} else {
 
		do {
 
			// Make sure that a road can be built here.
 
			if (AiBuildRoadHelper(tile, DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, p[0])) {
 
				AiBuildRoadRecursive(arf, tile, p[1]);
 
			}
 

	
 
			// At the bottom depth?
 
			if (arf->depth == 1) AiCheckRoadPathBetter(arf, p);
 

	
 
			p += 2;
 
		} while (!(p[0] & 0x80));
 
	}
 

	
 
	AiCheckBuildRoadBridgeHere(arf, tile, p);
 
	AiCheckBuildRoadTunnelHere(arf, tile, p+1);
 

	
 
	arf->depth--;
 
}
 

	
 

	
 
static void AiBuildRoadConstruct(Player *p)
 
{
 
	AiRoadFinder arf;
 
	int i;
 
	TileIndex tile;
 

	
 
	// Reached destination?
 
	if (AiCheckRoadFinished(p)) {
 
		p->ai.state_mode = 255;
 
		return;
 
	}
 

	
 
	// Setup recursive finder and call it.
 
	arf.player = p;
 
	arf.final_tile = p->ai.cur_tile_b;
 
	arf.final_dir = p->ai.cur_dir_b;
 
	arf.depth = 0;
 
	arf.recursive_mode = 0;
 
	arf.best_ptr = NULL;
 
	arf.cur_best_dist = (uint)-1;
 
	arf.cur_best_depth = 0xff;
 
	arf.best_dist = (uint)-1;
 
	arf.best_depth =  0xff;
 
	arf.cur_best_tile = 0;
 
	arf.best_tile = 0;
 
	AiBuildRoadRecursive(&arf, p->ai.cur_tile_a, p->ai.cur_dir_a);
 

	
 
	// Reached destination?
 
	if (arf.recursive_mode == 2 && arf.cur_best_depth == 0) {
 
		p->ai.state_mode = 255;
 
		return;
 
	}
 

	
 
	// Didn't find anything to build?
 
	if (arf.best_ptr == NULL) {
 
		// Terraform some
 
do_some_terraform:
 
		for (i = 0; i != 5; i++)
 
			AiDoTerraformLand(p->ai.cur_tile_a, p->ai.cur_dir_a, 3, 0);
 

	
 
		if (++p->ai.state_counter == 21) {
 
			p->ai.state_mode = 1;
 

	
 
			p->ai.cur_tile_a = TILE_MASK(p->ai.cur_tile_a + TileOffsByDiagDir(p->ai.cur_dir_a));
 
			p->ai.cur_dir_a ^= 2;
 
			p->ai.state_counter = 0;
 
		}
 
		return;
 
	}
 

	
 
	tile = TILE_MASK(p->ai.cur_tile_a + TileOffsByDiagDir(p->ai.cur_dir_a));
 

	
 
	if (arf.best_ptr[0]&0x80) {
 
		int i;
 
		int32 bridge_len;
 
		p->ai.cur_tile_a = arf.bridge_end_tile;
 
		bridge_len = GetBridgeLength(tile, p->ai.cur_tile_a); // tile
 

	
 
		/* Figure out what (road)bridge type to build
 
		 * start with best bridge, then go down to worse and worse bridges
 
		 * unnecessary to check for worse bridge (i=0), since AI will always build that.
 
		 *AI is so fucked up that fixing this small thing will probably not solve a thing
 
		 */
 
		for (i = 10; i != 0; i--) {
 
			if (CheckBridge_Stuff(i, bridge_len)) {
 
				int32 cost = DoCommand(tile, p->ai.cur_tile_a, i + (0x80 << 8), DC_AUTO, CMD_BUILD_BRIDGE);
 
				if (!CmdFailed(cost) && cost < (p->player_money >> 5)) break;
 
			}
 
		}
 

	
 
		// Build it
 
		DoCommand(tile, p->ai.cur_tile_a, i + (0x80 << 8), DC_AUTO | DC_EXEC, CMD_BUILD_BRIDGE);
 

	
 
		p->ai.state_counter = 0;
 
	} else if (arf.best_ptr[0]&0x40) {
 
		// tunnel
 
		DoCommand(tile, 0x200, 0, DC_AUTO | DC_EXEC, CMD_BUILD_TUNNEL);
 
		p->ai.cur_tile_a = _build_tunnel_endtile;
 
		p->ai.state_counter = 0;
 
	} else {
 
		// road
 
		if (!AiBuildRoadHelper(tile, DC_EXEC | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, arf.best_ptr[0]))
 
			goto do_some_terraform;
 

	
 
		p->ai.cur_dir_a = arf.best_ptr[1];
 
		p->ai.cur_tile_a = tile;
 
		p->ai.state_counter = 0;
 
	}
 

	
 
	if (arf.best_tile != 0) {
 
		for (i = 0; i != 2; i++)
 
			AiDoTerraformLand(arf.best_tile, arf.best_dir, 3, 0);
 
	}
 
}
 

	
 

	
 
static void AiBuildRoad(Player *p)
 
{
 
	if (p->ai.state_mode < 1) {
 
		// Construct mode, build new road.
 
		AiBuildRoadConstruct(p);
 
	} else if (p->ai.state_mode == 1) {
 
		// Destruct mode, not implemented for roads.
 
		p->ai.state_mode = 2;
 
		p->ai.state_counter = 0;
 
	} else if (p->ai.state_mode == 2) {
 
		uint i;
 

	
 
		// Terraform some and then try building again.
 
		for (i = 0; i != 4; i++) {
 
			AiDoTerraformLand(p->ai.cur_tile_a, p->ai.cur_dir_a, 3, 0);
 
		}
 

	
 
		if (++p->ai.state_counter == 4) {
 
			p->ai.state_counter = 0;
 
			p->ai.state_mode = 0;
 
		}
 
	}
 
}
 

	
 
static TileIndex AiGetRoadBlockEdge(byte rule, TileIndex tile, int *dir)
 
{
 
	const AiDefaultBlockData *p = _road_default_block_data[rule]->data;
 
	while (p->mode != 1) p++;
 
	*dir = p->attr;
 
	return TILE_ADD(tile, ToTileIndexDiff(p->tileoffs));
 
}
 

	
 

	
 
static void AiStateBuildRoad(Player *p)
 
{
 
	int num;
 
	AiBuildRec *aib;
 
	byte cmd;
 
	TileIndex tile;
 
	int dir;
 

	
 
	// time out?
 
	if (++p->ai.timeout_counter == 1388) {
 
		p->ai.state = AIS_DELETE_ROAD_BLOCKS;
 
		return;
 
	}
 

	
 
	// Currently building a road between two points?
 
	if (p->ai.state_mode != 255) {
 
		AiBuildRoad(p);
 

	
 
		// Alternate between edges
 
		swap_tile(&p->ai.start_tile_a, &p->ai.start_tile_b);
 
		swap_tile(&p->ai.cur_tile_a, &p->ai.cur_tile_b);
 
		swap_byte(&p->ai.start_dir_a, &p->ai.start_dir_b);
 
		swap_byte(&p->ai.cur_dir_a, &p->ai.cur_dir_b);
 

	
 
		return;
 
	}
 

	
 
	// Now, find two new points to build between
 
	num = p->ai.num_build_rec;
 
	aib = &p->ai.src;
 

	
 
	for (;;) {
 
		cmd = aib->buildcmd_a;
 
		aib->buildcmd_a = 255;
 
		if (cmd != 255) break;
 

	
 
		aib++;
 
		if (--num == 0) {
 
			p->ai.state = AIS_BUILD_ROAD_VEHICLES;
 
			return;
 
		}
 
	}
 

	
 
	// Find first edge to build from.
 
	tile = AiGetRoadBlockEdge(aib->cur_building_rule, aib->use_tile, &dir);
 
	p->ai.start_tile_a = tile;
 
	p->ai.cur_tile_a = tile;
 
	p->ai.start_dir_a = dir;
 
	p->ai.cur_dir_a = dir;
 

	
 
	// Find second edge to build to
 
	aib = (&p->ai.src) + (cmd&0xF);
 
	tile = AiGetRoadBlockEdge(aib->cur_building_rule, aib->use_tile, &dir);
 
	p->ai.start_tile_b = tile;
 
	p->ai.cur_tile_b = tile;
 
	p->ai.start_dir_b = dir;
 
	p->ai.cur_dir_b = dir;
 

	
 
	// And setup state.
 
	p->ai.state_mode = 2;
 
	p->ai.state_counter = 0;
 
	p->ai.banned_tile_count = 0;
 
}
 

	
 
static StationID AiGetStationIdFromRoadBlock(TileIndex tile, int id)
 
{
 
	const AiDefaultBlockData *p = _road_default_block_data[id]->data;
 
	while (p->mode != 1) p++;
 
	return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)));
 
}
 

	
 
static void AiStateBuildRoadVehicles(Player *p)
 
{
 
	const AiDefaultBlockData *ptr;
 
	TileIndex tile;
 
	VehicleID loco_id;
 
	EngineID veh;
 
	uint i;
 

	
 
	ptr = _road_default_block_data[p->ai.src.cur_building_rule]->data;
 
	for (; ptr->mode != 0; ptr++) {}
 
	tile = TILE_ADD(p->ai.src.use_tile, ToTileIndexDiff(ptr->tileoffs));
 

	
 
	veh = AiChooseRoadVehToBuild(p->ai.cargo_type, p->player_money, tile);
 
	if (veh == INVALID_ENGINE) {
 
		p->ai.state = AIS_0;
 
		return;
 
	}
 

	
 
	if (CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_ROAD_VEH))) return;
 

	
 
	loco_id = _new_vehicle_id;
 

	
 
	if (GetVehicle(loco_id)->cargo_type != p->ai.cargo_type) {
 
		/* Cargo type doesn't match, so refit it */
 
		if (CmdFailed(DoCommand(tile, loco_id, p->ai.cargo_type, DC_EXEC, CMD_REFIT_ROAD_VEH))) {
 
			/* Refit failed... sell the vehicle */
 
			DoCommand(tile, loco_id, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
 
			return;
 
		}
 
	}
 

	
 
	for (i = 0; p->ai.order_list_blocks[i] != 0xFF; i++) {
 
		const AiBuildRec* aib = &p->ai.src + p->ai.order_list_blocks[i];
 
		bool is_pass = (
 
			p->ai.cargo_type == CT_PASSENGERS ||
 
			p->ai.cargo_type == CT_MAIL ||
 
			(_opt.landscape == LT_NORMAL && p->ai.cargo_type == CT_VALUABLES)
 
		);
 
		Order order;
 

	
 
		order.type = OT_GOTO_STATION;
 
		order.flags = 0;
 
		order.dest = AiGetStationIdFromRoadBlock(aib->use_tile, aib->cur_building_rule);
 

	
 
		if (!is_pass && i == 1) order.flags |= OF_UNLOAD;
 
		if (p->ai.num_want_fullload != 0 && (is_pass || i == 0))
 
			order.flags |= OF_FULL_LOAD;
 

	
 
		DoCommand(0, loco_id + (i << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER);
 
	}
 

	
 
	DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_ROADVEH);
 
	DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
 

	
 
	if (p->ai.num_want_fullload != 0) p->ai.num_want_fullload--;
 
	if (--p->ai.num_loco_to_build == 0) p->ai.state = AIS_0;
 
}
 

	
 
static void AiStateDeleteRoadBlocks(Player *p)
 
{
 
	const AiBuildRec* aib = &p->ai.src;
 
	uint num = p->ai.num_build_rec;
 

	
 
	do {
 
		const AiDefaultBlockData* b;
 

	
 
		if (aib->cur_building_rule == 255) continue;
 
		for (b = _road_default_block_data[aib->cur_building_rule]->data; b->mode != 4; b++) {
 
			if (b->mode > 1) continue;
 
			DoCommand(TILE_ADD(aib->use_tile, ToTileIndexDiff(b->tileoffs)), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
		}
 
	} while (++aib,--num);
 

	
 
	p->ai.state = AIS_0;
 
}
 

	
 

	
 
static void AiStateAirportStuff(Player *p)
 
{
 
	const Station* st;
 
	byte acc_planes;
 
	int i;
 
	AiBuildRec *aib;
 
	byte rule;
 

	
 
	// Here we look for an airport we could use instead of building a new
 
	// one. If we find such an aiport for any waypoint,
 
	// AiStateBuildDefaultAirportBlocks() will kindly skip that one when
 
	// building the waypoints.
 

	
 
	i = 0;
 
	do {
 
		// We do this all twice - once for the source (town in the case
 
		// of oilrig route) and then for the destination (oilrig in the
 
		// case of oilrig route).
 
		aib = &p->ai.src + i;
 

	
 
		FOR_ALL_STATIONS(st) {
 
			// Is this an airport?
 
			if (!(st->facilities & FACIL_AIRPORT)) continue;
 

	
 
			// Do we own the airport? (Oilrigs aren't owned, though.)
 
			if (st->owner != OWNER_NONE && st->owner != _current_player) continue;
 

	
 
			acc_planes = GetAirport(st->airport_type)->acc_planes;
 

	
 
			// Dismiss heliports, unless we are checking an oilrig.
 
			if (acc_planes == HELICOPTERS_ONLY && (p->ai.build_kind != 1 || i != 1))
 
				continue;
 

	
 
			// Dismiss country airports if we are doing the other
 
			// endpoint of an oilrig route.
 
			if (acc_planes == AIRCRAFT_ONLY && (p->ai.build_kind == 1 && i == 0))
 
				continue;
 

	
 
			// Dismiss airports too far away.
 
			if (DistanceMax(st->airport_tile, aib->spec_tile) > aib->rand_rng)
 
				continue;
 

	
 
			// It's ideal airport, let's take it!
 

	
 
			/* XXX: This part is utterly broken - rule should
 
			 * contain number of the rule appropriate for the
 
			 * airport type (country, town, ...), see
 
			 * _airport_default_block_data (rule is just an index
 
			 * in this array). But the only difference between the
 
			 * currently existing two rules (rule 0 - town and rule
 
			 * 1 - country) is the attr field which is used only
 
			 * when building new airports - and that's irrelevant
 
			 * for us. So using just about any rule will suffice
 
			 * here for now (some of the new airport types would be
 
			 * broken because they will probably need different
 
			 * tileoff values etc), no matter that
 
			 * IsHangarTile() makes no sense. --pasky */
 
			if (acc_planes == HELICOPTERS_ONLY) {
 
				/* Heliports should have maybe own rulesets but
 
				 * OTOH we don't want AI to pick them up when
 
				 * looking for a suitable airport type to build.
 
				 * So any of rules 0 or 1 would do for now. The
 
				 * original rule number was 2 but that's a bug
 
				 * because we have no such rule. */
 
				rule = 1;
 
			} else {
 
				rule = IsHangarTile(st->airport_tile);
 
			}
 

	
 
			aib->cur_building_rule = rule;
 
			aib->use_tile = st->airport_tile;
 
			break;
 
		}
 
	} while (++i != p->ai.num_build_rec);
 

	
 
	p->ai.state = AIS_BUILD_DEFAULT_AIRPORT_BLOCKS;
 
	p->ai.state_mode = 255;
 
	p->ai.state_counter = 0;
 
}
 

	
 
static int32 AiDoBuildDefaultAirportBlock(TileIndex tile, const AiDefaultBlockData *p, byte flag)
 
{
 
	int32 total_cost = 0, ret;
 

	
 
	for (; p->mode == 0; p++) {
 
		if (!HASBIT(_avail_aircraft, p->attr)) return CMD_ERROR;
 
		ret = DoCommand(TILE_MASK(tile + ToTileIndexDiff(p->tileoffs)), p->attr,0,flag | DC_AUTO | DC_NO_WATER,CMD_BUILD_AIRPORT);
 
		if (CmdFailed(ret)) return CMD_ERROR;
 
		total_cost += ret;
 
	}
 

	
 
	return total_cost;
 
}
 

	
 
static bool AiCheckAirportResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo)
 
{
 
	uint values[NUM_CARGO];
 
	int rad;
 

	
 
	if (_patches.modified_catchment) {
 
		rad = CA_AIR_LARGE; // I Have NFI what airport the
 
	} else { // AI is going to build here
 
		rad = 4;
 
	}
 

	
 
	for (; p->mode == 0; p++) {
 
		TileIndex tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs));
 
		const AirportFTAClass* airport = GetAirport(p->attr);
 
		uint w = airport->size_x;
 
		uint h = airport->size_y;
 

	
 
		if (cargo & 0x80) {
 
			GetProductionAroundTiles(values, tile2, w, h, rad);
 
			return values[cargo & 0x7F] != 0;
 
		} else {
 
			GetAcceptanceAroundTiles(values, tile2, w, h, rad);
 
			return values[cargo] >= 8;
 
		}
 
	}
 
	return true;
 
}
 

	
 
static int AiFindBestDefaultAirportBlock(TileIndex tile, byte cargo, byte heli, int32 *cost)
 
{
 
	const AiDefaultBlockData *p;
 
	uint i;
 

	
 
	for (i = 0; (p = _airport_default_block_data[i]) != NULL; i++) {
 
		// If we are doing a helicopter service, avoid building
 
		// airports where they can't land.
 
		if (heli && GetAirport(p->attr)->acc_planes == AIRCRAFT_ONLY) continue;
 

	
 
		*cost = AiDoBuildDefaultAirportBlock(tile, p, 0);
 
		if (!CmdFailed(*cost) && AiCheckAirportResources(tile, p, cargo))
 
			return i;
 
	}
 
	return -1;
 
}
 

	
 
static void AiStateBuildDefaultAirportBlocks(Player *p)
 
{
 
	int i, j;
 
	AiBuildRec *aib;
 
	int rule;
 
	int32 cost;
 

	
 
	// time out?
 
	if (++p->ai.timeout_counter == 1388) {
 
		p->ai.state = AIS_0;
 
		return;
 
	}
 

	
 
	// do the following 8 times
 
	i = 8;
 
	do {
 
		// check if we can build the default
 
		aib = &p->ai.src;
 
		j = p->ai.num_build_rec;
 
		do {
 
			// this item has already been built?
 
			if (aib->cur_building_rule != 255) continue;
 

	
 
			// adjust the coordinate randomly,
 
			// to make sure that we find a position.
 
			aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng);
 

	
 
			// check if the aircraft stuff can be built there.
 
			rule = AiFindBestDefaultAirportBlock(aib->use_tile, aib->cargo, p->ai.build_kind, &cost);
 

	
 
//			SetRedErrorSquare(aib->use_tile);
 

	
 
			if (rule == -1) {
 
				// cannot build, terraform after a while
 
				if (p->ai.state_counter >= 600) {
 
					AiDoTerraformLand(aib->use_tile, Random()&3, 3, (int8)p->ai.state_mode);
 
				}
 
				// also try the other terraform direction
 
				if (++p->ai.state_counter >= 1000) {
 
					p->ai.state_counter = 0;
 
					p->ai.state_mode = -p->ai.state_mode;
 
				}
 
			} else if (CheckPlayerHasMoney(cost) && AiCheckBlockDistances(p,aib->use_tile)) {
 
				// player has money, build it.
 
				int32 r;
 

	
 
				aib->cur_building_rule = rule;
 

	
 
				r = AiDoBuildDefaultAirportBlock(
 
					aib->use_tile,
 
					_airport_default_block_data[rule],
 
					DC_EXEC | DC_NO_TOWN_RATING
 
				);
 
				assert(!CmdFailed(r));
 
			}
 
		} while (++aib,--j);
 
	} while (--i);
 

	
 
	// check if we're done with all of them
 
	aib = &p->ai.src;
 
	j = p->ai.num_build_rec;
 
	do {
 
		if (aib->cur_building_rule == 255) return;
 
	} while (++aib,--j);
 

	
 
	// yep, all are done. switch state.
 
	p->ai.state = AIS_BUILD_AIRCRAFT_VEHICLES;
 
}
 

	
 
static StationID AiGetStationIdFromAircraftBlock(TileIndex tile, int id)
 
{
 
	const AiDefaultBlockData *p = _airport_default_block_data[id];
 
	while (p->mode != 1) p++;
 
	return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)));
 
}
 

	
 
static void AiStateBuildAircraftVehicles(Player *p)
 
{
 
	const AiDefaultBlockData *ptr;
 
	TileIndex tile;
 
	EngineID veh;
 
	int i;
 
	VehicleID loco_id;
 

	
 
	ptr = _airport_default_block_data[p->ai.src.cur_building_rule];
 
	for (; ptr->mode != 0; ptr++) {}
 

	
 
	tile = TILE_ADD(p->ai.src.use_tile, ToTileIndexDiff(ptr->tileoffs));
 

	
 
	veh = AiChooseAircraftToBuild(p->player_money, p->ai.build_kind != 0 ? 0 : AIR_CTOL);
 
	if (veh == INVALID_ENGINE) return;
 

	
 
	/* XXX - Have the AI pick the hangar terminal in an airport. Eg get airport-type
 
	 * and offset to the FIRST depot because the AI picks the st->xy tile */
 
	tile += ToTileIndexDiff(GetAirport(GetStationByTile(tile)->airport_type)->airport_depots[0]);
 
	if (CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_AIRCRAFT))) return;
 
	loco_id = _new_vehicle_id;
 

	
 
	for (i = 0; p->ai.order_list_blocks[i] != 0xFF; i++) {
 
		AiBuildRec *aib = (&p->ai.src) + p->ai.order_list_blocks[i];
 
		bool is_pass = (p->ai.cargo_type == CT_PASSENGERS || p->ai.cargo_type == CT_MAIL);
 
		Order order;
 

	
 
		order.type = OT_GOTO_STATION;
 
		order.flags = 0;
 
		order.dest = AiGetStationIdFromAircraftBlock(aib->use_tile, aib->cur_building_rule);
 

	
 
		if (!is_pass && i == 1) order.flags |= OF_UNLOAD;
 
		if (p->ai.num_want_fullload != 0 && (is_pass || i == 0))
 
			order.flags |= OF_FULL_LOAD;
 

	
 
		DoCommand(0, loco_id + (i << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER);
 
	}
 

	
 
	DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_AIRCRAFT);
 

	
 
	DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
 

	
 
	if (p->ai.num_want_fullload != 0) p->ai.num_want_fullload--;
 

	
 
	if (--p->ai.num_loco_to_build == 0) p->ai.state = AIS_0;
 
}
 

	
 
static void AiStateCheckShipStuff(Player *p)
 
{
 
	// XXX
 
	error("!AiStateCheckShipStuff");
 
}
 

	
 
static void AiStateBuildDefaultShipBlocks(Player *p)
 
{
 
	// XXX
 
	error("!AiStateBuildDefaultShipBlocks");
 
}
 

	
 
static void AiStateDoShipStuff(Player *p)
 
{
 
	// XXX
 
	error("!AiStateDoShipStuff");
 
}
 

	
 
static void AiStateSellVeh(Player *p)
 
{
 
	Vehicle *v = p->ai.cur_veh;
 

	
 
	if (v->owner == _current_player) {
 
		if (v->type == VEH_Train) {
 

	
 
			if (!IsTileDepotType(v->tile, TRANSPORT_RAIL) || v->u.rail.track != 0x80 || !(v->vehstatus&VS_STOPPED)) {
 
				if (v->current_order.type != OT_GOTO_DEPOT)
 
					DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_TRAIN_TO_DEPOT);
 
				goto going_to_depot;
 
			}
 

	
 
			// Sell whole train
 
			DoCommand(v->tile, v->index, 1, DC_EXEC, CMD_SELL_RAIL_WAGON);
 

	
 
		} else if (v->type == VEH_Road) {
 
			if (!IsRoadVehInDepotStopped(v)) {
 
				if (v->current_order.type != OT_GOTO_DEPOT)
 
					DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_ROADVEH_TO_DEPOT);
 
				goto going_to_depot;
 
			}
 

	
 
			DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
 
		} else if (v->type == VEH_Aircraft) {
 
			if (!IsAircraftInHangarStopped(v)) {
 
				if (v->current_order.type != OT_GOTO_DEPOT)
 
					DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
 
				goto going_to_depot;
 
			}
 

	
 
			DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_AIRCRAFT);
 
			} else if (v->type == VEH_Ship) {
 
			// XXX: not implemented
 
			error("!v->type == VEH_Ship");
 
		}
 
	}
 

	
 
	goto return_to_loop;
 
going_to_depot:;
 
	if (++p->ai.state_counter <= 832) return;
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT) {
 
		v->current_order.type = OT_DUMMY;
 
		v->current_order.flags = 0;
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
	}
 
return_to_loop:;
 
	p->ai.state = AIS_VEH_LOOP;
 
}
 

	
 
static void AiStateRemoveStation(Player *p)
 
{
 
	// Remove stations that aren't in use by any vehicle
 
	byte *in_use;
 
	const Order *ord;
 
	const Station *st;
 
	TileIndex tile;
 

	
 
	// Go to this state when we're done.
 
	p->ai.state = AIS_1;
 

	
 
	// Get a list of all stations that are in use by a vehicle
 
	in_use = malloc(GetMaxStationIndex() + 1);
 
	memset(in_use, 0, GetMaxStationIndex() + 1);
 
	FOR_ALL_ORDERS(ord) {
 
		if (ord->type == OT_GOTO_STATION) in_use[ord->dest] = 1;
 
	}
 

	
 
	// Go through all stations and delete those that aren't in use
 
	FOR_ALL_STATIONS(st) {
 
		if (st->owner == _current_player && !in_use[st->index] &&
 
				( (st->bus_stops != NULL && (tile = st->bus_stops->xy) != 0) ||
 
					(st->truck_stops != NULL && (tile = st->truck_stops->xy)) != 0 ||
 
					(tile = st->train_tile) != 0 ||
 
					(tile = st->dock_tile) != 0 ||
 
					(tile = st->airport_tile) != 0)) {
 
			DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
		}
 
	}
 

	
 
	free(in_use);
 
}
 

	
 
static void AiRemovePlayerRailOrRoad(Player *p, TileIndex tile)
 
{
 
	TrackBits rails;
 

	
 
	if (IsTileType(tile, MP_RAILWAY)) {
 
		if (!IsTileOwner(tile, _current_player)) return;
 

	
 
		if (IsPlainRailTile(tile)) {
 
is_rail_crossing:;
 
			rails = GetRailTrackStatus(tile);
 

	
 
			if (rails == TRACK_BIT_HORZ || rails == TRACK_BIT_VERT) return;
 

	
 
			if (rails & TRACK_BIT_3WAY_NE) {
 
pos_0:
 
				if ((GetRailTrackStatus(TILE_MASK(tile - TileDiffXY(1, 0))) & TRACK_BIT_3WAY_SW) == 0) {
 
					p->ai.cur_dir_a = 0;
 
					p->ai.cur_tile_a = tile;
 
					p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE;
 
					return;
 
				}
 
			}
 

	
 
			if (rails & TRACK_BIT_3WAY_SE) {
 
pos_1:
 
				if ((GetRailTrackStatus(TILE_MASK(tile + TileDiffXY(0, 1))) & TRACK_BIT_3WAY_NW) == 0) {
 
					p->ai.cur_dir_a = 1;
 
					p->ai.cur_tile_a = tile;
 
					p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE;
 
					return;
 
				}
 
			}
 

	
 
			if (rails & TRACK_BIT_3WAY_SW) {
 
pos_2:
 
				if ((GetRailTrackStatus(TILE_MASK(tile + TileDiffXY(1, 0))) & TRACK_BIT_3WAY_NE) == 0) {
 
					p->ai.cur_dir_a = 2;
 
					p->ai.cur_tile_a = tile;
 
					p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE;
 
					return;
 
				}
 
			}
 

	
 
			if (rails & TRACK_BIT_3WAY_NW) {
 
pos_3:
 
				if ((GetRailTrackStatus(TILE_MASK(tile - TileDiffXY(0, 1))) & TRACK_BIT_3WAY_SE) == 0) {
 
					p->ai.cur_dir_a = 3;
 
					p->ai.cur_tile_a = tile;
 
					p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE;
 
					return;
 
				}
 
			}
 
		} else {
 
			static const byte _depot_bits[] = {0x19,0x16,0x25,0x2A};
 

	
 
			DiagDirection dir = GetRailDepotDirection(tile);
 

	
 
			if (GetRailTrackStatus(tile + TileOffsByDiagDir(dir)) & _depot_bits[dir])
 
				return;
 

	
 
			DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
		}
 
	} else if (IsTileType(tile, MP_STREET)) {
 
		if (!IsTileOwner(tile, _current_player)) return;
 

	
 
		if (IsLevelCrossing(tile)) goto is_rail_crossing;
 

	
 
		if (GetRoadTileType(tile) == ROAD_TILE_DEPOT) {
 
			DiagDirection dir;
 
			TileIndex t;
 

	
 
			// Check if there are any stations around.
 
			t = tile + TileDiffXY(-1, 0);
 
			if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_player)) return;
 

	
 
			t = tile + TileDiffXY(1, 0);
 
			if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_player)) return;
 

	
 
			t = tile + TileDiffXY(0, -1);
 
			if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_player)) return;
 

	
 
			t = tile + TileDiffXY(0, 1);
 
			if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_player)) return;
 

	
 
			dir = GetRoadDepotDirection(tile);
 

	
 
			DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
			DoCommand(
 
				TILE_MASK(tile + TileOffsByDiagDir(dir)),
 
				DiagDirToRoadBits(ReverseDiagDir(dir)),
 
				0,
 
				DC_EXEC,
 
				CMD_REMOVE_ROAD);
 
		}
 
	} else if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
		if (!IsTileOwner(tile, _current_player) ||
 
				!IsBridge(tile) ||
 
				GetBridgeTransportType(tile) != TRANSPORT_RAIL) {
 
			return;
 
		}
 

	
 
		rails = 0;
 

	
 
		switch (GetBridgeRampDirection(tile)) {
 
			default:
 
			case DIAGDIR_NE: goto pos_2;
 
			case DIAGDIR_SE: goto pos_3;
 
			case DIAGDIR_SW: goto pos_0;
 
			case DIAGDIR_NW: goto pos_1;
 
		}
 
	}
 
}
 

	
 
static void AiStateRemoveTrack(Player *p)
 
{
 
	/* Was 1000 for standard 8x8 maps. */
 
	int num = MapSizeX() * 4;
 

	
 
	do {
 
		TileIndex tile = ++p->ai.state_counter;
 

	
 
		// Iterated all tiles?
 
		if (tile >= MapSize()) {
 
			p->ai.state = AIS_REMOVE_STATION;
 
			return;
 
		}
 

	
 
		// Remove player stuff in that tile
 
		AiRemovePlayerRailOrRoad(p, tile);
 
		if (p->ai.state != AIS_REMOVE_TRACK) return;
 
	} while (--num);
 
}
 

	
 
static void AiStateRemoveSingleRailTile(Player *p)
 
{
 
	// Remove until we can't remove more.
 
	if (!AiRemoveTileAndGoForward(p)) p->ai.state = AIS_REMOVE_TRACK;
 
}
 

	
 
static AiStateAction * const _ai_actions[] = {
 
	AiCase0,
 
	AiCase1,
 
	AiStateVehLoop,
 
	AiStateCheckReplaceVehicle,
 
	AiStateDoReplaceVehicle,
 
	AiStateWantNewRoute,
 

	
 
	AiStateBuildDefaultRailBlocks,
 
	AiStateBuildRail,
 
	AiStateBuildRailVeh,
 
	AiStateDeleteRailBlocks,
 

	
 
	AiStateBuildDefaultRoadBlocks,
 
	AiStateBuildRoad,
 
	AiStateBuildRoadVehicles,
 
	AiStateDeleteRoadBlocks,
 

	
 
	AiStateAirportStuff,
 
	AiStateBuildDefaultAirportBlocks,
 
	AiStateBuildAircraftVehicles,
 

	
 
	AiStateCheckShipStuff,
 
	AiStateBuildDefaultShipBlocks,
 
	AiStateDoShipStuff,
 

	
 
	AiStateSellVeh,
 
	AiStateRemoveStation,
 
	AiStateRemoveTrack,
 

	
 
	AiStateRemoveSingleRailTile
 
};
 

	
 
extern void ShowBuyCompanyDialog(uint player);
 

	
 
static void AiHandleTakeover(Player *p)
 
{
 
	if (p->bankrupt_timeout != 0) {
 
		p->bankrupt_timeout -= 8;
 
		if (p->bankrupt_timeout > 0) return;
 
		p->bankrupt_timeout = 0;
 
		DeleteWindowById(WC_BUY_COMPANY, _current_player);
 
		if (IsLocalPlayer()) {
 
			AskExitToGameMenu();
 
			return;
 
		}
 
		if (IsHumanPlayer(_current_player)) return;
 
	}
 

	
 
	if (p->bankrupt_asked == 255) return;
 

	
 
	{
 
		uint asked = p->bankrupt_asked;
 
		Player *pp, *best_pl = NULL;
 
		int32 best_val = -1;
 
		uint old_p;
 

	
 
		// Ask the guy with the highest performance hist.
 
		FOR_ALL_PLAYERS(pp) {
 
			if (pp->is_active &&
 
					!(asked&1) &&
 
					pp->bankrupt_asked == 0 &&
 
					best_val < pp->old_economy[1].performance_history) {
 
				best_val = pp->old_economy[1].performance_history;
 
				best_pl = pp;
 
			}
 
			asked>>=1;
 
		}
 

	
 
		// Asked all players?
 
		if (best_val == -1) {
 
			p->bankrupt_asked = 255;
 
			return;
 
		}
 

	
 
		SETBIT(p->bankrupt_asked, best_pl->index);
 

	
 
		if (best_pl->index == _local_player) {
 
			p->bankrupt_timeout = 4440;
 
			ShowBuyCompanyDialog(_current_player);
 
			return;
 
		}
 
		if (IsHumanPlayer(best_pl->index)) return;
 

	
 
		// Too little money for computer to buy it?
 
		if (best_pl->player_money >> 1 >= p->bankrupt_value) {
 
			// Computer wants to buy it.
 
			old_p = _current_player;
 
			_current_player = p->index;
 
			DoCommand(0, old_p, 0, DC_EXEC, CMD_BUY_COMPANY);
 
			_current_player = old_p;
 
		}
 
	}
 
}
 

	
 
static void AiAdjustLoan(const Player* p)
 
{
 
	int32 base = AiGetBasePrice(p);
 

	
 
	if (p->player_money > base * 1400) {
 
		// Decrease loan
 
		if (p->current_loan != 0) {
 
			DoCommand(0, 0, 0, DC_EXEC, CMD_DECREASE_LOAN);
 
		}
 
	} else if (p->player_money < base * 500) {
 
		// Increase loan
 
		if (p->current_loan < _economy.max_loan &&
 
				p->num_valid_stat_ent >= 2 &&
 
				-(p->old_economy[0].expenses+p->old_economy[1].expenses) < base * 60) {
 
			DoCommand(0, 0, 0, DC_EXEC, CMD_INCREASE_LOAN);
 
		}
 
	}
 
}
 

	
 
static void AiBuildCompanyHQ(Player *p)
 
{
 
	TileIndex tile;
 

	
 
	if (p->location_of_house == 0 &&
 
			p->last_build_coordinate != 0) {
 
		tile = AdjustTileCoordRandomly(p->last_build_coordinate, 8);
 
		DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ);
 
	}
 
}
 

	
 

	
 
void AiDoGameLoop(Player *p)
 
{
 
	if (p->bankrupt_asked != 0) {
 
		AiHandleTakeover(p);
 
		return;
 
	}
 

	
 
	// Ugly hack to make sure the service interval of the AI is good, not looking
 
	//  to the patch-setting
 
	// Also, it takes into account the setting if the service-interval is in days
 
	//  or in %
 
	_ai_service_interval = _patches.servint_ispercent?80:180;
 

	
 
	if (IsHumanPlayer(_current_player)) return;
 

	
 
	AiAdjustLoan(p);
 
	AiBuildCompanyHQ(p);
 

	
 
#if 0
 
	{
 
		static byte old_state = 99;
 
		static bool hasdots = false;
 
		char *_ai_state_names[]={
 
			"AiCase0",
 
			"AiCase1",
 
			"AiStateVehLoop",
 
			"AiStateCheckReplaceVehicle",
 
			"AiStateDoReplaceVehicle",
 
			"AiStateWantNewRoute",
 
			"AiStateBuildDefaultRailBlocks",
 
			"AiStateBuildRail",
 
			"AiStateBuildRailVeh",
 
			"AiStateDeleteRailBlocks",
 
			"AiStateBuildDefaultRoadBlocks",
 
			"AiStateBuildRoad",
 
			"AiStateBuildRoadVehicles",
 
			"AiStateDeleteRoadBlocks",
 
			"AiStateAirportStuff",
 
			"AiStateBuildDefaultAirportBlocks",
 
			"AiStateBuildAircraftVehicles",
 
			"AiStateCheckShipStuff",
 
			"AiStateBuildDefaultShipBlocks",
 
			"AiStateDoShipStuff",
 
			"AiStateSellVeh",
 
			"AiStateRemoveStation",
 
			"AiStateRemoveTrack",
 
			"AiStateRemoveSingleRailTile"
 
		};
 

	
 
		if (p->ai.state != old_state) {
 
			if (hasdots)
 
				printf("\n");
 
			hasdots=false;
 
			printf("AiState: %s\n", _ai_state_names[old_state=p->ai.state]);
 
		} else {
 
			printf(".");
 
			hasdots=true;
 
		}
 
	}
 
#endif
 

	
 
	_ai_actions[p->ai.state](p);
 
}
src/ai/trolly/build.c
Show inline comments
 
deleted file
src/ai/trolly/build.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../../stdafx.h"
 
#include "../../openttd.h"
 
#include "../../debug.h"
 
#include "../../functions.h"
 
#include "../../map.h"
 
#include "../../road_map.h"
 
#include "../../tile.h"
 
#include "../../vehicle.h"
 
#include "../../command.h"
 
#include "trolly.h"
 
#include "../../engine.h"
 
#include "../../station.h"
 
#include "../../variables.h"
 
#include "../../bridge.h"
 
#include "../ai.h"
 

	
 
// Build HQ
 
//  Params:
 
//    tile : tile where HQ is going to be build
 
bool AiNew_Build_CompanyHQ(Player *p, TileIndex tile)
 
{
 
	if (CmdFailed(AI_DoCommand(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ)))
 
		return false;
 
	AI_DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ);
 
	return true;
 
}
 

	
 

	
 
// Build station
 
//  Params:
 
//    type : AI_TRAIN/AI_BUS/AI_TRUCK : indicates the type of station
 
//    tile : tile where station is going to be build
 
//    length : in case of AI_TRAIN: length of station
 
//    numtracks : in case of AI_TRAIN: tracks of station
 
//    direction : the direction of the station
 
//    flag : flag passed to DoCommand (normally 0 to get the cost or DC_EXEC to build it)
 
int AiNew_Build_Station(Player *p, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag)
 
{
 
	if (type == AI_TRAIN)
 
		return AI_DoCommand(tile, direction + (numtracks << 8) + (length << 16), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION);
 

	
 
	if (type == AI_BUS)
 
		return AI_DoCommand(tile, direction, RS_BUS, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP);
 

	
 
	return AI_DoCommand(tile, direction, RS_TRUCK, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP);
 
}
 

	
 

	
 
// Builds a brdige. The second best out of the ones available for this player
 
//  Params:
 
//   tile_a : starting point
 
//   tile_b : end point
 
//   flag : flag passed to DoCommand
 
int AiNew_Build_Bridge(Player *p, TileIndex tile_a, TileIndex tile_b, byte flag)
 
{
 
	int bridge_type, bridge_len, type, type2;
 

	
 
	// Find a good bridgetype (the best money can buy)
 
	bridge_len = GetBridgeLength(tile_a, tile_b);
 
	type = type2 = 0;
 
	for (bridge_type = MAX_BRIDGES-1; bridge_type >= 0; bridge_type--) {
 
		if (CheckBridge_Stuff(bridge_type, bridge_len)) {
 
			type2 = type;
 
			type = bridge_type;
 
			// We found two bridges, exit
 
			if (type2 != 0) break;
 
		}
 
	}
 
	// There is only one bridge that can be built
 
	if (type2 == 0 && type != 0) type2 = type;
 

	
 
	// Now, simply, build the bridge!
 
	if (p->ainew.tbt == AI_TRAIN) {
 
		return AI_DoCommand(tile_a, tile_b, (0x00 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
 
	} else {
 
		return AI_DoCommand(tile_a, tile_b, (0x80 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
 
	}
 
}
 

	
 

	
 
// Build the route part by part
 
// Basicly what this function do, is build that amount of parts of the route
 
//  that go in the same direction. It sets 'part' to the last part of the route builded.
 
//  The return value is the cost for the builded parts
 
//
 
//  Params:
 
//   PathFinderInfo : Pointer to the PathFinderInfo used for AiPathFinder
 
//   part : Which part we need to build
 
//
 
// TODO: skip already builded road-pieces (e.g.: cityroad)
 
int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte flag)
 
{
 
	int part = PathFinderInfo->position;
 
	byte *route_extra = PathFinderInfo->route_extra;
 
	TileIndex *route = PathFinderInfo->route;
 
	int dir;
 
	int old_dir = -1;
 
	int cost = 0;
 
	int res;
 
	// We need to calculate the direction with the parent of the parent.. so we skip
 
	//  the first pieces and the last piece
 
	if (part < 1) part = 1;
 
	// When we are done, stop it
 
	if (part >= PathFinderInfo->route_length - 1) {
 
		PathFinderInfo->position = -2;
 
		return 0;
 
	}
 

	
 

	
 
	if (PathFinderInfo->rail_or_road) {
 
		// Tunnel code
 
		if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) {
 
			cost += AI_DoCommand(route[part], 0, 0, flag, CMD_BUILD_TUNNEL);
 
			PathFinderInfo->position++;
 
			// TODO: problems!
 
			if (CmdFailed(cost)) {
 
				DEBUG(ai, 0, "[BuildPath] tunnel could not be built (0x%X)", route[part]);
 
				return 0;
 
			}
 
			return cost;
 
		}
 
		// Bridge code
 
		if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) {
 
			cost += AiNew_Build_Bridge(p, route[part], route[part-1], flag);
 
			PathFinderInfo->position++;
 
			// TODO: problems!
 
			if (CmdFailed(cost)) {
 
				DEBUG(ai, 0, "[BuildPath] bridge could not be built (0x%X, 0x%X)", route[part], route[part - 1]);
 
				return 0;
 
			}
 
			return cost;
 
		}
 

	
 
		// Build normal rail
 
		// Keep it doing till we go an other way
 
		if (route_extra[part - 1] == 0 && route_extra[part] == 0) {
 
			while (route_extra[part] == 0) {
 
				// Get the current direction
 
				dir = AiNew_GetRailDirection(route[part-1], route[part], route[part+1]);
 
				// Is it the same as the last one?
 
				if (old_dir != -1 && old_dir != dir) break;
 
				old_dir = dir;
 
				// Build the tile
 
				res = AI_DoCommand(route[part], 0, dir, flag, CMD_BUILD_SINGLE_RAIL);
 
				if (CmdFailed(res)) {
 
					// Problem.. let's just abort it all!
 
					p->ainew.state = AI_STATE_NOTHING;
 
					return 0;
 
				}
 
				cost += res;
 
				// Go to the next tile
 
				part++;
 
				// Check if it is still in range..
 
				if (part >= PathFinderInfo->route_length - 1) break;
 
			}
 
			part--;
 
		}
 
		// We want to return the last position, so we go back one
 
		PathFinderInfo->position = part;
 
	} else {
 
		// Tunnel code
 
		if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) {
 
			cost += AI_DoCommand(route[part], 0x200, 0, flag, CMD_BUILD_TUNNEL);
 
			PathFinderInfo->position++;
 
			// TODO: problems!
 
			if (CmdFailed(cost)) {
 
				DEBUG(ai, 0, "[BuildPath] tunnel could not be built (0x%X)", route[part]);
 
				return 0;
 
			}
 
			return cost;
 
		}
 
		// Bridge code
 
		if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) {
 
			cost += AiNew_Build_Bridge(p, route[part], route[part+1], flag);
 
			PathFinderInfo->position++;
 
			// TODO: problems!
 
			if (CmdFailed(cost)) {
 
				DEBUG(ai, 0, "[BuildPath] bridge could not be built (0x%X, 0x%X)", route[part], route[part + 1]);
 
				return 0;
 
			}
 
			return cost;
 
		}
 

	
 
		// Build normal road
 
		// Keep it doing till we go an other way
 
		// EnsureNoVehicle makes sure we don't build on a tile where a vehicle is. This way
 
		//  it will wait till the vehicle is gone..
 
		if (route_extra[part-1] == 0 && route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicle(route[part]))) {
 
			while (route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicle(route[part]))) {
 
				// Get the current direction
 
				dir = AiNew_GetRoadDirection(route[part-1], route[part], route[part+1]);
 
				// Is it the same as the last one?
 
				if (old_dir != -1 && old_dir != dir) break;
 
				old_dir = dir;
 
				// There is already some road, and it is a bridge.. don't build!!!
 
				if (!IsTileType(route[part], MP_TUNNELBRIDGE)) {
 
					// Build the tile
 
					res = AI_DoCommand(route[part], dir, 0, flag | DC_NO_WATER, CMD_BUILD_ROAD);
 
					// Currently, we ignore CMD_ERRORs!
 
					if (CmdFailed(res) && flag == DC_EXEC && !IsTileType(route[part], MP_STREET) && !EnsureNoVehicle(route[part])) {
 
						// Problem.. let's just abort it all!
 
						DEBUG(ai, 0, "[BuidPath] route building failed at tile 0x%X, aborting", route[part]);
 
						p->ainew.state = AI_STATE_NOTHING;
 
						return 0;
 
					}
 

	
 
					if (!CmdFailed(res)) cost += res;
 
				}
 
				// Go to the next tile
 
				part++;
 
				// Check if it is still in range..
 
				if (part >= PathFinderInfo->route_length - 1) break;
 
			}
 
			part--;
 
			// We want to return the last position, so we go back one
 
		}
 
		if (!EnsureNoVehicle(route[part]) && flag == DC_EXEC) part--;
 
		PathFinderInfo->position = part;
 
	}
 

	
 
	return cost;
 
}
 

	
 

	
 
// This functions tries to find the best vehicle for this type of cargo
 
// It returns INVALID_ENGINE if not suitable engine is found
 
EngineID AiNew_PickVehicle(Player *p)
 
{
 
	if (p->ainew.tbt == AI_TRAIN) {
 
		// Not supported yet
 
		return INVALID_ENGINE;
 
	} else {
 
		EngineID best_veh_index = INVALID_ENGINE;
 
		int32 best_veh_rating = 0;
 
		EngineID start = ROAD_ENGINES_INDEX;
 
		EngineID end   = ROAD_ENGINES_INDEX + NUM_ROAD_ENGINES;
 
		EngineID i;
 

	
 
		/* Loop through all road vehicles */
 
		for (i = start; i != end; i++) {
 
			const RoadVehicleInfo *rvi = RoadVehInfo(i);
 
			const Engine* e = GetEngine(i);
 
			int32 rating;
 
			int32 ret;
 

	
 
			/* Skip vehicles which can't take our cargo type */
 
			if (rvi->cargo_type != p->ainew.cargo && !CanRefitTo(i, p->ainew.cargo)) continue;
 

	
 
			// Is it availiable?
 
			// Also, check if the reliability of the vehicle is above the AI_VEHICLE_MIN_RELIABILTY
 
			if (!HASBIT(e->player_avail, _current_player) || e->reliability * 100 < AI_VEHICLE_MIN_RELIABILTY << 16) continue;
 

	
 
			/* Rate and compare the engine by speed & capacity */
 
			rating = rvi->max_speed * rvi->capacity;
 
			if (rating <= best_veh_rating) continue;
 

	
 
			// Can we build it?
 
			ret = AI_DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_ROAD_VEH);
 
			if (CmdFailed(ret)) continue;
 

	
 
			best_veh_rating = rating;
 
			best_veh_index = i;
 
		}
 

	
 
		return best_veh_index;
 
	}
 
}
 

	
 

	
 
void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	Player* p = GetPlayer(_current_player);
 

	
 
	if (success) {
 
		p->ainew.state = AI_STATE_GIVE_ORDERS;
 
		p->ainew.veh_id = _new_vehicle_id;
 

	
 
		if (GetVehicle(p->ainew.veh_id)->cargo_type != p->ainew.cargo) {
 
			/* Cargo type doesn't match, so refit it */
 
			if (CmdFailed(DoCommand(tile, p->ainew.veh_id, p->ainew.cargo, DC_EXEC, CMD_REFIT_ROAD_VEH))) {
 
				/* Refit failed, so sell the vehicle */
 
				DoCommand(tile, p->ainew.veh_id, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
 
				p->ainew.state = AI_STATE_NOTHING;
 
			}
 
		}
 
	} else {
 
		/* XXX this should be handled more gracefully */
 
		p->ainew.state = AI_STATE_NOTHING;
 
	}
 
}
 

	
 

	
 
// Builds the best vehicle possible
 
int AiNew_Build_Vehicle(Player *p, TileIndex tile, byte flag)
 
{
 
	EngineID i = AiNew_PickVehicle(p);
 

	
 
	if (i == INVALID_ENGINE) return CMD_ERROR;
 
	if (p->ainew.tbt == AI_TRAIN) return CMD_ERROR;
 

	
 
	if (flag & DC_EXEC) {
 
		return AI_DoCommandCc(tile, i, 0, flag, CMD_BUILD_ROAD_VEH, CcAI);
 
	} else {
 
		return AI_DoCommand(tile, i, 0, flag, CMD_BUILD_ROAD_VEH);
 
	}
 
}
 

	
 
int AiNew_Build_Depot(Player* p, TileIndex tile, DiagDirection direction, byte flag)
 
{
 
	int ret, ret2;
 
	if (p->ainew.tbt == AI_TRAIN) {
 
		return AI_DoCommand(tile, 0, direction, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRAIN_DEPOT);
 
	} else {
 
		ret = AI_DoCommand(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_DEPOT);
 
		if (CmdFailed(ret)) return ret;
 
		// Try to build the road from the depot
 
		ret2 = AI_DoCommand(tile + TileOffsByDiagDir(direction), DiagDirToRoadBits(ReverseDiagDir(direction)), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
 
		// If it fails, ignore it..
 
		if (CmdFailed(ret2)) return ret;
 
		return ret + ret2;
 
	}
 
}
src/ai/trolly/pathfinder.c
Show inline comments
 
deleted file
src/ai/trolly/pathfinder.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../../stdafx.h"
 
#include "../../openttd.h"
 
#include "../../bridge_map.h"
 
#include "../../debug.h"
 
#include "../../functions.h"
 
#include "../../map.h"
 
#include "../../tile.h"
 
#include "../../command.h"
 
#include "trolly.h"
 
#include "../../depot.h"
 
#include "../../tunnel_map.h"
 
#include "../../bridge.h"
 
#include "../ai.h"
 

	
 
#define TEST_STATION_NO_DIR 0xFF
 

	
 
// Tests if a station can be build on the given spot
 
// TODO: make it train compatible
 
static bool TestCanBuildStationHere(TileIndex tile, byte dir)
 
{
 
	Player *p = GetPlayer(_current_player);
 

	
 
	if (dir == TEST_STATION_NO_DIR) {
 
		int32 ret;
 
		// TODO: currently we only allow spots that can be access from al 4 directions...
 
		//  should be fixed!!!
 
		for (dir = 0; dir < 4; dir++) {
 
			ret = AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST);
 
			if (!CmdFailed(ret)) return true;
 
		}
 
		return false;
 
	}
 

	
 
	// return true if command succeeded, so the inverse of CmdFailed()
 
	return !CmdFailed(AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST));
 
}
 

	
 

	
 
static bool IsRoad(TileIndex tile)
 
{
 
	return
 
		// MP_STREET, but not a road depot?
 
		(IsTileType(tile, MP_STREET) && !IsTileDepotType(tile, TRANSPORT_ROAD)) ||
 
		(IsTileType(tile, MP_TUNNELBRIDGE) && (
 
			(IsTunnel(tile) && GetTunnelTransportType(tile) == TRANSPORT_ROAD) ||
 
			(IsBridge(tile) && GetBridgeTransportType(tile) == TRANSPORT_ROAD)
 
		));
 
}
 

	
 

	
 
// Checks if a tile 'a' is between the tiles 'b' and 'c'
 
#define TILES_BETWEEN(a, b, c) (TileX(a) >= TileX(b) && TileX(a) <= TileX(c) && TileY(a) >= TileY(b) && TileY(a) <= TileY(c))
 

	
 

	
 
// Check if the current tile is in our end-area
 
static int32 AyStar_AiPathFinder_EndNodeCheck(AyStar *aystar, OpenListNode *current)
 
{
 
	const Ai_PathFinderInfo* PathFinderInfo = aystar->user_target;
 

	
 
	// It is not allowed to have a station on the end of a bridge or tunnel ;)
 
	if (current->path.node.user_data[0] != 0) return AYSTAR_DONE;
 
	if (TILES_BETWEEN(current->path.node.tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br))
 
		if (IsTileType(current->path.node.tile, MP_CLEAR) || IsTileType(current->path.node.tile, MP_TREES))
 
			if (current->path.parent == NULL || TestCanBuildStationHere(current->path.node.tile, AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile)))
 
				return AYSTAR_FOUND_END_NODE;
 

	
 
	return AYSTAR_DONE;
 
}
 

	
 

	
 
// Calculates the hash
 
//   Currently it is a 10 bit hash, so the hash array has a max depth of 6 bits (so 64)
 
static uint AiPathFinder_Hash(uint key1, uint key2)
 
{
 
	return (TileX(key1) & 0x1F) + ((TileY(key1) & 0x1F) << 5);
 
}
 

	
 

	
 
// Clear the memory of all the things
 
static void AyStar_AiPathFinder_Free(AyStar *aystar)
 
{
 
	AyStarMain_Free(aystar);
 
	free(aystar);
 
}
 

	
 

	
 
static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
 
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
 
static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current);
 
static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current);
 

	
 

	
 
// This creates the AiPathFinder
 
AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo)
 
{
 
	PathNode start_node;
 
	uint x;
 
	uint y;
 
	// Create AyStar
 
	AyStar *result = malloc(sizeof(AyStar));
 
	init_AyStar(result, AiPathFinder_Hash, 1 << 10);
 
	// Set the function pointers
 
	result->CalculateG = AyStar_AiPathFinder_CalculateG;
 
	result->CalculateH = AyStar_AiPathFinder_CalculateH;
 
	result->EndNodeCheck = AyStar_AiPathFinder_EndNodeCheck;
 
	result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode;
 
	result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours;
 

	
 
	result->free = AyStar_AiPathFinder_Free;
 

	
 
	// Set some information
 
	result->loops_per_tick = AI_PATHFINDER_LOOPS_PER_TICK;
 
	result->max_path_cost = 0;
 
	result->max_search_nodes = AI_PATHFINDER_MAX_SEARCH_NODES;
 

	
 
	// Set the user_data to the PathFinderInfo
 
	result->user_target = PathFinderInfo;
 

	
 
	// Set the start node
 
	start_node.parent = NULL;
 
	start_node.node.direction = 0;
 
	start_node.node.user_data[0] = 0;
 

	
 
	// Now we add all the starting tiles
 
	for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) {
 
		for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) {
 
			start_node.node.tile = TileXY(x, y);
 
			result->addstart(result, &start_node.node, 0);
 
		}
 
	}
 

	
 
	return result;
 
}
 

	
 

	
 
// To reuse AyStar we sometimes have to clean all the memory
 
void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo)
 
{
 
	PathNode start_node;
 
	uint x;
 
	uint y;
 

	
 
	aystar->clear(aystar);
 

	
 
	// Set the user_data to the PathFinderInfo
 
	aystar->user_target = PathFinderInfo;
 

	
 
	// Set the start node
 
	start_node.parent = NULL;
 
	start_node.node.direction = 0;
 
	start_node.node.user_data[0] = 0;
 
	start_node.node.tile = PathFinderInfo->start_tile_tl;
 

	
 
	// Now we add all the starting tiles
 
	for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) {
 
		for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) {
 
			TileIndex tile = TileXY(x, y);
 

	
 
			if (!IsTileType(tile, MP_CLEAR) && !IsTileType(tile, MP_TREES)) continue;
 
			if (!TestCanBuildStationHere(tile, TEST_STATION_NO_DIR)) continue;
 
			start_node.node.tile = tile;
 
			aystar->addstart(aystar, &start_node.node, 0);
 
		}
 
	}
 
}
 

	
 

	
 
// The h-value, simple calculation
 
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
 
{
 
	const Ai_PathFinderInfo* PathFinderInfo = aystar->user_target;
 
	int r, r2;
 

	
 
	if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION) {
 
		// The station is pointing to a direction, add a tile towards that direction, so the H-value is more accurate
 
		r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl + TileOffsByDiagDir(PathFinderInfo->end_direction));
 
		r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br + TileOffsByDiagDir(PathFinderInfo->end_direction));
 
	} else {
 
		// No direction, so just get the fastest route to the station
 
		r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl);
 
		r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br);
 
	}
 
	// See if the bottomright is faster than the topleft..
 
	if (r2 < r) r = r2;
 
	return r * AI_PATHFINDER_H_MULTIPLER;
 
}
 

	
 

	
 
// We found the end.. let's get the route back and put it in an array
 
static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current)
 
{
 
	Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
 
	uint i = 0;
 
	PathNode *parent = &current->path;
 

	
 
	do {
 
		PathFinderInfo->route_extra[i] = parent->node.user_data[0];
 
		PathFinderInfo->route[i++] = parent->node.tile;
 
		if (i > lengthof(PathFinderInfo->route)) {
 
			// We ran out of space for the PathFinder
 
			DEBUG(ai, 0, "No more space in pathfinder route[] array");
 
			PathFinderInfo->route_length = -1; // -1 indicates out of space
 
			return;
 
		}
 
		parent = parent->parent;
 
	} while (parent != NULL);
 
	PathFinderInfo->route_length = i;
 
	DEBUG(ai, 1, "Found route of %d nodes long in %d nodes of searching", i, Hash_Size(&aystar->ClosedListHash));
 
}
 

	
 

	
 
// What tiles are around us.
 
static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current)
 
{
 
	uint i;
 
	int ret;
 
	int dir;
 

	
 
	Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
 

	
 
	aystar->num_neighbours = 0;
 

	
 
	// Go through all surrounding tiles and check if they are within the limits
 
	for (i = 0; i < 4; i++) {
 
		TileIndex ctile = current->path.node.tile; // Current tile
 
		TileIndex atile = ctile + TileOffsByDiagDir(i); // Adjacent tile
 

	
 
		if (TileX(atile) > 1 && TileX(atile) < MapMaxX() - 1 &&
 
				TileY(atile) > 1 && TileY(atile) < MapMaxY() - 1) {
 
			// We also directly test if the current tile can connect to this tile..
 
			//  We do this simply by just building the tile!
 

	
 
			// If the next step is a bridge, we have to enter it the right way
 
			if (!PathFinderInfo->rail_or_road && IsRoad(atile)) {
 
				if (IsTileType(atile, MP_TUNNELBRIDGE)) {
 
					if (IsTunnel(atile)) {
 
						if (GetTunnelDirection(atile) != i) continue;
 
					} else {
 
						if ((_m[atile].m5 & 1U) != DiagDirToAxis(i)) continue;
 
					}
 
				}
 
			}
 
			// But also if we are on a bridge, we can only move a certain direction
 
			if (!PathFinderInfo->rail_or_road && IsRoad(ctile)) {
 
				if (IsTileType(ctile, MP_TUNNELBRIDGE)) {
 
					// An existing bridge/tunnel... let's test the direction ;)
 
					if ((_m[ctile].m5 & 1U) != (i & 1)) continue;
 
				}
 
			}
 

	
 
			if ((AI_PATHFINDER_FLAG_BRIDGE & current->path.node.user_data[0]) != 0 ||
 
					(AI_PATHFINDER_FLAG_TUNNEL & current->path.node.user_data[0]) != 0) {
 
				// We are a bridge/tunnel, how cool!!
 
				//  This means we can only point forward.. get the direction from the user_data
 
				if (i != (current->path.node.user_data[0] >> 8)) continue;
 
			}
 
			dir = 0;
 

	
 
			// First, check if we have a parent
 
			if (current->path.parent == NULL && current->path.node.user_data[0] == 0) {
 
				// If not, this means we are at the starting station
 
				if (PathFinderInfo->start_direction != AI_PATHFINDER_NO_DIRECTION) {
 
					// We do need a direction?
 
					if (AiNew_GetDirection(ctile, atile) != PathFinderInfo->start_direction) {
 
						// We are not pointing the right way, invalid tile
 
						continue;
 
					}
 
				}
 
			} else if (current->path.node.user_data[0] == 0) {
 
				if (PathFinderInfo->rail_or_road) {
 
					// Rail check
 
					dir = AiNew_GetRailDirection(current->path.parent->node.tile, ctile, atile);
 
					ret = AI_DoCommand(ctile, 0, dir, DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL);
 
					if (CmdFailed(ret)) continue;
 
#ifdef AI_PATHFINDER_NO_90DEGREES_TURN
 
					if (current->path.parent->parent != NULL) {
 
						// Check if we don't make a 90degree curve
 
						int dir1 = AiNew_GetRailDirection(current->path.parent->parent->node.tile, current->path.parent->node.tile, ctile);
 
						if (_illegal_curves[dir1] == dir || _illegal_curves[dir] == dir1) {
 
							continue;
 
						}
 
					}
 
#endif
 
				} else {
 
					// Road check
 
					dir = AiNew_GetRoadDirection(current->path.parent->node.tile, ctile, atile);
 
					if (IsRoad(ctile)) {
 
						if (IsTileType(ctile, MP_TUNNELBRIDGE)) {
 
							// We have a bridge, how nicely! We should mark it...
 
							dir = 0;
 
						} else {
 
							// It already has road.. check if we miss any bits!
 
							if ((_m[ctile].m5 & dir) != dir) {
 
								// We do miss some pieces :(
 
								dir &= ~_m[ctile].m5;
 
							} else {
 
								dir = 0;
 
							}
 
						}
 
					}
 
					// Only destruct things if it is MP_CLEAR of MP_TREES
 
					if (dir != 0) {
 
						ret = AI_DoCommand(ctile, dir, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
 
						if (CmdFailed(ret)) continue;
 
					}
 
				}
 
			}
 

	
 
			// The tile can be connected
 
			aystar->neighbours[aystar->num_neighbours].tile = atile;
 
			aystar->neighbours[aystar->num_neighbours].user_data[0] = 0;
 
			aystar->neighbours[aystar->num_neighbours++].direction = 0;
 
		}
 
	}
 

	
 
	// Next step, check for bridges and tunnels
 
	if (current->path.parent != NULL && current->path.node.user_data[0] == 0) {
 
		// First we get the dir from this tile and his parent
 
		DiagDirection dir = AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile);
 
		// It means we can only walk with the track, so the bridge has to be in the same direction
 
		TileIndex tile = current->path.node.tile;
 
		TileIndex new_tile = tile;
 
		Slope tileh = GetTileSlope(tile, NULL);
 

	
 
		// Bridges can only be build on land that is not flat
 
		//  And if there is a road or rail blocking
 
		if (tileh != SLOPE_FLAT ||
 
				(PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_STREET)) ||
 
				(!PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_RAILWAY))) {
 
			for (;;) {
 
				new_tile += TileOffsByDiagDir(dir);
 

	
 
				// Precheck, is the length allowed?
 
				if (!CheckBridge_Stuff(0, GetBridgeLength(tile, new_tile))) break;
 

	
 
				// Check if we hit the station-tile.. we don't like that!
 
				if (TILES_BETWEEN(new_tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) break;
 

	
 
				// Try building the bridge..
 
				ret = AI_DoCommand(tile, new_tile, (0 << 8) + (MAX_BRIDGES / 2), DC_AUTO, CMD_BUILD_BRIDGE);
 
				if (CmdFailed(ret)) continue;
 
				// We can build a bridge here.. add him to the neighbours
 
				aystar->neighbours[aystar->num_neighbours].tile = new_tile;
 
				aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_BRIDGE + (dir << 8);
 
				aystar->neighbours[aystar->num_neighbours++].direction = 0;
 
				// We can only have 12 neighbours, and we need 1 left for tunnels
 
				if (aystar->num_neighbours == 11) break;
 
			}
 
		}
 

	
 
		// Next, check for tunnels!
 
		// Tunnels can only be built on slopes corresponding to the direction
 
		//  For now, we check both sides for this tile.. terraforming gives fuzzy result
 
		if ((dir == DIAGDIR_NE && tileh == SLOPE_NE) ||
 
				(dir == DIAGDIR_SE && tileh == SLOPE_SE) ||
 
				(dir == DIAGDIR_SW && tileh == SLOPE_SW) ||
 
				(dir == DIAGDIR_NW && tileh == SLOPE_NW)) {
 
			// Now simply check if a tunnel can be build
 
			ret = AI_DoCommand(tile, (PathFinderInfo->rail_or_road?0:0x200), 0, DC_AUTO, CMD_BUILD_TUNNEL);
 
			tileh = GetTileSlope(_build_tunnel_endtile, NULL);
 
			if (!CmdFailed(ret) && (tileh == SLOPE_SW || tileh == SLOPE_SE || tileh == SLOPE_NW || tileh == SLOPE_NE)) {
 
				aystar->neighbours[aystar->num_neighbours].tile = _build_tunnel_endtile;
 
				aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_TUNNEL + (dir << 8);
 
				aystar->neighbours[aystar->num_neighbours++].direction = 0;
 
			}
 
		}
 
	}
 
}
 

	
 

	
 
extern uint GetRailFoundation(Slope tileh, TrackBits bits); // XXX function declaration in .c
 
extern uint GetRoadFoundation(Slope tileh, uint bits); // XXX function declaration in .c
 
extern uint GetBridgeFoundation(Slope tileh, Axis); // XXX function declaration in .c
 
enum {
 
	BRIDGE_NO_FOUNDATION = 1 << 0 | 1 << 3 | 1 << 6 | 1 << 9 | 1 << 12,
 
};
 

	
 
// The most important function: it calculates the g-value
 
static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
 
{
 
	Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
 
	int r, res = 0;
 
	Slope tileh = GetTileSlope(current->tile, NULL);
 
	Slope parent_tileh = GetTileSlope(parent->path.node.tile, NULL);
 

	
 
	// Check if we hit the end-tile
 
	if (TILES_BETWEEN(current->tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) {
 
		// We are at the end-tile, check if we had a direction or something...
 
		if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION && AiNew_GetDirection(current->tile, parent->path.node.tile) != PathFinderInfo->end_direction) {
 
			// We are not pointing the right way, invalid tile
 
			return AYSTAR_INVALID_NODE;
 
		}
 
		// If it was valid, drop out.. we don't build on the endtile
 
		return 0;
 
	}
 

	
 
	// Give everything a small penalty
 
	res += AI_PATHFINDER_PENALTY;
 

	
 
	if (!PathFinderInfo->rail_or_road) {
 
		// Road has the lovely advantage it can use other road... check if
 
		//  the current tile is road, and if so, give a good bonus
 
		if (IsRoad(current->tile)) {
 
			res -= AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS;
 
		}
 
	}
 

	
 
	// We should give a penalty when the tile is going up or down.. this is one way to do so!
 
	//  Too bad we have to count it from the parent.. but that is not so bad.
 
	// We also dislike long routes on slopes, since they do not look too realistic
 
	//  when there is a flat land all around, they are more expensive to build, and
 
	//  especially they essentially block the ability to connect or cross the road
 
	//  from one side.
 
	if (parent_tileh != SLOPE_FLAT && parent->path.parent != NULL) {
 
		// Skip if the tile was from a bridge or tunnel
 
		if (parent->path.node.user_data[0] == 0 && current->user_data[0] == 0) {
 
			if (PathFinderInfo->rail_or_road) {
 
				r = GetRailFoundation(parent_tileh, 1 << AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
 
				// Maybe is BRIDGE_NO_FOUNDATION a bit strange here, but it contains just the right information..
 
				if (r >= 15 || (r == 0 && HASBIT(BRIDGE_NO_FOUNDATION, tileh))) {
 
					res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
 
				} else {
 
					res += AI_PATHFINDER_FOUNDATION_PENALTY;
 
				}
 
			} else {
 
				if (!IsRoad(parent->path.node.tile) || !IsTileType(parent->path.node.tile, MP_TUNNELBRIDGE)) {
 
					r = GetRoadFoundation(parent_tileh, AiNew_GetRoadDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
 
					if (r >= 15 || r == 0) {
 
						res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
 
					} else {
 
						res += AI_PATHFINDER_FOUNDATION_PENALTY;
 
					}
 
				}
 
			}
 
		}
 
	}
 

	
 
	// Are we part of a tunnel?
 
	if ((AI_PATHFINDER_FLAG_TUNNEL & current->user_data[0]) != 0) {
 
		// Tunnels are very expensive when build on long routes..
 
		// Ironicly, we are using BridgeCode here ;)
 
		r = AI_PATHFINDER_TUNNEL_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
 
		res += r + (r >> 8);
 
	}
 

	
 
	// Are we part of a bridge?
 
	if ((AI_PATHFINDER_FLAG_BRIDGE & current->user_data[0]) != 0) {
 
		// That means for every length a penalty
 
		res += AI_PATHFINDER_BRIDGE_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
 
		// Check if we are going up or down, first for the starting point
 
		// In user_data[0] is at the 8th bit the direction
 
		if (!HASBIT(BRIDGE_NO_FOUNDATION, parent_tileh)) {
 
			if (GetBridgeFoundation(parent_tileh, (current->user_data[0] >> 8) & 1) < 15) {
 
				res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
 
			}
 
		}
 
		// Second for the end point
 
		if (!HASBIT(BRIDGE_NO_FOUNDATION, tileh)) {
 
			if (GetBridgeFoundation(tileh, (current->user_data[0] >> 8) & 1) < 15) {
 
				res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
 
			}
 
		}
 
		if (parent_tileh == SLOPE_FLAT) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
 
		if (tileh == SLOPE_FLAT) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
 
	}
 

	
 
	//  To prevent the AI from taking the fastest way in tiles, but not the fastest way
 
	//    in speed, we have to give a good penalty to direction changing
 
	//  This way, we get almost the fastest way in tiles, and a very good speed on the track
 
	if (!PathFinderInfo->rail_or_road) {
 
		if (parent->path.parent != NULL &&
 
				AiNew_GetDirection(current->tile, parent->path.node.tile) != AiNew_GetDirection(parent->path.node.tile, parent->path.parent->node.tile)) {
 
			// When road exists, we don't like turning, but its free, so don't be to piggy about it
 
			if (IsRoad(parent->path.node.tile)) {
 
				res += AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY;
 
			} else {
 
				res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
 
			}
 
		}
 
	} else {
 
		// For rail we have 1 exeption: diagonal rail..
 
		// So we fetch 2 raildirection. That of the current one, and of the one before that
 
		if (parent->path.parent != NULL && parent->path.parent->parent != NULL) {
 
			int dir1 = AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile);
 
			int dir2 = AiNew_GetRailDirection(parent->path.parent->parent->node.tile, parent->path.parent->node.tile, parent->path.node.tile);
 
			// First, see if we are on diagonal path, that is better than straight path
 
			if (dir1 > 1) res -= AI_PATHFINDER_DIAGONAL_BONUS;
 

	
 
			// First see if they are different
 
			if (dir1 != dir2) {
 
				// dir 2 and 3 are 1 diagonal track, and 4 and 5.
 
				if (!(((dir1 == 2 || dir1 == 3) && (dir2 == 2 || dir2 == 3)) || ((dir1 == 4 || dir1 == 5) && (dir2 == 4 || dir2 == 5)))) {
 
					// It is not, so we changed of direction
 
					res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
 
				}
 
				if (parent->path.parent->parent->parent != NULL) {
 
					int dir3 = AiNew_GetRailDirection(parent->path.parent->parent->parent->node.tile, parent->path.parent->parent->node.tile, parent->path.parent->node.tile);
 
					// Check if we changed 3 tiles of direction in 3 tiles.. bad!!!
 
					if ((dir1 == 0 || dir1 == 1) && dir2 > 1 && (dir3 == 0 || dir3 == 1)) {
 
						res += AI_PATHFINDER_CURVE_PENALTY;
 
					}
 
				}
 
			}
 
		}
 
	}
 

	
 
	return (res < 0) ? 0 : res;
 
}
src/ai/trolly/shared.c
Show inline comments
 
deleted file
src/ai/trolly/shared.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../../stdafx.h"
 
#include "../../openttd.h"
 
#include "../../debug.h"
 
#include "../../map.h"
 
#include "trolly.h"
 
#include "../../vehicle.h"
 

	
 
int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c)
 
{
 
	// 0 = vert
 
	// 1 = horz
 
	// 2 = dig up-left
 
	// 3 = dig down-right
 
	// 4 = dig down-left
 
	// 5 = dig up-right
 

	
 
	uint x1 = TileX(tile_a);
 
	uint x2 = TileX(tile_b);
 
	uint x3 = TileX(tile_c);
 

	
 
	uint y1 = TileY(tile_a);
 
	uint y2 = TileY(tile_b);
 
	uint y3 = TileY(tile_c);
 

	
 
	if (y1 == y2 && y2 == y3) return 0;
 
	if (x1 == x2 && x2 == x3) return 1;
 
	if (y2 > y1) return x2 > x3 ? 2 : 4;
 
	if (x2 > x1) return y2 > y3 ? 2 : 5;
 
	if (y1 > y2) return x2 > x3 ? 5 : 3;
 
	if (x1 > x2) return y2 > y3 ? 4 : 3;
 

	
 
	return 0;
 
}
 

	
 
int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c)
 
{
 
	int x1, x2, x3;
 
	int y1, y2, y3;
 
	int r;
 

	
 
	x1 = TileX(tile_a);
 
	x2 = TileX(tile_b);
 
	x3 = TileX(tile_c);
 

	
 
	y1 = TileY(tile_a);
 
	y2 = TileY(tile_b);
 
	y3 = TileY(tile_c);
 

	
 
	r = 0;
 

	
 
	if (x1 < x2) r += 8;
 
	if (y1 < y2) r += 1;
 
	if (x1 > x2) r += 2;
 
	if (y1 > y2) r += 4;
 

	
 
	if (x2 < x3) r += 2;
 
	if (y2 < y3) r += 4;
 
	if (x2 > x3) r += 8;
 
	if (y2 > y3) r += 1;
 

	
 
	return r;
 
}
 

	
 
// Get's the direction between 2 tiles seen from tile_a
 
DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b)
 
{
 
	if (TileY(tile_a) < TileY(tile_b)) return DIAGDIR_SE;
 
	if (TileY(tile_a) > TileY(tile_b)) return DIAGDIR_NW;
 
	if (TileX(tile_a) < TileX(tile_b)) return DIAGDIR_SW;
 
	return DIAGDIR_NE;
 
}
 

	
 

	
 
// This functions looks up if this vehicle is special for this AI
 
//  and returns his flag
 
uint AiNew_GetSpecialVehicleFlag(Player* p, Vehicle* v)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) {
 
		if (p->ainew.special_vehicles[i].veh_id == v->index) {
 
			return p->ainew.special_vehicles[i].flag;
 
		}
 
	}
 

	
 
	// Not found :(
 
	return 0;
 
}
 

	
 

	
 
bool AiNew_SetSpecialVehicleFlag(Player* p, Vehicle* v, uint flag)
 
{
 
	int new_id = -1;
 
	uint i;
 

	
 
	for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) {
 
		if (p->ainew.special_vehicles[i].veh_id == v->index) {
 
			p->ainew.special_vehicles[i].flag |= flag;
 
			return true;
 
		}
 
		if (new_id == -1 &&
 
				p->ainew.special_vehicles[i].veh_id == 0 &&
 
				p->ainew.special_vehicles[i].flag == 0) {
 
			new_id = i;
 
		}
 
	}
 

	
 
	// Out of special_vehicle spots :s
 
	if (new_id == -1) {
 
		DEBUG(ai, 1, "special_vehicles list is too small");
 
		return false;
 
	}
 
	p->ainew.special_vehicles[new_id].veh_id = v->index;
 
	p->ainew.special_vehicles[new_id].flag = flag;
 
	return true;
 
}
src/ai/trolly/trolly.c
Show inline comments
 
deleted file
src/ai/trolly/trolly.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/*
 
 * This AI was created as a direct reaction to the big demand for some good AIs
 
 * in OTTD. Too bad it never left alpha-stage, and it is considered dead in its
 
 * current form.
 
 * By the time of writing this, we, the creator of this AI and a good friend of
 
 * mine, are designing a whole new AI-system that allows us to create AIs
 
 * easier and without all the fuzz we encountered while I was working on this
 
 * AI. By the time that system is finished, you can expect that this AI will
 
 * dissapear, because it is pretty obselete and bad programmed.
 
 *
 
 * Meanwhile I wish you all much fun with this AI; if you are interested as
 
 * AI-developer in this AI, I advise you not stare too long to some code, some
 
 * things in here really are... strange ;) But in either way: enjoy :)
 
 *
 
 *  -- TrueLight :: 2005-09-01
 
 */
 

	
 
#include "../../stdafx.h"
 
#include "../../openttd.h"
 
#include "../../debug.h"
 
#include "../../functions.h"
 
#include "../../road_map.h"
 
#include "../../station_map.h"
 
#include "table/strings.h"
 
#include "../../map.h"
 
#include "../../tile.h"
 
#include "../../command.h"
 
#include "trolly.h"
 
#include "../../town.h"
 
#include "../../industry.h"
 
#include "../../station.h"
 
#include "../../engine.h"
 
#include "../../gui.h"
 
#include "../../depot.h"
 
#include "../../vehicle.h"
 
#include "../../date.h"
 
#include "../ai.h"
 

	
 
// This function is called after StartUp. It is the init of an AI
 
static void AiNew_State_FirstTime(Player *p)
 
{
 
	// This assert is used to protect those function from misuse
 
	//   You have quickly a small mistake in the state-array
 
	//   With that, everything would go wrong. Finding that, is almost impossible
 
	//   With this assert, that problem can never happen.
 
	assert(p->ainew.state == AI_STATE_FIRST_TIME);
 
	// We first have to init some things
 

	
 
	if (_current_player == 1) ShowErrorMessage(INVALID_STRING_ID, TEMP_AI_IN_PROGRESS, 0, 0);
 

	
 
	// The PathFinder (AyStar)
 
	// TODO: Maybe when an AI goes bankrupt, this is de-init
 
	//  or when coming from a savegame.. should be checked out!
 
	p->ainew.path_info.start_tile_tl = 0;
 
	p->ainew.path_info.start_tile_br = 0;
 
	p->ainew.path_info.end_tile_tl = 0;
 
	p->ainew.path_info.end_tile_br = 0;
 
	p->ainew.pathfinder = new_AyStar_AiPathFinder(12, &p->ainew.path_info);
 

	
 
	p->ainew.idle = 0;
 
	p->ainew.last_vehiclecheck_date = _date;
 

	
 
	// We ALWAYS start with a bus route.. just some basic money ;)
 
	p->ainew.action = AI_ACTION_BUS_ROUTE;
 

	
 
	// Let's popup the news, and after that, start building..
 
	p->ainew.state = AI_STATE_WAKE_UP;
 
}
 

	
 

	
 
// This function just waste some time
 
//  It keeps it more real. The AI can build on such tempo no normal user
 
//  can ever keep up with that. The competitor_speed already delays a bit
 
//  but after the AI finished a track it really needs to go to sleep.
 
//
 
// Let's say, we sleep between one and three days if the AI is put on Very Fast.
 
//  This means that on Very Slow it will be between 16 and 48 days.. slow enough?
 
static void AiNew_State_Nothing(Player *p)
 
{
 
	assert(p->ainew.state == AI_STATE_NOTHING);
 
	// If we are done idling, start over again
 
	if (p->ainew.idle == 0) p->ainew.idle = AI_RandomRange(DAY_TICKS * 2) + DAY_TICKS;
 
	if (--p->ainew.idle == 0) {
 
		// We are done idling.. what you say? Let's do something!
 
		// I mean.. the next tick ;)
 
		p->ainew.state = AI_STATE_WAKE_UP;
 
	}
 
}
 

	
 

	
 
// This function picks out a task we are going to do.
 
//  Currently supported:
 
//    - Make new route
 
//    - Check route
 
//    - Build HQ
 
static void AiNew_State_WakeUp(Player *p)
 
{
 
	int32 money;
 
	int c;
 
	assert(p->ainew.state == AI_STATE_WAKE_UP);
 
	// First, check if we have a HQ
 
	if (p->location_of_house == 0) {
 
		// We have no HQ yet, build one on a random place
 
		// Random till we found a place for it!
 
		// TODO: this should not be on a random place..
 
		AiNew_Build_CompanyHQ(p, AI_Random() % MapSize());
 
		// Enough for now, but we want to come back here the next time
 
		//  so we do not change any status
 
		return;
 
	}
 

	
 
	money = p->player_money - AI_MINIMUM_MONEY;
 

	
 
	// Let's pick an action!
 
	if (p->ainew.action == AI_ACTION_NONE) {
 
		c = AI_Random() & 0xFF;
 
		if (p->current_loan > 0 &&
 
				p->old_economy[1].income > AI_MINIMUM_INCOME_FOR_LOAN &&
 
				c < 10) {
 
			p->ainew.action = AI_ACTION_REPAY_LOAN;
 
		} else if (p->ainew.last_vehiclecheck_date + AI_DAYS_BETWEEN_VEHICLE_CHECKS < _date) {
 
			// Check all vehicles once in a while
 
			p->ainew.action = AI_ACTION_CHECK_ALL_VEHICLES;
 
			p->ainew.last_vehiclecheck_date = _date;
 
		} else if (c < 100 && !_patches.ai_disable_veh_roadveh) {
 
			// Do we have any spots for road-vehicles left open?
 
			if (GetFreeUnitNumber(VEH_Road) <= _patches.max_roadveh) {
 
				if (c < 85) {
 
					p->ainew.action = AI_ACTION_TRUCK_ROUTE;
 
				} else {
 
					p->ainew.action = AI_ACTION_BUS_ROUTE;
 
				}
 
			}
 
#if 0
 
		} else if (c < 200 && !_patches.ai_disable_veh_train) {
 
			if (GetFreeUnitNumber(VEH_Train) <= _patches.max_trains) {
 
				p->ainew.action = AI_ACTION_TRAIN_ROUTE;
 
			}
 
#endif
 
		}
 

	
 
		p->ainew.counter = 0;
 
	}
 

	
 
	if (p->ainew.counter++ > AI_MAX_TRIES_FOR_SAME_ROUTE) {
 
		p->ainew.action = AI_ACTION_NONE;
 
		return;
 
	}
 

	
 
	if (_patches.ai_disable_veh_roadveh && (
 
				p->ainew.action == AI_ACTION_BUS_ROUTE ||
 
				p->ainew.action == AI_ACTION_TRUCK_ROUTE
 
			)) {
 
		p->ainew.action = AI_ACTION_NONE;
 
		return;
 
	}
 

	
 
	if (p->ainew.action == AI_ACTION_REPAY_LOAN &&
 
			money > AI_MINIMUM_LOAN_REPAY_MONEY) {
 
		// We start repaying some money..
 
		p->ainew.state = AI_STATE_REPAY_MONEY;
 
		return;
 
	}
 

	
 
	if (p->ainew.action == AI_ACTION_CHECK_ALL_VEHICLES) {
 
		p->ainew.state = AI_STATE_CHECK_ALL_VEHICLES;
 
		return;
 
	}
 

	
 
	// It is useless to start finding a route if we don't have enough money
 
	//  to build the route anyway..
 
	if (p->ainew.action == AI_ACTION_BUS_ROUTE &&
 
			money > AI_MINIMUM_BUS_ROUTE_MONEY) {
 
		if (GetFreeUnitNumber(VEH_Road) > _patches.max_roadveh) {
 
			p->ainew.action = AI_ACTION_NONE;
 
			return;
 
		}
 
		p->ainew.cargo = AI_NEED_CARGO;
 
		p->ainew.state = AI_STATE_LOCATE_ROUTE;
 
		p->ainew.tbt = AI_BUS; // Bus-route
 
		return;
 
	}
 
	if (p->ainew.action == AI_ACTION_TRUCK_ROUTE &&
 
			money > AI_MINIMUM_TRUCK_ROUTE_MONEY) {
 
		if (GetFreeUnitNumber(VEH_Road) > _patches.max_roadveh) {
 
			p->ainew.action = AI_ACTION_NONE;
 
			return;
 
		}
 
		p->ainew.cargo = AI_NEED_CARGO;
 
		p->ainew.last_id = 0;
 
		p->ainew.state = AI_STATE_LOCATE_ROUTE;
 
		p->ainew.tbt = AI_TRUCK;
 
		return;
 
	}
 

	
 
	p->ainew.state = AI_STATE_NOTHING;
 
}
 

	
 

	
 
static void AiNew_State_ActionDone(Player *p)
 
{
 
	p->ainew.action = AI_ACTION_NONE;
 
	p->ainew.state = AI_STATE_NOTHING;
 
}
 

	
 

	
 
// Check if a city or industry is good enough to start a route there
 
static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type)
 
{
 
	if (type == AI_CITY) {
 
		const Town* t = GetTown(ic);
 
		const Station* st;
 
		uint count = 0;
 
		int j = 0;
 

	
 
		// We don't like roadconstructions, don't even true such a city
 
		if (t->road_build_months != 0) return false;
 

	
 
		// Check if the rating in a city is high enough
 
		//  If not, take a chance if we want to continue
 
		if (t->ratings[_current_player] < 0 && AI_CHANCE16(1,4)) return false;
 

	
 
		if (t->max_pass - t->act_pass < AI_CHECKCITY_NEEDED_CARGO && !AI_CHANCE16(1,AI_CHECKCITY_CITY_CHANCE)) return false;
 

	
 
		// Check if we have build a station in this town the last 6 months
 
		//  else we don't do it. This is done, because stat updates can be slow
 
		//  and sometimes it takes up to 4 months before the stats are corectly.
 
		//  This way we don't get 12 busstations in one city of 100 population ;)
 
		FOR_ALL_STATIONS(st) {
 
			// Do we own it?
 
			if (st->owner == _current_player) {
 
				// Are we talking busses?
 
				if (p->ainew.tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) != FACIL_BUS_STOP) continue;
 
				// Is it the same city as we are in now?
 
				if (st->town != t) continue;
 
				// When was this station build?
 
				if (_date - st->build_date < AI_CHECKCITY_DATE_BETWEEN) return false;
 
				// Cound the amount of stations in this city that we own
 
				count++;
 
			} else {
 
				// We do not own it, request some info about the station
 
				//  we want to know if this station gets the same good. If so,
 
				//  we want to know its rating. If it is too high, we are not going
 
				//  to build there
 
				if (!st->goods[CT_PASSENGERS].last_speed) continue;
 
				// Is it around our city
 
				if (DistanceManhattan(st->xy, t->xy) > 10) continue;
 
				// It does take this cargo.. what is his rating?
 
				if (st->goods[CT_PASSENGERS].rating < AI_CHECKCITY_CARGO_RATING) continue;
 
				j++;
 
				// When this is the first station, we build a second with no problem ;)
 
				if (j == 1) continue;
 
				// The rating is high.. second station...
 
				//  a little chance that we still continue
 
				//  But if there are 3 stations of this size, we never go on...
 
				if (j == 2 && AI_CHANCE16(1, AI_CHECKCITY_CARGO_RATING_CHANCE)) continue;
 
				// We don't like this station :(
 
				return false;
 
			}
 
		}
 

	
 
		// We are about to add one...
 
		count++;
 
		// Check if we the city can provide enough cargo for this amount of stations..
 
		if (count * AI_CHECKCITY_CARGO_PER_STATION > t->max_pass) return false;
 

	
 
		// All check are okay, so we can build here!
 
		return true;
 
	}
 
	if (type == AI_INDUSTRY) {
 
		const Industry* i = GetIndustry(ic);
 
		const Station* st;
 
		int count = 0;
 
		int j = 0;
 

	
 
		if (i->town != NULL && i->town->ratings[_current_player] < 0 && AI_CHANCE16(1,4)) return false;
 

	
 
		// No limits on delevering stations!
 
		//  Or for industry that does not give anything yet
 
		if (i->produced_cargo[0] == CT_INVALID || i->total_production[0] == 0) return true;
 

	
 
		if (i->total_production[0] - i->total_transported[0] < AI_CHECKCITY_NEEDED_CARGO) return false;
 

	
 
		// Check if we have build a station in this town the last 6 months
 
		//  else we don't do it. This is done, because stat updates can be slow
 
		//  and sometimes it takes up to 4 months before the stats are corectly.
 
		FOR_ALL_STATIONS(st) {
 
			// Do we own it?
 
			if (st->owner == _current_player) {
 
				// Are we talking trucks?
 
				if (p->ainew.tbt == AI_TRUCK && (FACIL_TRUCK_STOP & st->facilities) != FACIL_TRUCK_STOP) continue;
 
				// Is it the same city as we are in now?
 
				if (st->town != i->town) continue;
 
				// When was this station build?
 
				if (_date - st->build_date < AI_CHECKCITY_DATE_BETWEEN) return false;
 
				// Cound the amount of stations in this city that we own
 
				count++;
 
			} else {
 
				// We do not own it, request some info about the station
 
				//  we want to know if this station gets the same good. If so,
 
				//  we want to know its rating. If it is too high, we are not going
 
				//  to build there
 
				if (i->produced_cargo[0] == CT_INVALID) continue;
 
				// It does not take this cargo
 
				if (!st->goods[i->produced_cargo[0]].last_speed) continue;
 
				// Is it around our industry
 
				if (DistanceManhattan(st->xy, i->xy) > 5) continue;
 
				// It does take this cargo.. what is his rating?
 
				if (st->goods[i->produced_cargo[0]].rating < AI_CHECKCITY_CARGO_RATING) continue;
 
				j++;
 
				// The rating is high.. a little chance that we still continue
 
				//  But if there are 2 stations of this size, we never go on...
 
				if (j == 1 && AI_CHANCE16(1, AI_CHECKCITY_CARGO_RATING_CHANCE)) continue;
 
				// We don't like this station :(
 
				return false;
 
			}
 
		}
 

	
 
		// We are about to add one...
 
		count++;
 
		// Check if we the city can provide enough cargo for this amount of stations..
 
		if (count * AI_CHECKCITY_CARGO_PER_STATION > i->total_production[0]) return false;
 

	
 
		// All check are okay, so we can build here!
 
		return true;
 
	}
 

	
 
	return true;
 
}
 

	
 

	
 
// This functions tries to locate a good route
 
static void AiNew_State_LocateRoute(Player *p)
 
{
 
	assert(p->ainew.state == AI_STATE_LOCATE_ROUTE);
 
	// For now, we only support PASSENGERS, CITY and BUSSES
 

	
 
	// We don't have a route yet
 
	if (p->ainew.cargo == AI_NEED_CARGO) {
 
		p->ainew.new_cost = 0; // No cost yet
 
		p->ainew.temp = -1;
 
		// Reset the counter
 
		p->ainew.counter = 0;
 

	
 
		p->ainew.from_ic = -1;
 
		p->ainew.to_ic = -1;
 
		if (p->ainew.tbt == AI_BUS) {
 
			// For now we only have a passenger route
 
			p->ainew.cargo = CT_PASSENGERS;
 

	
 
			// Find a route to cities
 
			p->ainew.from_type = AI_CITY;
 
			p->ainew.to_type = AI_CITY;
 
		} else if (p->ainew.tbt == AI_TRUCK) {
 
			p->ainew.cargo = AI_NO_CARGO;
 

	
 
			p->ainew.from_type = AI_INDUSTRY;
 
			p->ainew.to_type = AI_INDUSTRY;
 
		}
 

	
 
		// Now we are doing initing, we wait one tick
 
		return;
 
	}
 

	
 
	// Increase the counter and abort if it is taking too long!
 
	p->ainew.counter++;
 
	if (p->ainew.counter > AI_LOCATE_ROUTE_MAX_COUNTER) {
 
		// Switch back to doing nothing!
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 

	
 
	// We are going to locate a city from where we are going to connect
 
	if (p->ainew.from_ic == -1) {
 
		if (p->ainew.temp == -1) {
 
			// First, we pick a random spot to search from
 
			if (p->ainew.from_type == AI_CITY) {
 
				p->ainew.temp = AI_RandomRange(GetMaxTownIndex() + 1);
 
			} else {
 
				p->ainew.temp = AI_RandomRange(GetMaxIndustryIndex() + 1);
 
			}
 
		}
 

	
 
		if (!AiNew_Check_City_or_Industry(p, p->ainew.temp, p->ainew.from_type)) {
 
			// It was not a valid city
 
			//  increase the temp with one, and return. We will come back later here
 
			//  to try again
 
			p->ainew.temp++;
 
			if (p->ainew.from_type == AI_CITY) {
 
				if (p->ainew.temp > GetMaxTownIndex()) p->ainew.temp = 0;
 
			} else {
 
				if (p->ainew.temp > GetMaxIndustryIndex()) p->ainew.temp = 0;
 
			}
 

	
 
			// Don't do an attempt if we are trying the same id as the last time...
 
			if (p->ainew.last_id == p->ainew.temp) return;
 
			p->ainew.last_id = p->ainew.temp;
 

	
 
			return;
 
		}
 

	
 
		// We found a good city/industry, save the data of it
 
		p->ainew.from_ic = p->ainew.temp;
 

	
 
		// Start the next tick with finding a to-city
 
		p->ainew.temp = -1;
 
		return;
 
	}
 

	
 
	// Find a to-city
 
	if (p->ainew.temp == -1) {
 
		// First, we pick a random spot to search to
 
		if (p->ainew.to_type == AI_CITY) {
 
			p->ainew.temp = AI_RandomRange(GetMaxTownIndex() + 1);
 
		} else {
 
			p->ainew.temp = AI_RandomRange(GetMaxIndustryIndex() + 1);
 
		}
 
	}
 

	
 
	// The same city is not allowed
 
	// Also check if the city is valid
 
	if (p->ainew.temp != p->ainew.from_ic && AiNew_Check_City_or_Industry(p, p->ainew.temp, p->ainew.to_type)) {
 
		// Maybe it is valid..
 

	
 
		/* We need to know if they are not to far apart from eachother..
 
		 * We do that by checking how much cargo we have to move and how long the
 
		 * route is.
 
		 */
 

	
 
		if (p->ainew.from_type == AI_CITY && p->ainew.tbt == AI_BUS) {
 
			const Town* town_from = GetTown(p->ainew.from_ic);
 
			const Town* town_temp = GetTown(p->ainew.temp);
 
			uint distance = DistanceManhattan(town_from->xy, town_temp->xy);
 
			int max_cargo;
 

	
 
			max_cargo  = town_from->max_pass + town_temp->max_pass;
 
			max_cargo -= town_from->act_pass + town_temp->act_pass;
 

	
 
			// max_cargo is now the amount of cargo we can move between the two cities
 
			// If it is more than the distance, we allow it
 
			if (distance <= max_cargo * AI_LOCATEROUTE_BUS_CARGO_DISTANCE) {
 
				// We found a good city/industry, save the data of it
 
				p->ainew.to_ic = p->ainew.temp;
 
				p->ainew.state = AI_STATE_FIND_STATION;
 

	
 
				DEBUG(ai, 1, "[LocateRoute] found bus-route of %d tiles long (from %d to %d)",
 
					distance,
 
					p->ainew.from_ic,
 
					p->ainew.temp
 
				);
 

	
 
				p->ainew.from_tile = 0;
 
				p->ainew.to_tile = 0;
 

	
 
				return;
 
			}
 
		} else if (p->ainew.tbt == AI_TRUCK) {
 
			const Industry* ind_from = GetIndustry(p->ainew.from_ic);
 
			const Industry* ind_temp = GetIndustry(p->ainew.temp);
 
			bool found = false;
 
			int max_cargo = 0;
 
			uint i;
 

	
 
			// TODO: in max_cargo, also check other cargo (beside [0])
 
			// First we check if the from_ic produces cargo that this ic accepts
 
			if (ind_from->produced_cargo[0] != CT_INVALID && ind_from->total_production[0] != 0) {
 
				for (i = 0; i < lengthof(ind_temp->accepts_cargo); i++) {
 
					if (ind_temp->accepts_cargo[i] == CT_INVALID) break;
 
					if (ind_from->produced_cargo[0] == ind_temp->accepts_cargo[i]) {
 
						// Found a compatible industry
 
						max_cargo = ind_from->total_production[0] - ind_from->total_transported[0];
 
						found = true;
 
						p->ainew.from_deliver = true;
 
						p->ainew.to_deliver = false;
 
						break;
 
					}
 
				}
 
			}
 
			if (!found && ind_temp->produced_cargo[0] != CT_INVALID && ind_temp->total_production[0] != 0) {
 
				// If not check if the current ic produces cargo that the from_ic accepts
 
				for (i = 0; i < lengthof(ind_from->accepts_cargo); i++) {
 
					if (ind_from->accepts_cargo[i] == CT_INVALID) break;
 
					if (ind_temp->produced_cargo[0] == ind_from->accepts_cargo[i]) {
 
						// Found a compatbiel industry
 
						found = true;
 
						max_cargo = ind_temp->total_production[0] - ind_temp->total_transported[0];
 
						p->ainew.from_deliver = false;
 
						p->ainew.to_deliver = true;
 
						break;
 
					}
 
				}
 
			}
 
			if (found) {
 
				// Yeah, they are compatible!!!
 
				// Check the length against the amount of goods
 
				uint distance = DistanceManhattan(ind_from->xy, ind_temp->xy);
 

	
 
				if (distance > AI_LOCATEROUTE_TRUCK_MIN_DISTANCE &&
 
						distance <= max_cargo * AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE) {
 
					p->ainew.to_ic = p->ainew.temp;
 
					if (p->ainew.from_deliver) {
 
						p->ainew.cargo = ind_from->produced_cargo[0];
 
					} else {
 
						p->ainew.cargo = ind_temp->produced_cargo[0];
 
					}
 
					p->ainew.state = AI_STATE_FIND_STATION;
 

	
 
					DEBUG(ai, 1, "[LocateRoute] found truck-route of %d tiles long (from %d to %d)",
 
						distance,
 
						p->ainew.from_ic,
 
						p->ainew.temp
 
					);
 

	
 
					p->ainew.from_tile = 0;
 
					p->ainew.to_tile = 0;
 

	
 
					return;
 
				}
 
			}
 
		}
 
	}
 

	
 
	// It was not a valid city
 
	//  increase the temp with one, and return. We will come back later here
 
	//  to try again
 
	p->ainew.temp++;
 
	if (p->ainew.to_type == AI_CITY) {
 
		if (p->ainew.temp > GetMaxTownIndex()) p->ainew.temp = 0;
 
	} else {
 
		if (p->ainew.temp > GetMaxIndustryIndex()) p->ainew.temp = 0;
 
	}
 

	
 
	// Don't do an attempt if we are trying the same id as the last time...
 
	if (p->ainew.last_id == p->ainew.temp) return;
 
	p->ainew.last_id = p->ainew.temp;
 
}
 

	
 

	
 
// Check if there are not more than a certain amount of vehicles pointed to a certain
 
//  station. This to prevent 10 busses going to one station, which gives... problems ;)
 
static bool AiNew_CheckVehicleStation(Player *p, Station *st)
 
{
 
	int count = 0;
 
	Vehicle *v;
 

	
 
	// Also check if we don't have already a lot of busses to this city...
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->owner == _current_player) {
 
			const Order *order;
 

	
 
			FOR_VEHICLE_ORDERS(v, order) {
 
				if (order->type == OT_GOTO_STATION && GetStation(order->dest) == st) {
 
					// This vehicle has this city in its list
 
					count++;
 
				}
 
			}
 
		}
 
	}
 

	
 
	if (count > AI_CHECK_MAX_VEHICLE_PER_STATION) return false;
 
	return true;
 
}
 

	
 
// This function finds a good spot for a station
 
static void AiNew_State_FindStation(Player *p)
 
{
 
	TileIndex tile;
 
	Station *st;
 
	int count = 0;
 
	EngineID i;
 
	TileIndex new_tile = 0;
 
	byte direction = 0;
 
	Town *town = NULL;
 
	assert(p->ainew.state == AI_STATE_FIND_STATION);
 

	
 
	if (p->ainew.from_tile == 0) {
 
		// First we scan for a station in the from-city
 
		if (p->ainew.from_type == AI_CITY) {
 
			town = GetTown(p->ainew.from_ic);
 
			tile = town->xy;
 
		} else {
 
			tile = GetIndustry(p->ainew.from_ic)->xy;
 
		}
 
	} else if (p->ainew.to_tile == 0) {
 
		// Second we scan for a station in the to-city
 
		if (p->ainew.to_type == AI_CITY) {
 
			town = GetTown(p->ainew.to_ic);
 
			tile = town->xy;
 
		} else {
 
			tile = GetIndustry(p->ainew.to_ic)->xy;
 
		}
 
	} else {
 
		// Unsupported request
 
		// Go to FIND_PATH
 
		p->ainew.temp = -1;
 
		p->ainew.state = AI_STATE_FIND_PATH;
 
		return;
 
	}
 

	
 
	// First, we are going to look at the stations that already exist inside the city
 
	//  If there is enough cargo left in the station, we take that station
 
	//  If that is not possible, and there are more than 2 stations in the city, abort
 
	i = AiNew_PickVehicle(p);
 
	// Euhmz, this should not happen _EVER_
 
	// Quit finding a route...
 
	if (i == INVALID_ENGINE) {
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 

	
 
	FOR_ALL_STATIONS(st) {
 
		if (st->owner == _current_player) {
 
			if (p->ainew.tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) == FACIL_BUS_STOP) {
 
				if (st->town == town) {
 
					// Check how much cargo there is left in the station
 
					if ((st->goods[p->ainew.cargo].waiting_acceptance & 0xFFF) > RoadVehInfo(i)->capacity * AI_STATION_REUSE_MULTIPLER) {
 
						if (AiNew_CheckVehicleStation(p, st)) {
 
							// We did found a station that was good enough!
 
							new_tile = st->xy;
 
							direction = GetRoadStopDir(st->xy);
 
							break;
 
						}
 
					}
 
					count++;
 
				}
 
			}
 
		}
 
	}
 
	// We are going to add a new station...
 
	if (new_tile == 0) count++;
 
	// No more than 2 stations allowed in a city
 
	//  This is because only the best 2 stations of one cargo do get any cargo
 
	if (count > 2) {
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 

	
 
	if (new_tile == 0 && p->ainew.tbt == AI_BUS) {
 
		uint x, y, i = 0;
 
		int r;
 
		uint best;
 
		uint accepts[NUM_CARGO];
 
		TileIndex found_spot[AI_FINDSTATION_TILE_RANGE*AI_FINDSTATION_TILE_RANGE*4];
 
		uint found_best[AI_FINDSTATION_TILE_RANGE*AI_FINDSTATION_TILE_RANGE*4];
 
		// To find a good spot we scan a range from the center, a get the point
 
		//  where we get the most cargo and where it is buildable.
 
		// TODO: also check for station of myself and make sure we are not
 
		//   taking eachothers passangers away (bad result when it does not)
 
		for (x = TileX(tile) - AI_FINDSTATION_TILE_RANGE; x <= TileX(tile) + AI_FINDSTATION_TILE_RANGE; x++) {
 
			for (y = TileY(tile) - AI_FINDSTATION_TILE_RANGE; y <= TileY(tile) + AI_FINDSTATION_TILE_RANGE; y++) {
 
				new_tile = TileXY(x, y);
 
				if (IsTileType(new_tile, MP_CLEAR) || IsTileType(new_tile, MP_TREES)) {
 
					// This tile we can build on!
 
					// Check acceptance
 
					// XXX - Get the catchment area
 
					GetAcceptanceAroundTiles(accepts, new_tile, 1, 1, 4);
 
					// >> 3 == 0 means no cargo
 
					if (accepts[p->ainew.cargo] >> 3 == 0) continue;
 
					// See if we can build the station
 
					r = AiNew_Build_Station(p, p->ainew.tbt, new_tile, 0, 0, 0, DC_QUERY_COST);
 
					if (CmdFailed(r)) continue;
 
					// We can build it, so add it to found_spot
 
					found_spot[i] = new_tile;
 
					found_best[i++] = accepts[p->ainew.cargo];
 
				}
 
			}
 
		}
 

	
 
		// If i is still zero, we did not find anything
 
		if (i == 0) {
 
			p->ainew.state = AI_STATE_NOTHING;
 
			return;
 
		}
 

	
 
		// Go through all the found_best and check which has the highest value
 
		best = 0;
 
		new_tile = 0;
 

	
 
		for (x = 0; x < i; x++) {
 
			if (found_best[x] > best ||
 
					(found_best[x] == best && DistanceManhattan(tile, new_tile) > DistanceManhattan(tile, found_spot[x]))) {
 
				new_tile = found_spot[x];
 
				best = found_best[x];
 
			}
 
		}
 

	
 
		// See how much it is going to cost us...
 
		r = AiNew_Build_Station(p, p->ainew.tbt, new_tile, 0, 0, 0, DC_QUERY_COST);
 
		p->ainew.new_cost += r;
 

	
 
		direction = AI_PATHFINDER_NO_DIRECTION;
 
	} else if (new_tile == 0 && p->ainew.tbt == AI_TRUCK) {
 
		// Truck station locater works differently.. a station can be on any place
 
		//  as long as it is in range. So we give back code AI_STATION_RANGE
 
		//  so the pathfinder routine can work it out!
 
		new_tile = AI_STATION_RANGE;
 
		direction = AI_PATHFINDER_NO_DIRECTION;
 
	}
 

	
 
	if (p->ainew.from_tile == 0) {
 
		p->ainew.from_tile = new_tile;
 
		p->ainew.from_direction = direction;
 
		// Now we found thisone, go in for to_tile
 
		return;
 
	} else if (p->ainew.to_tile == 0) {
 
		p->ainew.to_tile = new_tile;
 
		p->ainew.to_direction = direction;
 
		// K, done placing stations!
 
		p->ainew.temp = -1;
 
		p->ainew.state = AI_STATE_FIND_PATH;
 
		return;
 
	}
 
}
 

	
 

	
 
// We try to find a path between 2 points
 
static void AiNew_State_FindPath(Player *p)
 
{
 
	int r;
 
	assert(p->ainew.state == AI_STATE_FIND_PATH);
 

	
 
	// First time, init some data
 
	if (p->ainew.temp == -1) {
 
		// Init path_info
 
		if (p->ainew.from_tile == AI_STATION_RANGE) {
 
			const Industry* i = GetIndustry(p->ainew.from_ic);
 

	
 
			// For truck routes we take a range around the industry
 
			p->ainew.path_info.start_tile_tl = i->xy - TileDiffXY(1, 1);
 
			p->ainew.path_info.start_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1);
 
			p->ainew.path_info.start_direction = p->ainew.from_direction;
 
		} else {
 
			p->ainew.path_info.start_tile_tl = p->ainew.from_tile;
 
			p->ainew.path_info.start_tile_br = p->ainew.from_tile;
 
			p->ainew.path_info.start_direction = p->ainew.from_direction;
 
		}
 

	
 
		if (p->ainew.to_tile == AI_STATION_RANGE) {
 
			const Industry* i = GetIndustry(p->ainew.to_ic);
 

	
 
			p->ainew.path_info.end_tile_tl = i->xy - TileDiffXY(1, 1);
 
			p->ainew.path_info.end_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1);
 
			p->ainew.path_info.end_direction = p->ainew.to_direction;
 
		} else {
 
			p->ainew.path_info.end_tile_tl = p->ainew.to_tile;
 
			p->ainew.path_info.end_tile_br = p->ainew.to_tile;
 
			p->ainew.path_info.end_direction = p->ainew.to_direction;
 
		}
 

	
 
		p->ainew.path_info.rail_or_road = (p->ainew.tbt == AI_TRAIN);
 

	
 
		// First, clean the pathfinder with our new begin and endpoints
 
		clean_AyStar_AiPathFinder(p->ainew.pathfinder, &p->ainew.path_info);
 

	
 
		p->ainew.temp = 0;
 
	}
 

	
 
	// Start the pathfinder
 
	r = p->ainew.pathfinder->main(p->ainew.pathfinder);
 
	switch (r) {
 
		case AYSTAR_NO_PATH:
 
			DEBUG(ai, 1, "No route found by pathfinder");
 
			// Start all over again
 
			p->ainew.state = AI_STATE_NOTHING;
 
			break;
 

	
 
		case AYSTAR_FOUND_END_NODE: // We found the end-point
 
			p->ainew.temp = -1;
 
			p->ainew.state = AI_STATE_FIND_DEPOT;
 
			break;
 

	
 
		// In any other case, we are still busy finding the route
 
		default: break;
 
	}
 
}
 

	
 

	
 
// This function tries to locate a good place for a depot!
 
static void AiNew_State_FindDepot(Player *p)
 
{
 
	// To place the depot, we walk through the route, and if we find a lovely spot (MP_CLEAR, MP_TREES), we place it there..
 
	// Simple, easy, works!
 
	// To make the depot stand in the middle of the route, we start from the center..
 
	// But first we walk through the route see if we can find a depot that is ours
 
	//  this keeps things nice ;)
 
	int g, i, r;
 
	DiagDirection j;
 
	TileIndex tile;
 
	assert(p->ainew.state == AI_STATE_FIND_DEPOT);
 

	
 
	p->ainew.depot_tile = 0;
 

	
 
	for (i=2;i<p->ainew.path_info.route_length-2;i++) {
 
		tile = p->ainew.path_info.route[i];
 
		for (j = 0; j < 4; j++) {
 
			TileIndex t = tile + TileOffsByDiagDir(j);
 

	
 
			if (IsTileType(t, MP_STREET) &&
 
					GetRoadTileType(t) == ROAD_TILE_DEPOT &&
 
					IsTileOwner(t, _current_player) &&
 
					GetRoadDepotDirection(t) == ReverseDiagDir(j)) {
 
				p->ainew.depot_tile = t;
 
				p->ainew.depot_direction = ReverseDiagDir(j);
 
				p->ainew.state = AI_STATE_VERIFY_ROUTE;
 
				return;
 
			}
 
		}
 
	}
 

	
 
	// This routine let depot finding start in the middle, and work his way to the stations
 
	// It makes depot placing nicer :)
 
	i = p->ainew.path_info.route_length / 2;
 
	g = 1;
 
	while (i > 1 && i < p->ainew.path_info.route_length - 2) {
 
		i += g;
 
		g *= -1;
 
		(g < 0?g--:g++);
 

	
 
		if (p->ainew.path_info.route_extra[i] != 0 || p->ainew.path_info.route_extra[i+1] != 0) {
 
			// Bridge or tunnel.. we can't place a depot there
 
			continue;
 
		}
 

	
 
		tile = p->ainew.path_info.route[i];
 

	
 
		for (j = 0; j < 4; j++) {
 
			TileIndex t = tile + TileOffsByDiagDir(j);
 

	
 
			// It may not be placed on the road/rail itself
 
			// And because it is not build yet, we can't see it on the tile..
 
			// So check the surrounding tiles :)
 
			if (t == p->ainew.path_info.route[i - 1] ||
 
					t == p->ainew.path_info.route[i + 1]) {
 
				continue;
 
			}
 
			// Not around a bridge?
 
			if (p->ainew.path_info.route_extra[i] != 0) continue;
 
			if (IsTileType(tile, MP_TUNNELBRIDGE)) continue;
 
			// Is the terrain clear?
 
			if (IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)) {
 
				// If the current tile is on a slope then we do not allow this
 
				if (GetTileSlope(tile, NULL) != SLOPE_FLAT) continue;
 
				// Check if everything went okay..
 
				r = AiNew_Build_Depot(p, t, ReverseDiagDir(j), 0);
 
				if (CmdFailed(r)) continue;
 
				// Found a spot!
 
				p->ainew.new_cost += r;
 
				p->ainew.depot_tile = t;
 
				p->ainew.depot_direction = ReverseDiagDir(j); // Reverse direction
 
				p->ainew.state = AI_STATE_VERIFY_ROUTE;
 
				return;
 
			}
 
		}
 
	}
 

	
 
	// Failed to find a depot?
 
	p->ainew.state = AI_STATE_NOTHING;
 
}
 

	
 

	
 
// This function calculates how many vehicles there are needed on this
 
//  traject.
 
// It works pretty simple: get the length, see how much we move around
 
//  and hussle that, and you know how many vehicles there are needed.
 
// It returns the cost for the vehicles
 
static int AiNew_HowManyVehicles(Player *p)
 
{
 
	if (p->ainew.tbt == AI_BUS) {
 
		// For bus-routes we look at the time before we are back in the station
 
		EngineID i;
 
		int length, tiles_a_day;
 
		int amount;
 
		i = AiNew_PickVehicle(p);
 
		if (i == INVALID_ENGINE) return 0;
 
		// Passenger run.. how long is the route?
 
		length = p->ainew.path_info.route_length;
 
		// Calculating tiles a day a vehicle moves is not easy.. this is how it must be done!
 
		tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16;
 
		// We want a vehicle in a station once a month at least, so, calculate it!
 
		// (the * 2 is because we have 2 stations ;))
 
		amount = length * 2 * 2 / tiles_a_day / 30;
 
		if (amount == 0) amount = 1;
 
		return amount;
 
	} else if (p->ainew.tbt == AI_TRUCK) {
 
		// For truck-routes we look at the cargo
 
		EngineID i;
 
		int length, amount, tiles_a_day;
 
		int max_cargo;
 
		i = AiNew_PickVehicle(p);
 
		if (i == INVALID_ENGINE) return 0;
 
		// Passenger run.. how long is the route?
 
		length = p->ainew.path_info.route_length;
 
		// Calculating tiles a day a vehicle moves is not easy.. this is how it must be done!
 
		tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16;
 
		if (p->ainew.from_deliver) {
 
			max_cargo = GetIndustry(p->ainew.from_ic)->total_production[0];
 
		} else {
 
			max_cargo = GetIndustry(p->ainew.to_ic)->total_production[0];
 
		}
 

	
 
		// This is because moving 60% is more than we can dream of!
 
		max_cargo *= 0.6;
 
		// We want all the cargo to be gone in a month.. so, we know the cargo it delivers
 
		//  we know what the vehicle takes with him, and we know the time it takes him
 
		//  to get back here.. now let's do some math!
 
		amount = 2 * length * max_cargo / tiles_a_day / 30 / RoadVehInfo(i)->capacity;
 
		amount += 1;
 
		return amount;
 
	} else {
 
		// Currently not supported
 
		return 0;
 
	}
 
}
 

	
 

	
 
// This function checks:
 
//   - If the route went okay
 
//   - Calculates the amount of money needed to build the route
 
//   - Calculates how much vehicles needed for the route
 
static void AiNew_State_VerifyRoute(Player *p)
 
{
 
	int res, i;
 
	assert(p->ainew.state == AI_STATE_VERIFY_ROUTE);
 

	
 
	// Let's calculate the cost of the path..
 
	//  new_cost already contains the cost of the stations
 
	p->ainew.path_info.position = -1;
 

	
 
	do {
 
		p->ainew.path_info.position++;
 
		p->ainew.new_cost += AiNew_Build_RoutePart(p, &p->ainew.path_info, DC_QUERY_COST);
 
	} while (p->ainew.path_info.position != -2);
 

	
 
	// Now we know the price of build station + path. Now check how many vehicles
 
	//  we need and what the price for that will be
 
	res = AiNew_HowManyVehicles(p);
 
	// If res == 0, no vehicle was found, or an other problem did occour
 
	if (res == 0) {
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 
	p->ainew.amount_veh = res;
 
	p->ainew.cur_veh = 0;
 

	
 
	// Check how much it it going to cost us..
 
	for (i=0;i<res;i++) {
 
		p->ainew.new_cost += AiNew_Build_Vehicle(p, 0, DC_QUERY_COST);
 
	}
 

	
 
	// Now we know how much the route is going to cost us
 
	//  Check if we have enough money for it!
 
	if (p->ainew.new_cost > p->player_money - AI_MINIMUM_MONEY) {
 
		// Too bad..
 
		DEBUG(ai, 1, "Insufficient funds to build route (%d)", p->ainew.new_cost);
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 

	
 
	// Now we can build the route, check the direction of the stations!
 
	if (p->ainew.from_direction == AI_PATHFINDER_NO_DIRECTION) {
 
		p->ainew.from_direction = AiNew_GetDirection(p->ainew.path_info.route[p->ainew.path_info.route_length-1], p->ainew.path_info.route[p->ainew.path_info.route_length-2]);
 
	}
 
	if (p->ainew.to_direction == AI_PATHFINDER_NO_DIRECTION) {
 
		p->ainew.to_direction = AiNew_GetDirection(p->ainew.path_info.route[0], p->ainew.path_info.route[1]);
 
	}
 
	if (p->ainew.from_tile == AI_STATION_RANGE)
 
		p->ainew.from_tile = p->ainew.path_info.route[p->ainew.path_info.route_length-1];
 
	if (p->ainew.to_tile == AI_STATION_RANGE)
 
		p->ainew.to_tile = p->ainew.path_info.route[0];
 

	
 
	p->ainew.state = AI_STATE_BUILD_STATION;
 
	p->ainew.temp = 0;
 

	
 
	DEBUG(ai, 1, "The route is set and buildable, building 0x%X to 0x%X...", p->ainew.from_tile, p->ainew.to_tile);
 
}
 

	
 

	
 
// Build the stations
 
static void AiNew_State_BuildStation(Player *p)
 
{
 
	int res = 0;
 
	assert(p->ainew.state == AI_STATE_BUILD_STATION);
 
	if (p->ainew.temp == 0) {
 
		if (!IsTileType(p->ainew.from_tile, MP_STATION))
 
			res = AiNew_Build_Station(p, p->ainew.tbt, p->ainew.from_tile, 0, 0, p->ainew.from_direction, DC_EXEC);
 
	} else {
 
		if (!IsTileType(p->ainew.to_tile, MP_STATION))
 
			res = AiNew_Build_Station(p, p->ainew.tbt, p->ainew.to_tile, 0, 0, p->ainew.to_direction, DC_EXEC);
 
		p->ainew.state = AI_STATE_BUILD_PATH;
 
	}
 
	if (CmdFailed(res)) {
 
		DEBUG(ai, 0, "[BuildStation] station could not be built (0x%X)", p->ainew.to_tile);
 
		p->ainew.state = AI_STATE_NOTHING;
 
		// If the first station _was_ build, destroy it
 
		if (p->ainew.temp != 0)
 
			AI_DoCommand(p->ainew.from_tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
		return;
 
	}
 
	p->ainew.temp++;
 
}
 

	
 

	
 
// Build the path
 
static void AiNew_State_BuildPath(Player *p)
 
{
 
	assert(p->ainew.state == AI_STATE_BUILD_PATH);
 
	// p->ainew.temp is set to -1 when this function is called for the first time
 
	if (p->ainew.temp == -1) {
 
		DEBUG(ai, 1, "Starting to build new path");
 
		// Init the counter
 
		p->ainew.counter = (4 - _opt.diff.competitor_speed) * AI_BUILDPATH_PAUSE + 1;
 
		// Set the position to the startingplace (-1 because in a minute we do ++)
 
		p->ainew.path_info.position = -1;
 
		// And don't do this again
 
		p->ainew.temp = 0;
 
	}
 
	// Building goes very fast on normal rate, so we are going to slow it down..
 
	//  By let the counter count from AI_BUILDPATH_PAUSE to 0, we have a nice way :)
 
	if (--p->ainew.counter != 0) return;
 
	p->ainew.counter = (4 - _opt.diff.competitor_speed) * AI_BUILDPATH_PAUSE + 1;
 

	
 
	// Increase the building position
 
	p->ainew.path_info.position++;
 
	// Build route
 
	AiNew_Build_RoutePart(p, &p->ainew.path_info, DC_EXEC);
 
	if (p->ainew.path_info.position == -2) {
 
		// This means we are done building!
 

	
 
		if (p->ainew.tbt == AI_TRUCK && !_patches.roadveh_queue) {
 
			// If they not queue, they have to go up and down to try again at a station...
 
			// We don't want that, so try building some road left or right of the station
 
			int dir1, dir2, dir3;
 
			TileIndex tile;
 
			int i, ret;
 
			for (i=0;i<2;i++) {
 
				if (i == 0) {
 
					tile = p->ainew.from_tile + TileOffsByDiagDir(p->ainew.from_direction);
 
					dir1 = p->ainew.from_direction - 1;
 
					if (dir1 < 0) dir1 = 3;
 
					dir2 = p->ainew.from_direction + 1;
 
					if (dir2 > 3) dir2 = 0;
 
					dir3 = p->ainew.from_direction;
 
				} else {
 
					tile = p->ainew.to_tile + TileOffsByDiagDir(p->ainew.to_direction);
 
					dir1 = p->ainew.to_direction - 1;
 
					if (dir1 < 0) dir1 = 3;
 
					dir2 = p->ainew.to_direction + 1;
 
					if (dir2 > 3) dir2 = 0;
 
					dir3 = p->ainew.to_direction;
 
				}
 

	
 
				ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir1)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
				if (!CmdFailed(ret)) {
 
					dir1 = TileOffsByDiagDir(dir1);
 
					if (IsTileType(tile + dir1, MP_CLEAR) || IsTileType(tile + dir1, MP_TREES)) {
 
						ret = AI_DoCommand(tile+dir1, AiNew_GetRoadDirection(tile, tile+dir1, tile+dir1+dir1), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						if (!CmdFailed(ret)) {
 
							if (IsTileType(tile + dir1 + dir1, MP_CLEAR) || IsTileType(tile + dir1 + dir1, MP_TREES))
 
								AI_DoCommand(tile+dir1+dir1, AiNew_GetRoadDirection(tile+dir1, tile+dir1+dir1, tile+dir1+dir1+dir1), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						}
 
					}
 
				}
 

	
 
				ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir2)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
				if (!CmdFailed(ret)) {
 
					dir2 = TileOffsByDiagDir(dir2);
 
					if (IsTileType(tile + dir2, MP_CLEAR) || IsTileType(tile + dir2, MP_TREES)) {
 
						ret = AI_DoCommand(tile+dir2, AiNew_GetRoadDirection(tile, tile+dir2, tile+dir2+dir2), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						if (!CmdFailed(ret)) {
 
							if (IsTileType(tile + dir2 + dir2, MP_CLEAR) || IsTileType(tile + dir2 + dir2, MP_TREES))
 
								AI_DoCommand(tile+dir2+dir2, AiNew_GetRoadDirection(tile+dir2, tile+dir2+dir2, tile+dir2+dir2+dir2), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						}
 
					}
 
				}
 

	
 
				ret = AI_DoCommand(tile, DiagDirToRoadBits(dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
				if (!CmdFailed(ret)) {
 
					dir3 = TileOffsByDiagDir(dir3);
 
					if (IsTileType(tile + dir3, MP_CLEAR) || IsTileType(tile + dir3, MP_TREES)) {
 
						ret = AI_DoCommand(tile+dir3, AiNew_GetRoadDirection(tile, tile+dir3, tile+dir3+dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						if (!CmdFailed(ret)) {
 
							if (IsTileType(tile + dir3 + dir3, MP_CLEAR) || IsTileType(tile + dir3 + dir3, MP_TREES))
 
								AI_DoCommand(tile+dir3+dir3, AiNew_GetRoadDirection(tile+dir3, tile+dir3+dir3, tile+dir3+dir3+dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						}
 
					}
 
				}
 
			}
 
		}
 

	
 
		DEBUG(ai, 1, "Finished building path, cost: %d", p->ainew.new_cost);
 
		p->ainew.state = AI_STATE_BUILD_DEPOT;
 
	}
 
}
 

	
 

	
 
// Builds the depot
 
static void AiNew_State_BuildDepot(Player *p)
 
{
 
	int res = 0;
 
	assert(p->ainew.state == AI_STATE_BUILD_DEPOT);
 

	
 
	if (IsTileType(p->ainew.depot_tile, MP_STREET) && GetRoadTileType(p->ainew.depot_tile) == ROAD_TILE_DEPOT) {
 
		if (IsTileOwner(p->ainew.depot_tile, _current_player)) {
 
			// The depot is already built
 
			p->ainew.state = AI_STATE_BUILD_VEHICLE;
 
			return;
 
		} else {
 
			// There is a depot, but not of our team! :(
 
			p->ainew.state = AI_STATE_NOTHING;
 
			return;
 
		}
 
	}
 

	
 
	// There is a bus on the tile we want to build road on... idle till he is gone! (BAD PERSON! :p)
 
	if (!EnsureNoVehicle(p->ainew.depot_tile + TileOffsByDiagDir(p->ainew.depot_direction)))
 
		return;
 

	
 
	res = AiNew_Build_Depot(p, p->ainew.depot_tile, p->ainew.depot_direction, DC_EXEC);
 
	if (CmdFailed(res)) {
 
		DEBUG(ai, 0, "[BuildDepot] depot could not be built (0x%X)", p->ainew.depot_tile);
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 

	
 
	p->ainew.state = AI_STATE_BUILD_VEHICLE;
 
	p->ainew.idle = 10;
 
	p->ainew.veh_main_id = INVALID_VEHICLE;
 
}
 

	
 

	
 
// Build vehicles
 
static void AiNew_State_BuildVehicle(Player *p)
 
{
 
	int res;
 
	assert(p->ainew.state == AI_STATE_BUILD_VEHICLE);
 

	
 
	// Check if we need to build a vehicle
 
	if (p->ainew.amount_veh == 0) {
 
		// Nope, we are done!
 
		// This means: we are all done! The route is open.. go back to NOTHING
 
		//  He will idle some time and it will all start over again.. :)
 
		p->ainew.state = AI_STATE_ACTION_DONE;
 
		return;
 
	}
 
	if (--p->ainew.idle != 0) return;
 
	// It is realistic that the AI can only build 1 vehicle a day..
 
	// This makes sure of that!
 
	p->ainew.idle = AI_BUILD_VEHICLE_TIME_BETWEEN;
 

	
 
	// Build the vehicle
 
	res = AiNew_Build_Vehicle(p, p->ainew.depot_tile, DC_EXEC);
 
	if (CmdFailed(res)) {
 
		// This happens when the AI can't build any more vehicles!
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 
	// Increase the current counter
 
	p->ainew.cur_veh++;
 
	// Decrease the total counter
 
	p->ainew.amount_veh--;
 
	// Go give some orders!
 
	p->ainew.state = AI_STATE_WAIT_FOR_BUILD;
 
}
 

	
 

	
 
// Put the stations in the order list
 
static void AiNew_State_GiveOrders(Player *p)
 
{
 
	int idx;
 
	Order order;
 

	
 
	assert(p->ainew.state == AI_STATE_GIVE_ORDERS);
 

	
 
	if (p->ainew.veh_main_id != INVALID_VEHICLE) {
 
		AI_DoCommand(0, p->ainew.veh_id + (p->ainew.veh_main_id << 16), 0, DC_EXEC, CMD_CLONE_ORDER);
 

	
 
		p->ainew.state = AI_STATE_START_VEHICLE;
 
		return;
 
	} else {
 
		p->ainew.veh_main_id = p->ainew.veh_id;
 
	}
 

	
 
	// Very handy for AI, goto depot.. but yeah, it needs to be activated ;)
 
	if (_patches.gotodepot) {
 
		idx = 0;
 
		order.type = OT_GOTO_DEPOT;
 
		order.flags = OF_UNLOAD;
 
		order.dest = GetDepotByTile(p->ainew.depot_tile)->index;
 
		AI_DoCommand(0, p->ainew.veh_id + (idx << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER);
 
	}
 

	
 
	idx = 0;
 
	order.type = OT_GOTO_STATION;
 
	order.flags = 0;
 
	order.dest = GetStationIndex(p->ainew.to_tile);
 
	if (p->ainew.tbt == AI_TRUCK && p->ainew.to_deliver)
 
		order.flags |= OF_FULL_LOAD;
 
	AI_DoCommand(0, p->ainew.veh_id + (idx << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER);
 

	
 
	idx = 0;
 
	order.type = OT_GOTO_STATION;
 
	order.flags = 0;
 
	order.dest = GetStationIndex(p->ainew.from_tile);
 
	if (p->ainew.tbt == AI_TRUCK && p->ainew.from_deliver)
 
		order.flags |= OF_FULL_LOAD;
 
	AI_DoCommand(0, p->ainew.veh_id + (idx << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER);
 

	
 
	// Start the engines!
 
	p->ainew.state = AI_STATE_START_VEHICLE;
 
}
 

	
 

	
 
// Start the vehicle
 
static void AiNew_State_StartVehicle(Player *p)
 
{
 
	assert(p->ainew.state == AI_STATE_START_VEHICLE);
 

	
 
	// Skip the first order if it is a second vehicle
 
	//  This to make vehicles go different ways..
 
	if (p->ainew.cur_veh & 1)
 
		AI_DoCommand(0, p->ainew.veh_id, 0, DC_EXEC, CMD_SKIP_ORDER);
 

	
 
	// 3, 2, 1... go! (give START_STOP command ;))
 
	AI_DoCommand(0, p->ainew.veh_id, 0, DC_EXEC, CMD_START_STOP_ROADVEH);
 
	// Try to build an other vehicle (that function will stop building when needed)
 
	p->ainew.idle  = 10;
 
	p->ainew.state = AI_STATE_BUILD_VEHICLE;
 
}
 

	
 

	
 
// Repays money
 
static void AiNew_State_RepayMoney(Player *p)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < AI_LOAN_REPAY; i++) {
 
		AI_DoCommand(0, 0, 0, DC_EXEC, CMD_DECREASE_LOAN);
 
	}
 
	p->ainew.state = AI_STATE_ACTION_DONE;
 
}
 

	
 

	
 
static void AiNew_CheckVehicle(Player *p, Vehicle *v)
 
{
 
	// When a vehicle is under the 6 months, we don't check for anything
 
	if (v->age < 180) return;
 

	
 
	// When a vehicle is older then 1 year, it should make money...
 
	if (v->age > 360) {
 
		// If both years together are not more than AI_MINIMUM_ROUTE_PROFIT,
 
		//  it is not worth the line I guess...
 
		if (v->profit_last_year + v->profit_this_year < AI_MINIMUM_ROUTE_PROFIT ||
 
				(v->reliability * 100 >> 16) < 40) {
 
			// There is a possibility that the route is fucked up...
 
			if (v->cargo_days > AI_VEHICLE_LOST_DAYS) {
 
				// The vehicle is lost.. check the route, or else, get the vehicle
 
				//  back to a depot
 
				// TODO: make this piece of code
 
			}
 

	
 

	
 
			// We are already sending him back
 
			if (AiNew_GetSpecialVehicleFlag(p, v) & AI_VEHICLEFLAG_SELL) {
 
				if (v->type == VEH_Road && IsTileDepotType(v->tile, TRANSPORT_ROAD) &&
 
						(v->vehstatus&VS_STOPPED)) {
 
					// We are at the depot, sell the vehicle
 
					AI_DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
 
				}
 
				return;
 
			}
 

	
 
			if (!AiNew_SetSpecialVehicleFlag(p, v, AI_VEHICLEFLAG_SELL)) return;
 
			{
 
				int ret = 0;
 
				if (v->type == VEH_Road)
 
					ret = AI_DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_ROADVEH_TO_DEPOT);
 
				// This means we can not find a depot :s
 
				//				if (CmdFailed(ret))
 
			}
 
		}
 
	}
 
}
 

	
 

	
 
// Checks all vehicles if they are still valid and make money and stuff
 
static void AiNew_State_CheckAllVehicles(Player *p)
 
{
 
	Vehicle *v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->owner != p->index) continue;
 
		// Currently, we only know how to handle road-vehicles
 
		if (v->type != VEH_Road) continue;
 

	
 
		AiNew_CheckVehicle(p, v);
 
	}
 

	
 
	p->ainew.state = AI_STATE_ACTION_DONE;
 
}
 

	
 

	
 
// Using the technique simular to the original AI
 
//   Keeps things logical
 
// It really should be in the same order as the AI_STATE's are!
 
static AiNew_StateFunction* const _ainew_state[] = {
 
	NULL,
 
	AiNew_State_FirstTime,
 
	AiNew_State_Nothing,
 
	AiNew_State_WakeUp,
 
	AiNew_State_LocateRoute,
 
	AiNew_State_FindStation,
 
	AiNew_State_FindPath,
 
	AiNew_State_FindDepot,
 
	AiNew_State_VerifyRoute,
 
	AiNew_State_BuildStation,
 
	AiNew_State_BuildPath,
 
	AiNew_State_BuildDepot,
 
	AiNew_State_BuildVehicle,
 
	NULL,
 
	AiNew_State_GiveOrders,
 
	AiNew_State_StartVehicle,
 
	AiNew_State_RepayMoney,
 
	AiNew_State_CheckAllVehicles,
 
	AiNew_State_ActionDone,
 
	NULL,
 
};
 

	
 
static void AiNew_OnTick(Player *p)
 
{
 
	if (_ainew_state[p->ainew.state] != NULL)
 
		_ainew_state[p->ainew.state](p);
 
}
 

	
 

	
 
void AiNewDoGameLoop(Player *p)
 
{
 
	if (p->ainew.state == AI_STATE_STARTUP) {
 
		// The AI just got alive!
 
		p->ainew.state = AI_STATE_FIRST_TIME;
 
		p->ainew.tick = 0;
 

	
 
		// Only startup the AI
 
		return;
 
	}
 

	
 
	// We keep a ticker. We use it for competitor_speed
 
	p->ainew.tick++;
 

	
 
	// If we come here, we can do a tick.. do so!
 
	AiNew_OnTick(p);
 
}
src/aircraft_gui.c
Show inline comments
 
deleted file
src/aircraft_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "aircraft.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "vehicle.h"
 
#include "gfx.h"
 
#include "command.h"
 
#include "engine.h"
 
#include "viewport.h"
 
#include "player.h"
 
#include "depot.h"
 
#include "vehicle_gui.h"
 
#include "newgrf_engine.h"
 

	
 

	
 
void CcCloneAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) ShowAircraftViewWindow(GetVehicle(_new_vehicle_id));
 
}
 

	
 
static void AircraftDetailsWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const Vehicle *v = GetVehicle(w->window_number);
 

	
 
		SetWindowWidgetDisabledState(w, 2, v->owner != _local_player);
 

	
 
		/* Disable service-scroller when interval is set to disabled */
 
		SetWindowWidgetDisabledState(w, 5, !_patches.servint_aircraft);
 
		SetWindowWidgetDisabledState(w, 6, !_patches.servint_aircraft);
 

	
 
		SetDParam(0, v->string_id);
 
		SetDParam(1, v->unitnumber);
 
		DrawWindowWidgets(w);
 

	
 
		/* Draw running cost */
 
		{
 
			int year = v->age / 366;
 

	
 
			SetDParam(1, year);
 

	
 
			SetDParam(0, (v->age + 365 < v->max_age) ? STR_AGE : STR_AGE_RED);
 
			SetDParam(2, v->max_age / 366);
 
			SetDParam(3, _price.aircraft_running * AircraftVehInfo(v->engine_type)->running_cost >> 8);
 
			DrawString(2, 15, STR_A00D_AGE_RUNNING_COST_YR, 0);
 
		}
 

	
 
		/* Draw max speed */
 
		{
 
			SetDParam(0, v->max_speed * 128 / 10);
 
			DrawString(2, 25, STR_A00E_MAX_SPEED, 0);
 
		}
 

	
 
		/* Draw profit */
 
		{
 
			SetDParam(0, v->profit_this_year);
 
			SetDParam(1, v->profit_last_year);
 
			DrawString(2, 35, STR_A00F_PROFIT_THIS_YEAR_LAST_YEAR, 0);
 
		}
 

	
 
		/* Draw breakdown & reliability */
 
		{
 
			SetDParam(0, v->reliability * 100 >> 16);
 
			SetDParam(1, v->breakdowns_since_last_service);
 
			DrawString(2, 45, STR_A010_RELIABILITY_BREAKDOWNS, 0);
 
		}
 

	
 
		/* Draw service interval text */
 
		{
 
			SetDParam(0, v->service_interval);
 
			SetDParam(1, v->date_of_last_service);
 
			DrawString(13, 103, _patches.servint_ispercent?STR_SERVICING_INTERVAL_PERCENT:STR_883C_SERVICING_INTERVAL_DAYS, 0);
 
		}
 

	
 
		DrawAircraftImage(v, 3, 57, INVALID_VEHICLE);
 

	
 
		{
 
			const Vehicle *u;
 
			int y = 57;
 

	
 
			do {
 
				if (v->subtype <= 2) {
 
					SetDParam(0, GetCustomEngineName(v->engine_type));
 
					SetDParam(1, v->build_year);
 
					SetDParam(2, v->value);
 
					DrawString(60, y, STR_A011_BUILT_VALUE, 0);
 
					y += 10;
 

	
 
					SetDParam(0, v->cargo_type);
 
					SetDParam(1, v->cargo_cap);
 
					u = v->next;
 
					SetDParam(2, u->cargo_type);
 
					SetDParam(3, u->cargo_cap);
 
					DrawString(60, y, (u->cargo_cap != 0) ? STR_A019_CAPACITY : STR_A01A_CAPACITY, 0);
 
					y += 14;
 
				}
 

	
 
				if (v->cargo_count != 0) {
 

	
 
					/* Cargo names (fix pluralness) */
 
					SetDParam(0, v->cargo_type);
 
					SetDParam(1, v->cargo_count);
 
					SetDParam(2, v->cargo_source);
 
					DrawString(60, y, STR_8813_FROM, 0);
 

	
 
					y += 10;
 
				}
 
			} while ( (v=v->next) != NULL);
 
		}
 
	} break;
 

	
 
	case WE_CLICK: {
 
		int mod;
 
		const Vehicle *v;
 
		switch (e->we.click.widget) {
 
		case 2: /* rename */
 
			v = GetVehicle(w->window_number);
 
			SetDParam(0, v->unitnumber);
 
			ShowQueryString(v->string_id, STR_A030_NAME_AIRCRAFT, 31, 150, w, CS_ALPHANUMERAL);
 
			break;
 
		case 5: /* increase int */
 
			mod = _ctrl_pressed? 5 : 10;
 
			goto do_change_service_int;
 
		case 6: /* decrease int */
 
			mod = _ctrl_pressed?- 5 : -10;
 
do_change_service_int:
 
			v = GetVehicle(w->window_number);
 

	
 
			mod = GetServiceIntervalClamped(mod + v->service_interval);
 
			if (mod == v->service_interval) return;
 

	
 
			DoCommandP(v->tile, v->index, mod, NULL, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING));
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_ON_EDIT_TEXT:
 
		if (e->we.edittext.str[0] != '\0') {
 
			_cmd_text = e->we.edittext.str;
 
			DoCommandP(0, w->window_number, 0, NULL,
 
				CMD_NAME_VEHICLE | CMD_MSG(STR_A031_CAN_T_NAME_AIRCRAFT));
 
		}
 
		break;
 
	}
 
}
 

	
 

	
 
static const Widget _aircraft_details_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   349,     0,    13, STR_A00C_DETAILS, STR_018C_WINDOW_TITLE_DRAG_THIS },
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   350,   389,     0,    13, STR_01AA_NAME,    STR_A032_NAME_AIRCRAFT },
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   389,    14,    55, 0x0,              STR_NULL },
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   389,    56,   101, 0x0,              STR_NULL },
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    10,   102,   107, STR_0188,         STR_884D_INCREASE_SERVICING_INTERVAL },
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    10,   108,   113, STR_0189,         STR_884E_DECREASE_SERVICING_INTERVAL },
 
{      WWT_PANEL,   RESIZE_NONE,    14,    11,   389,   102,   113, 0x0,              STR_NULL },
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _aircraft_details_desc = {
 
	WDP_AUTO, WDP_AUTO, 390, 114,
 
	WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_aircraft_details_widgets,
 
	AircraftDetailsWndProc
 
};
 

	
 

	
 
static void ShowAircraftDetailsWindow(const Vehicle *v)
 
{
 
	Window *w;
 
	VehicleID veh = v->index;
 

	
 
	DeleteWindowById(WC_VEHICLE_ORDERS, veh);
 
	DeleteWindowById(WC_VEHICLE_DETAILS, veh);
 

	
 
	w = AllocateWindowDescFront(&_aircraft_details_desc, veh);
 
	w->caption_color = v->owner;
 
//	w->vscroll.cap = 6;
 
//	w->traindetails_d.tab = 0;
 
}
 

	
 

	
 
static const Widget _aircraft_view_widgets[] = {
 
{   WWT_CLOSEBOX,  RESIZE_NONE,  14,   0,  10,   0,  13, STR_00C5,                 STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION, RESIZE_RIGHT,  14,  11, 237,   0,  13, STR_A00A,                 STR_018C_WINDOW_TITLE_DRAG_THIS },
 
{  WWT_STICKYBOX,    RESIZE_LR,  14, 238, 249,   0,  13, 0x0,                      STR_STICKY_BUTTON },
 
{      WWT_PANEL,    RESIZE_RB,  14,   0, 231,  14, 103, 0x0,                      STR_NULL },
 
{      WWT_INSET,    RESIZE_RB,  14,   2, 229,  16, 101, 0x0,                      STR_NULL },
 
{    WWT_PUSHBTN,   RESIZE_RTB,  14,   0, 237, 104, 115, 0x0,                      STR_A027_CURRENT_AIRCRAFT_ACTION },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  14,  31, SPR_CENTRE_VIEW_VEHICLE,  STR_A029_CENTER_MAIN_VIEW_ON_AIRCRAFT },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  32,  49, SPR_SEND_AIRCRAFT_TODEPOT,STR_A02A_SEND_AIRCRAFT_TO_HANGAR },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  50,  67, SPR_REFIT_VEHICLE,        STR_A03B_REFIT_AIRCRAFT_TO_CARRY },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  68,  85, SPR_SHOW_ORDERS,          STR_A028_SHOW_AIRCRAFT_S_ORDERS },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  86, 103, SPR_SHOW_VEHICLE_DETAILS, STR_A02B_SHOW_AIRCRAFT_DETAILS },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR,  14, 232, 249,  32,  49, SPR_CLONE_AIRCRAFT,       STR_CLONE_AIRCRAFT_INFO },
 
{      WWT_PANEL,   RESIZE_LRB,  14, 232, 249, 104, 103, 0x0,                      STR_NULL },
 
{  WWT_RESIZEBOX,  RESIZE_LRTB,  14, 238, 249, 104, 115, 0x0,                      STR_NULL },
 
{   WIDGETS_END},
 
};
 

	
 

	
 
static void AircraftViewWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const Vehicle *v = GetVehicle(w->window_number);
 
		StringID str;
 
		bool is_localplayer = v->owner == _local_player;
 

	
 
		SetWindowWidgetDisabledState(w,  7, !is_localplayer);
 
		SetWindowWidgetDisabledState(w,  8, !IsAircraftInHangarStopped(v) || !is_localplayer);
 
		SetWindowWidgetDisabledState(w, 11, !is_localplayer);
 

	
 

	
 
		/* draw widgets & caption */
 
		SetDParam(0, v->string_id);
 
		SetDParam(1, v->unitnumber);
 
		DrawWindowWidgets(w);
 

	
 
		if (v->vehstatus & VS_CRASHED) {
 
			str = STR_8863_CRASHED;
 
		} else if (v->vehstatus & VS_STOPPED) {
 
			str = STR_8861_STOPPED;
 
		} else {
 
			switch (v->current_order.type) {
 
			case OT_GOTO_STATION: {
 
				SetDParam(0, v->current_order.dest);
 
				SetDParam(1, v->cur_speed * 128 / 10);
 
				str = STR_HEADING_FOR_STATION + _patches.vehicle_speed;
 
			} break;
 

	
 
			case OT_GOTO_DEPOT: {
 
				/* Aircrafts always go to a station, even if you say depot */
 
				SetDParam(0, v->current_order.dest);
 
				SetDParam(1, v->cur_speed * 128 / 10);
 
				if (HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT) && !HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) {
 
					str = STR_HEADING_FOR_HANGAR + _patches.vehicle_speed;
 
				} else {
 
					str = STR_HEADING_FOR_HANGAR_SERVICE + _patches.vehicle_speed;
 
				}
 
			} break;
 

	
 
			case OT_LOADING:
 
				str = STR_882F_LOADING_UNLOADING;
 
				break;
 

	
 
			default:
 
				if (v->num_orders == 0) {
 
					str = STR_NO_ORDERS + _patches.vehicle_speed;
 
					SetDParam(0, v->cur_speed * 128 / 10);
 
				} else {
 
					str = STR_EMPTY;
 
				}
 
				break;
 
			}
 
		}
 

	
 
		/* draw the flag plus orders */
 
		DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, 2, w->widget[5].top + 1);
 
		DrawStringCenteredTruncated(w->widget[5].left + 8, w->widget[5].right, w->widget[5].top + 1, str, 0);
 
		DrawWindowViewport(w);
 
	} break;
 

	
 
	case WE_CLICK: {
 
		const Vehicle *v = GetVehicle(w->window_number);
 

	
 
		switch (e->we.click.widget) {
 
		case 5: /* start stop */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_AIRCRAFT | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT));
 
			break;
 
		case 6: /* center main view */
 
			ScrollMainWindowTo(v->x_pos, v->y_pos);
 
			break;
 
		case 7: /* goto hangar */
 
			DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_MSG(STR_A012_CAN_T_SEND_AIRCRAFT_TO));
 
			break;
 
		case 8: /* refit */
 
			ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID);
 
			break;
 
		case 9: /* show orders */
 
			ShowOrdersWindow(v);
 
			break;
 
		case 10: /* show details */
 
			ShowAircraftDetailsWindow(v);
 
			break;
 
		case 11:
 
			/* clone vehicle */
 
			DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneAircraft, CMD_CLONE_VEHICLE | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT));
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_RESIZE:
 
		w->viewport->width          += e->we.sizing.diff.x;
 
		w->viewport->height         += e->we.sizing.diff.y;
 
		w->viewport->virtual_width  += e->we.sizing.diff.x;
 
		w->viewport->virtual_height += e->we.sizing.diff.y;
 
		break;
 

	
 
	case WE_DESTROY:
 
		DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number);
 
		DeleteWindowById(WC_VEHICLE_REFIT, w->window_number);
 
		DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number);
 
		break;
 

	
 
	case WE_MOUSELOOP: {
 
		const Vehicle *v = GetVehicle(w->window_number);
 
		bool plane_stopped = IsAircraftInHangarStopped(v);
 

	
 
		/* Widget 7 (send to hangar) must be hidden if the plane is already stopped in hangar.
 
		 * Widget 11 (clone) should then be shown, since cloning is allowed only while in hangar and stopped.
 
		 * This sytem allows to have two buttons, on top of each other*/
 
		if (plane_stopped != IsWindowWidgetHidden(w, 7) || plane_stopped == IsWindowWidgetHidden(w, 11)) {
 
			SetWindowWidgetHiddenState(w,  7, plane_stopped);  // send to hangar
 
			SetWindowWidgetHiddenState(w, 11, !plane_stopped); // clone
 
			SetWindowDirty(w);
 
		}
 
	} break;
 
	}
 
}
 

	
 

	
 
static const WindowDesc _aircraft_view_desc = {
 
	WDP_AUTO, WDP_AUTO, 250, 116,
 
	WC_VEHICLE_VIEW ,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_aircraft_view_widgets,
 
	AircraftViewWndProc
 
};
 

	
 

	
 
void ShowAircraftViewWindow(const Vehicle *v)
 
{
 
	Window *w = AllocateWindowDescFront(&_aircraft_view_desc, v->index);
 

	
 
	if (w != NULL) {
 
		w->caption_color = v->owner;
 
		AssignWindowViewport(w, 3, 17, 0xE2, 0x54, w->window_number | (1 << 31), 0);
 
	}
 
}
src/airport.c
Show inline comments
 
deleted file
src/airport.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "map.h"
 
#include "airport.h"
 
#include "macros.h"
 
#include "variables.h"
 
#include "airport_movement.h"
 
#include "date.h"
 

	
 
/* Uncomment this to print out a full report of the airport-structure
 
 * You should either use
 
 * - true: full-report, print out every state and choice with string-names
 
 * OR
 
 * - false: give a summarized report which only shows current and next position */
 
//#define DEBUG_AIRPORT false
 

	
 
static AirportFTAClass *CountryAirport;
 
static AirportFTAClass *CityAirport;
 
static AirportFTAClass *Oilrig;
 
static AirportFTAClass *Heliport;
 
static AirportFTAClass *MetropolitanAirport;
 
static AirportFTAClass *InternationalAirport;
 
static AirportFTAClass *CommuterAirport;
 
static AirportFTAClass *HeliDepot;
 
static AirportFTAClass *IntercontinentalAirport;
 
static AirportFTAClass *HeliStation;
 

	
 
static void AirportFTAClass_Constructor(AirportFTAClass *apc,
 
	const byte *terminals, const byte *helipads,
 
	const byte entry_point,  const byte acc_planes,
 
	const AirportFTAbuildup *apFA,
 
	const TileIndexDiffC *depots, const byte nof_depots,
 
	uint size_x, uint size_y
 
);
 
static void AirportFTAClass_Destructor(AirportFTAClass *apc);
 

	
 
static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA);
 
static void AirportBuildAutomata(AirportFTAClass *apc, const AirportFTAbuildup *apFA);
 
static byte AirportGetTerminalCount(const byte *terminals, byte *groups);
 
static byte AirportTestFTA(const AirportFTAClass *apc);
 

	
 
#ifdef DEBUG_AIRPORT
 
static void AirportPrintOut(const AirportFTAClass *apc, bool full_report);
 
#endif /* DEBUG_AIRPORT */
 

	
 
void InitializeAirports(void)
 
{
 
	// country airport
 
	CountryAirport = malloc(sizeof(AirportFTAClass));
 

	
 
	AirportFTAClass_Constructor(
 
		CountryAirport,
 
		_airport_terminal_country,
 
		NULL,
 
		16,
 
		ALL,
 
		_airport_fta_country,
 
		_airport_depots_country,
 
		lengthof(_airport_depots_country),
 
		4, 3
 
	);
 

	
 
	// city airport
 
	CityAirport = malloc(sizeof(AirportFTAClass));
 

	
 
	AirportFTAClass_Constructor(
 
		CityAirport,
 
		_airport_terminal_city,
 
		NULL,
 
		19,
 
		ALL,
 
		_airport_fta_city,
 
		_airport_depots_city,
 
		lengthof(_airport_depots_city),
 
		6, 6
 
	);
 

	
 
	// metropolitan airport
 
	MetropolitanAirport = malloc(sizeof(AirportFTAClass));
 

	
 
	AirportFTAClass_Constructor(
 
		MetropolitanAirport,
 
		_airport_terminal_metropolitan,
 
		NULL,
 
		20,
 
		ALL,
 
		_airport_fta_metropolitan,
 
		_airport_depots_metropolitan,
 
		lengthof(_airport_depots_metropolitan),
 
		6, 6
 
	);
 

	
 
	// international airport
 
	InternationalAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
 

	
 
	AirportFTAClass_Constructor(
 
		InternationalAirport,
 
		_airport_terminal_international,
 
		_airport_helipad_international,
 
		37,
 
		ALL,
 
		_airport_fta_international,
 
		_airport_depots_international,
 
		lengthof(_airport_depots_international),
 
		7, 7
 
	);
 

	
 
	// intercontintental airport
 
	IntercontinentalAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
 

	
 
	AirportFTAClass_Constructor(
 
		IntercontinentalAirport,
 
		_airport_terminal_intercontinental,
 
		_airport_helipad_intercontinental,
 
		43,
 
		ALL,
 
		_airport_fta_intercontinental,
 
		_airport_depots_intercontinental,
 
		lengthof(_airport_depots_intercontinental),
 
		9,11
 
	);
 

	
 
	// heliport, oilrig
 
	Heliport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
 

	
 
	AirportFTAClass_Constructor(
 
		Heliport,
 
		NULL,
 
		_airport_helipad_heliport_oilrig,
 
		7,
 
		HELICOPTERS_ONLY,
 
		_airport_fta_heliport_oilrig,
 
		NULL,
 
		0,
 
		1, 1
 
	);
 

	
 
	Oilrig = Heliport;  // exactly the same structure for heliport/oilrig, so share state machine
 

	
 
	// commuter airport
 
	CommuterAirport = malloc(sizeof(AirportFTAClass));
 

	
 
	AirportFTAClass_Constructor(
 
		CommuterAirport,
 
		_airport_terminal_commuter,
 
		_airport_helipad_commuter,
 
		22,
 
		ALL,
 
		_airport_fta_commuter,
 
		_airport_depots_commuter,
 
		lengthof(_airport_depots_commuter),
 
		5,4
 
	);
 

	
 
	// helidepot airport
 
	HeliDepot = malloc(sizeof(AirportFTAClass));
 

	
 
	AirportFTAClass_Constructor(
 
		HeliDepot,
 
		NULL,
 
		_airport_helipad_helidepot,
 
		4,
 
		HELICOPTERS_ONLY,
 
		_airport_fta_helidepot,
 
		_airport_depots_helidepot,
 
		lengthof(_airport_depots_helidepot),
 
		2,2
 
	);
 

	
 
	// helistation airport
 
	HeliStation = malloc(sizeof(AirportFTAClass));
 

	
 
	AirportFTAClass_Constructor(
 
		HeliStation,
 
		NULL,
 
		_airport_helipad_helistation,
 
		25,
 
		HELICOPTERS_ONLY,
 
		_airport_fta_helistation,
 
		_airport_depots_helistation,
 
		lengthof(_airport_depots_helistation),
 
		4,2
 
	);
 

	
 
}
 

	
 
void UnInitializeAirports(void)
 
{
 
	AirportFTAClass_Destructor(CountryAirport);
 
	AirportFTAClass_Destructor(CityAirport);
 
	AirportFTAClass_Destructor(Heliport);
 
	AirportFTAClass_Destructor(MetropolitanAirport);
 
	AirportFTAClass_Destructor(InternationalAirport);
 
	AirportFTAClass_Destructor(CommuterAirport);
 
	AirportFTAClass_Destructor(HeliDepot);
 
	AirportFTAClass_Destructor(IntercontinentalAirport);
 
	AirportFTAClass_Destructor(HeliStation);
 
}
 

	
 
static void AirportFTAClass_Constructor(AirportFTAClass *apc,
 
	const byte *terminals, const byte *helipads,
 
	const byte entry_point, const byte acc_planes,
 
	const AirportFTAbuildup *apFA,
 
	const TileIndexDiffC *depots, const byte nof_depots,
 
	uint size_x, uint size_y
 
)
 
{
 
	byte nofterminals, nofhelipads;
 
	byte nofterminalgroups, nofhelipadgroups;
 

	
 
	apc->size_x = size_x;
 
	apc->size_y = size_y;
 

	
 
	/* Set up the terminal and helipad count for an airport.
 
	 * TODO: If there are more than 10 terminals or 4 helipads, internal variables
 
	 * need to be changed, so don't allow that for now */
 
	nofterminals = AirportGetTerminalCount(terminals, &nofterminalgroups);
 
	if (nofterminals > MAX_TERMINALS) {
 
		DEBUG(misc, 0, "[Ap] only a maximum of %d terminals are supported (requested %d)", MAX_TERMINALS, nofterminals);
 
		assert(nofterminals <= MAX_TERMINALS);
 
	}
 
	apc->terminals = terminals;
 

	
 
	nofhelipads = AirportGetTerminalCount(helipads, &nofhelipadgroups);
 
	if (nofhelipads > MAX_HELIPADS) {
 
		DEBUG(misc, 0, "[Ap] only a maximum of %d helipads are supported (requested %d)", MAX_HELIPADS, nofhelipads);
 
		assert(nofhelipads <= MAX_HELIPADS);
 
	}
 
	apc->helipads = helipads;
 

	
 
	/* Get the number of elements from the source table. We also double check this
 
	 * with the entry point which must be within bounds and use this information
 
	 * later on to build and validate the state machine */
 
	apc->nofelements = AirportGetNofElements(apFA);
 
	if (entry_point >= apc->nofelements) {
 
		DEBUG(misc, 0, "[Ap] entry (%d) must be within the airport (maximum %d)", entry_point, apc->nofelements);
 
		assert(entry_point < apc->nofelements);
 
	}
 

	
 
	apc->acc_planes     = acc_planes;
 
	apc->entry_point    = entry_point;
 
	apc->airport_depots = depots;
 
	apc->nof_depots     = nof_depots;
 

	
 
	/* Build the state machine itself */
 
	AirportBuildAutomata(apc, apFA);
 
	DEBUG(misc, 2, "[Ap] #count %3d; #term %2d (%dgrp); #helipad %2d (%dgrp); entry %3d",
 
		apc->nofelements, nofterminals, nofterminalgroups, nofhelipads, nofhelipadgroups, apc->entry_point);
 

	
 
	/* Test if everything went allright. This is only a rude static test checking
 
	 * the symantic correctness. By no means does passing the test mean that the
 
	 * airport is working correctly or will not deadlock for example */
 
	{ byte ret = AirportTestFTA(apc);
 
		if (ret != MAX_ELEMENTS) DEBUG(misc, 0, "[Ap] problem with element: %d", ret - 1);
 
		assert(ret == MAX_ELEMENTS);
 
	}
 

	
 
#ifdef DEBUG_AIRPORT
 
	AirportPrintOut(apc, DEBUG_AIRPORT);
 
#endif
 
}
 

	
 
static void AirportFTAClass_Destructor(AirportFTAClass *apc)
 
{
 
	int i;
 
	AirportFTA *current, *next;
 

	
 
	for (i = 0; i < apc->nofelements; i++) {
 
		current = apc->layout[i].next;
 
		while (current != NULL) {
 
			next = current->next;
 
			free(current);
 
			current = next;
 
		};
 
	}
 
	free(apc->layout);
 
	free(apc);
 
}
 

	
 
/** Get the number of elements of a source Airport state automata
 
 * Since it is actually just a big array of AirportFTA types, we only
 
 * know one element from the other by differing 'position' identifiers */
 
static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA)
 
{
 
	int i;
 
	uint16 nofelements = 0;
 
	int temp = apFA[0].position;
 

	
 
	for (i = 0; i < MAX_ELEMENTS; i++) {
 
		if (temp != apFA[i].position) {
 
			nofelements++;
 
			temp = apFA[i].position;
 
		}
 
		if (apFA[i].position == MAX_ELEMENTS) break;
 
	}
 
	return nofelements;
 
}
 

	
 
/* We calculate the terminal/helipod count based on the data passed to us
 
 * This data (terminals) contains an index as a first element as to how many
 
 * groups there are, and then the number of terminals for each group */
 
static byte AirportGetTerminalCount(const byte *terminals, byte *groups)
 
{
 
	byte i;
 
	byte nof_terminals = 0;
 
	*groups = 0;
 

	
 
	if (terminals != NULL) {
 
		i = terminals[0];
 
		*groups = i;
 
		while (i-- > 0) {
 
			terminals++;
 
			assert(*terminals != 0); // no empty groups please
 
			nof_terminals += *terminals;
 
		}
 
	}
 
	return nof_terminals;
 
}
 

	
 
static void AirportBuildAutomata(AirportFTAClass *apc, const AirportFTAbuildup *apFA)
 
{
 
	AirportFTA *current;
 
	AirportFTA *FAutomata = malloc(sizeof(AirportFTA) * apc->nofelements);
 
	uint16 internalcounter = 0;
 
	uint16 i;
 

	
 
	apc->layout = FAutomata;
 
	for (i = 0; i < apc->nofelements; i++) {
 
		current = &apc->layout[i];
 
		current->position      = apFA[internalcounter].position;
 
		current->heading       = apFA[internalcounter].heading;
 
		current->block         = apFA[internalcounter].block;
 
		current->next_position = apFA[internalcounter].next;
 

	
 
		// outgoing nodes from the same position, create linked list
 
		while (current->position == apFA[internalcounter + 1].position) {
 
			AirportFTA *newNode = malloc(sizeof(AirportFTA));
 

	
 
			newNode->position      = apFA[internalcounter + 1].position;
 
			newNode->heading       = apFA[internalcounter + 1].heading;
 
			newNode->block         = apFA[internalcounter + 1].block;
 
			newNode->next_position = apFA[internalcounter + 1].next;
 
			// create link
 
			current->next = newNode;
 
			current = current->next;
 
			internalcounter++;
 
		} // while
 
		current->next = NULL;
 
		internalcounter++;
 
	}
 
}
 

	
 
static byte AirportTestFTA(const AirportFTAClass *apc)
 
{
 
	byte position, i, next_position;
 
	AirportFTA *current, *first;
 
	next_position = 0;
 

	
 
	for (i = 0; i < apc->nofelements; i++) {
 
		position = apc->layout[i].position;
 
		if (position != next_position) return i;
 
		current = first = &apc->layout[i];
 

	
 
		for (; current != NULL; current = current->next) {
 
			/* A heading must always be valid. The only exceptions are
 
			 * - multiple choices as start, identified by a special value of 255
 
			 * - terminal group which is identified by a special value of 255 */
 
			if (current->heading > MAX_HEADINGS) {
 
				if (current->heading != 255) return i;
 
				if (current == first && current->next == NULL) return i;
 
				if (current != first && current->next_position > apc->terminals[0]) return i;
 
			}
 

	
 
			/* If there is only one choice, it must be at the end */
 
			if (current->heading == 0 && current->next != NULL) return i;
 
			/* Obviously the elements of the linked list must have the same identifier */
 
			if (position != current->position) return i;
 
			/* A next position must be within bounds */
 
			if (current->next_position >= apc->nofelements) return i;
 
		}
 
		next_position++;
 
	}
 
	return MAX_ELEMENTS;
 
}
 

	
 
#ifdef DEBUG_AIRPORT
 
static const char* const _airport_heading_strings[] = {
 
	"TO_ALL",
 
	"HANGAR",
 
	"TERM1",
 
	"TERM2",
 
	"TERM3",
 
	"TERM4",
 
	"TERM5",
 
	"TERM6",
 
	"HELIPAD1",
 
	"HELIPAD2",
 
	"TAKEOFF",
 
	"STARTTAKEOFF",
 
	"ENDTAKEOFF",
 
	"HELITAKEOFF",
 
	"FLYING",
 
	"LANDING",
 
	"ENDLANDING",
 
	"HELILANDING",
 
	"HELIENDLANDING",
 
	"TERM7",
 
	"TERM8",
 
	"HELIPAD3",
 
	"HELIPAD4",
 
	"DUMMY" // extra heading for 255
 
};
 

	
 
static uint AirportBlockToString(uint32 block)
 
{
 
	uint i = 0;
 
	if (block & 0xffff0000) { block >>= 16; i += 16; }
 
	if (block & 0x0000ff00) { block >>=  8; i +=  8; }
 
	if (block & 0x000000f0) { block >>=  4; i +=  4; }
 
	if (block & 0x0000000c) { block >>=  2; i +=  2; }
 
	if (block & 0x00000002) { i += 1; }
 
	return i;
 
}
 

	
 
static void AirportPrintOut(const AirportFTAClass *apc, bool full_report)
 
{
 
	uint16 i;
 

	
 
	if (!full_report) printf("(P = Current Position; NP = Next Position)\n");
 

	
 
	for (i = 0; i < apc->nofelements; i++) {
 
		AirportFTA *current = &apc->layout[i];
 

	
 
		for (; current != NULL; current = current->next) {
 
			if (full_report) {
 
				byte heading = (current->heading == 255) ? MAX_HEADINGS + 1 : current->heading;
 
				printf("\tPos:%2d NPos:%2d Heading:%15s Block:%2d\n", current->position,
 
					    current->next_position, _airport_heading_strings[heading],
 
							AirportBlockToString(current->block));
 
			} else {
 
				printf("P:%2d NP:%2d", current->position, current->next_position);
 
			}
 
		}
 
		printf("\n");
 
	}
 
}
 
#endif
 

	
 
const AirportFTAClass *GetAirport(const byte airport_type)
 
{
 
	//FIXME -- AircraftNextAirportPos_and_Order -> Needs something nicer, don't like this code
 
	// needs constant change if more airports are added
 
	switch (airport_type) {
 
		default:               NOT_REACHED();
 
		case AT_SMALL:         return CountryAirport;
 
		case AT_LARGE:         return CityAirport;
 
		case AT_METROPOLITAN:  return MetropolitanAirport;
 
		case AT_HELIPORT:      return Heliport;
 
		case AT_OILRIG:        return Oilrig;
 
		case AT_INTERNATIONAL: return InternationalAirport;
 
		case AT_COMMUTER:      return CommuterAirport;
 
		case AT_HELIDEPOT:     return HeliDepot;
 
		case AT_INTERCON:      return IntercontinentalAirport;
 
		case AT_HELISTATION:   return HeliStation;
 
	}
 
}
 

	
 
const AirportMovingData *GetAirportMovingData(byte airport_type, byte position)
 
{
 
	assert(airport_type < lengthof(_airport_moving_datas));
 
	assert(position < GetAirport(airport_type)->nofelements);
 
	return &_airport_moving_datas[airport_type][position];
 
}
 

	
 
uint32 GetValidAirports(void)
 
{
 
	uint32 bytemask = _avail_aircraft; /// sets the first 3 bytes, 0 - 2, @see AdjustAvailAircraft()
 

	
 
	if (_cur_year >= 1980) SETBIT(bytemask, 3); // metropolitan airport
 
	if (_cur_year >= 1990) SETBIT(bytemask, 4); // international airport
 
	if (_cur_year >= 1983) SETBIT(bytemask, 5); // commuter airport
 
	if (_cur_year >= 1976) SETBIT(bytemask, 6); // helidepot
 
	if (_cur_year >= 2002) SETBIT(bytemask, 7); // intercontinental airport
 
	if (_cur_year >= 1980) SETBIT(bytemask, 8); // helistation
 
	return bytemask;
 
}
src/airport_gui.c
Show inline comments
 
deleted file
src/airport_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "sound.h"
 
#include "command.h"
 
#include "vehicle.h"
 
#include "station.h"
 
#include "airport.h"
 
#include "depot.h"
 

	
 
static byte _selected_airport_type;
 

	
 
static void ShowBuildAirportPicker(void);
 

	
 

	
 
void CcBuildAirport(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_1F_SPLAT, tile);
 
		ResetObjectToPlace();
 
	}
 
}
 

	
 
static void PlaceAirport(TileIndex tile)
 
{
 
	DoCommandP(tile, _selected_airport_type, 0, CcBuildAirport, CMD_BUILD_AIRPORT | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_A001_CAN_T_BUILD_AIRPORT_HERE));
 
}
 

	
 
static void PlaceAir_DemolishArea(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, 4);
 
}
 

	
 

	
 
enum {
 
	ATW_AIRPORT  = 3,
 
	ATW_DEMOLISH = 4
 
};
 

	
 

	
 
static void BuildAirClick_Airport(Window *w)
 
{
 
	if (HandlePlacePushButton(w, ATW_AIRPORT, SPR_CURSOR_AIRPORT, 1, PlaceAirport)) ShowBuildAirportPicker();
 
}
 

	
 
static void BuildAirClick_Demolish(Window *w)
 
{
 
	HandlePlacePushButton(w, ATW_DEMOLISH, ANIMCURSOR_DEMOLISH, 1, PlaceAir_DemolishArea);
 
}
 

	
 
static void BuildAirClick_Landscaping(Window *w)
 
{
 
	ShowTerraformToolbar();
 
}
 

	
 
typedef void OnButtonClick(Window *w);
 
static OnButtonClick * const _build_air_button_proc[] = {
 
	BuildAirClick_Airport,
 
	BuildAirClick_Demolish,
 
	BuildAirClick_Landscaping,
 
};
 

	
 
static void BuildAirToolbWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		if (e->we.click.widget - 3 >= 0)
 
			_build_air_button_proc[e->we.click.widget - 3](w);
 
		break;
 

	
 
	case WE_KEYPRESS: {
 
		switch (e->we.keypress.keycode) {
 
			case '1': BuildAirClick_Airport(w); break;
 
			case '2': BuildAirClick_Demolish(w); break;
 
			case 'l': BuildAirClick_Landscaping(w); break;
 
			default: return;
 
		}
 
	} break;
 

	
 
	case WE_PLACE_OBJ:
 
		_place_proc(e->we.place.tile);
 
		break;
 

	
 
	case WE_PLACE_DRAG:
 
		VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata);
 
		break;
 

	
 
	case WE_PLACE_MOUSEUP:
 
		if (e->we.place.pt.x != -1) {
 
			DoCommandP(e->we.place.tile, e->we.place.starttile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA));
 
		}
 
		break;
 

	
 
	case WE_ABORT_PLACE_OBJ:
 
		RaiseWindowButtons(w);
 

	
 
		w = FindWindowById(WC_BUILD_STATION, 0);
 
		if (w != 0)
 
			WP(w,def_d).close = true;
 
		break;
 

	
 
	case WE_DESTROY:
 
		if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0);
 
		break;
 
	}
 
}
 

	
 
static const Widget _air_toolbar_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,    73,     0,    13, STR_A000_AIRPORTS,   STR_018C_WINDOW_TITLE_DRAG_THIS },
 
{  WWT_STICKYBOX,   RESIZE_NONE,     7,    74,    85,     0,    13, 0x0,                 STR_STICKY_BUTTON },
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,     0,    41,    14,    35, SPR_IMG_AIRPORT,     STR_A01E_BUILD_AIRPORT },
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    42,    63,    14,    35, SPR_IMG_DYNAMITE,    STR_018D_DEMOLISH_BUILDINGS_ETC },
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    64,    85,    14,    35, SPR_IMG_LANDSCAPING, STR_LANDSCAPING_TOOLBAR_TIP },
 
{   WIDGETS_END},
 
};
 

	
 

	
 
static const WindowDesc _air_toolbar_desc = {
 
	WDP_ALIGN_TBR, 22, 86, 36,
 
	WC_BUILD_TOOLBAR, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
 
	_air_toolbar_widgets,
 
	BuildAirToolbWndProc
 
};
 

	
 
void ShowBuildAirToolbar(void)
 
{
 
	if (!IsValidPlayer(_current_player)) return;
 

	
 
	DeleteWindowById(WC_BUILD_TOOLBAR, 0);
 
	AllocateWindowDescFront(&_air_toolbar_desc, 0);
 
	if (_patches.link_terraform_toolbar) ShowTerraformToolbar();
 
}
 

	
 
static void BuildAirportPickerWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE:
 
		SetWindowWidgetLoweredState(w, 16, !_station_show_coverage);
 
		SetWindowWidgetLoweredState(w, 17, _station_show_coverage);
 
		LowerWindowWidget(w, _selected_airport_type + 7);
 
		break;
 

	
 
	case WE_PAINT: {
 
		int i; // airport enabling loop
 
		int rad = 4; // default catchment radious
 
		uint32 avail_airports;
 
		const AirportFTAClass *airport;
 

	
 
		if (WP(w,def_d).close) return;
 

	
 
		avail_airports = GetValidAirports();
 

	
 
		RaiseWindowWidget(w, _selected_airport_type + 7);
 
		if (!HASBIT(avail_airports, 0) && _selected_airport_type == AT_SMALL) _selected_airport_type = AT_LARGE;
 
		if (!HASBIT(avail_airports, 1) && _selected_airport_type == AT_LARGE) _selected_airport_type = AT_SMALL;
 
		LowerWindowWidget(w, _selected_airport_type + 7);
 

	
 
		/* 'Country Airport' starts at widget 7, and if its bit is set, it is
 
		 * available, so take its opposite value to set the disabled state.
 
		 * There are 9 buildable airports
 
		 * XXX TODO : all airports should be held in arrays, with all relevant data.
 
		 * This should be part of newgrf-airports, i suppose
 
		 */
 
		for (i = 0; i < 9; i++) SetWindowWidgetDisabledState(w, i + 7, !HASBIT(avail_airports, i));
 

	
 
		// select default the coverage area to 'Off' (16)
 
		airport = GetAirport(_selected_airport_type);
 
		SetTileSelectSize(airport->size_x, airport->size_y);
 

	
 
		if (_patches.modified_catchment) {
 
			switch (_selected_airport_type) {
 
				case AT_OILRIG:        rad = CA_AIR_OILPAD;   break;
 
				case AT_HELIPORT:      rad = CA_AIR_HELIPORT; break;
 
				case AT_SMALL:         rad = CA_AIR_SMALL;    break;
 
				case AT_LARGE:         rad = CA_AIR_LARGE;    break;
 
				case AT_METROPOLITAN:  rad = CA_AIR_METRO;    break;
 
				case AT_INTERNATIONAL: rad = CA_AIR_INTER;    break;
 
				case AT_COMMUTER:      rad = CA_AIR_COMMUTER; break;
 
				case AT_HELIDEPOT:     rad = CA_AIR_HELIDEPOT; break;
 
				case AT_INTERCON:      rad = CA_AIR_INTERCON; break;
 
				case AT_HELISTATION:   rad = CA_AIR_HELISTATION; break;
 
			}
 
		}
 

	
 
		if (_station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
 

	
 
		DrawWindowWidgets(w);
 
		// strings such as 'Size' and 'Coverage Area'
 
		// 'Coverage Area'
 
		DrawStationCoverageAreaText(2, 206, (uint)-1, rad);
 
		break;
 
	}
 

	
 
	case WE_CLICK: {
 
		switch (e->we.click.widget) {
 
		case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
 
			RaiseWindowWidget(w, _selected_airport_type + 7);
 
			_selected_airport_type = e->we.click.widget - 7;
 
			LowerWindowWidget(w, _selected_airport_type + 7);
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 
		case 16: case 17:
 
			_station_show_coverage = e->we.click.widget - 16;
 
			SetWindowWidgetLoweredState(w, 16, !_station_show_coverage);
 
			SetWindowWidgetLoweredState(w, 17, _station_show_coverage);
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_MOUSELOOP: {
 
		if (WP(w,def_d).close) {
 
			DeleteWindow(w);
 
			return;
 
		}
 

	
 
		CheckRedrawStationCoverage(w);
 
	} break;
 

	
 
	case WE_DESTROY:
 
		if (!WP(w,def_d).close) ResetObjectToPlace();
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_airport_picker_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   147,     0,    13, STR_3001_AIRPORT_SELECTION,       STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   147,    14,    52, 0x0,                              STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   147,    53,    89, 0x0,                              STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   147,    90,   127, 0x0,                              STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   147,   128,   177, 0x0,                              STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   147,   178,   239, 0x0,                              STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   145,    27,    38, STR_SMALL_AIRPORT,                STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   145,    65,    76, STR_CITY_AIRPORT,                 STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   145,   141,   152, STR_HELIPORT,                     STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   145,    77,    88, STR_METRO_AIRPORT ,               STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   145,   103,   114, STR_INTERNATIONAL_AIRPORT,        STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   145,    39,    50, STR_COMMUTER_AIRPORT,             STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   145,   165,   176, STR_HELIDEPOT,                    STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   145,   115,   126, STR_INTERCONTINENTAL_AIRPORT,     STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   145,   153,   164, STR_HELISTATION,                  STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    14,    73,   191,   202, STR_02DB_OFF,                     STR_3065_DON_T_HIGHLIGHT_COVERAGE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    74,   133,   191,   202, STR_02DA_ON,                      STR_3064_HIGHLIGHT_COVERAGE_AREA},
 
{      WWT_LABEL,   RESIZE_NONE,     7,     0,   147,    14,    27, STR_SMALL_AIRPORTS,               STR_NULL},
 
{      WWT_LABEL,   RESIZE_NONE,     7,     0,   147,    52,    65, STR_LARGE_AIRPORTS,               STR_NULL},
 
{      WWT_LABEL,   RESIZE_NONE,     7,     0,   147,    90,   103, STR_HUB_AIRPORTS,                 STR_NULL},
 
{      WWT_LABEL,   RESIZE_NONE,     7,     0,   147,   128,   141, STR_HELIPORTS,                    STR_NULL},
 
{      WWT_LABEL,   RESIZE_NONE,     7,     0,   147,   178,   191, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_airport_desc = {
 
	WDP_AUTO, WDP_AUTO, 148, 240,
 
	WC_BUILD_STATION, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_airport_picker_widgets,
 
	BuildAirportPickerWndProc
 
};
 

	
 
static void ShowBuildAirportPicker(void)
 
{
 
	AllocateWindowDesc(&_build_airport_desc);
 
}
 

	
 
void InitializeAirportGui(void)
 
{
 
	_selected_airport_type = AT_SMALL;
 
}
src/aystar.c
Show inline comments
 
deleted file
src/aystar.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/*
 
 * This file has the core function for AyStar
 
 *  AyStar is a fast pathfinding routine and is used for things like
 
 *  AI_pathfinding and Train_pathfinding.
 
 *  For more information about AyStar (A* Algorithm), you can look at
 
 *    http://en.wikipedia.org/wiki/A-star_search_algorithm
 
 */
 

	
 
/*
 
 * Friendly reminder:
 
 *  Call (AyStar).free() when you are done with Aystar. It reserves a lot of memory
 
 *  And when not free'd, it can cause system-crashes.
 
 * Also remember that when you stop an algorithm before it is finished, your
 
 * should call clear() yourself!
 
 */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "aystar.h"
 

	
 
int _aystar_stats_open_size;
 
int _aystar_stats_closed_size;
 

	
 
// This looks in the Hash if a node exists in ClosedList
 
//  If so, it returns the PathNode, else NULL
 
static PathNode* AyStarMain_ClosedList_IsInList(AyStar *aystar, const AyStarNode *node)
 
{
 
	return (PathNode*)Hash_Get(&aystar->ClosedListHash, node->tile, node->direction);
 
}
 

	
 
// This adds a node to the ClosedList
 
//  It makes a copy of the data
 
static void AyStarMain_ClosedList_Add(AyStar *aystar, const PathNode *node)
 
{
 
	// Add a node to the ClosedList
 
	PathNode *new_node = malloc(sizeof(*new_node));
 
	*new_node = *node;
 
	Hash_Set(&aystar->ClosedListHash, node->node.tile, node->node.direction, new_node);
 
}
 

	
 
// Checks if a node is in the OpenList
 
//   If so, it returns the OpenListNode, else NULL
 
static OpenListNode *AyStarMain_OpenList_IsInList(AyStar *aystar, const AyStarNode *node)
 
{
 
	return (OpenListNode*)Hash_Get(&aystar->OpenListHash, node->tile, node->direction);
 
}
 

	
 
// Gets the best node from OpenList
 
//  returns the best node, or NULL of none is found
 
// Also it deletes the node from the OpenList
 
static OpenListNode *AyStarMain_OpenList_Pop(AyStar *aystar)
 
{
 
	// Return the item the Queue returns.. the best next OpenList item.
 
	OpenListNode *res = (OpenListNode*)aystar->OpenListQueue.pop(&aystar->OpenListQueue);
 
	if (res != NULL) {
 
		Hash_Delete(&aystar->OpenListHash, res->path.node.tile, res->path.node.direction);
 
	}
 

	
 
	return res;
 
}
 

	
 
// Adds a node to the OpenList
 
//  It makes a copy of node, and puts the pointer of parent in the struct
 
static void AyStarMain_OpenList_Add(AyStar *aystar, PathNode *parent, const AyStarNode *node, int f, int g)
 
{
 
	// Add a new Node to the OpenList
 
	OpenListNode *new_node = malloc(sizeof(*new_node));
 
	new_node->g = g;
 
	new_node->path.parent = parent;
 
	new_node->path.node = *node;
 
	Hash_Set(&aystar->OpenListHash, node->tile, node->direction, new_node);
 

	
 
	// Add it to the queue
 
	aystar->OpenListQueue.push(&aystar->OpenListQueue, new_node, f);
 
}
 

	
 
/*
 
 * Checks one tile and calculate his f-value
 
 *  return values:
 
 * AYSTAR_DONE : indicates we are done
 
 */
 
int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
 
{
 
	int new_f, new_g, new_h;
 
	PathNode *closedlist_parent;
 
	OpenListNode *check;
 

	
 
	// Check the new node against the ClosedList
 
	if (AyStarMain_ClosedList_IsInList(aystar, current) != NULL) return AYSTAR_DONE;
 

	
 
	// Calculate the G-value for this node
 
	new_g = aystar->CalculateG(aystar, current, parent);
 
	// If the value was INVALID_NODE, we don't do anything with this node
 
	if (new_g == AYSTAR_INVALID_NODE) return AYSTAR_DONE;
 

	
 
	// There should not be given any other error-code..
 
	assert(new_g >= 0);
 
	// Add the parent g-value to the new g-value
 
	new_g += parent->g;
 
	if (aystar->max_path_cost != 0 && (uint)new_g > aystar->max_path_cost) return AYSTAR_DONE;
 

	
 
	// Calculate the h-value
 
	new_h = aystar->CalculateH(aystar, current, parent);
 
	// There should not be given any error-code..
 
	assert(new_h >= 0);
 

	
 
	// The f-value if g + h
 
	new_f = new_g + new_h;
 

	
 
	// Get the pointer to the parent in the ClosedList (the currentone is to a copy of the one in the OpenList)
 
	closedlist_parent = AyStarMain_ClosedList_IsInList(aystar, &parent->path.node);
 

	
 
	// Check if this item is already in the OpenList
 
	check = AyStarMain_OpenList_IsInList(aystar, current);
 
	if (check != NULL) {
 
		uint i;
 
		// Yes, check if this g value is lower..
 
		if (new_g > check->g) return AYSTAR_DONE;
 
		aystar->OpenListQueue.del(&aystar->OpenListQueue, check, 0);
 
		// It is lower, so change it to this item
 
		check->g = new_g;
 
		check->path.parent = closedlist_parent;
 
		/* Copy user data, will probably have changed */
 
		for (i = 0; i < lengthof(current->user_data); i++) {
 
			check->path.node.user_data[i] = current->user_data[i];
 
		}
 
		// Readd him in the OpenListQueue
 
		aystar->OpenListQueue.push(&aystar->OpenListQueue, check, new_f);
 
	} else {
 
		// A new node, add him to the OpenList
 
		AyStarMain_OpenList_Add(aystar, closedlist_parent, current, new_f, new_g);
 
	}
 

	
 
	return AYSTAR_DONE;
 
}
 

	
 
/*
 
 * This function is the core of AyStar. It handles one item and checks
 
 *  his neighbour items. If they are valid, they are added to be checked too.
 
 *  return values:
 
 *   AYSTAR_EMPTY_OPENLIST : indicates all items are tested, and no path
 
 *    has been found.
 
 *   AYSTAR_LIMIT_REACHED : Indicates that the max_nodes limit has been
 
 *    reached.
 
 *   AYSTAR_FOUND_END_NODE : indicates we found the end. Path_found now is true, and in path is the path found.
 
 *   AYSTAR_STILL_BUSY : indicates we have done this tile, did not found the path yet, and have items left to try.
 
 */
 
int AyStarMain_Loop(AyStar *aystar)
 
{
 
	int i, r;
 

	
 
	// Get the best node from OpenList
 
	OpenListNode *current = AyStarMain_OpenList_Pop(aystar);
 
	// If empty, drop an error
 
	if (current == NULL) return AYSTAR_EMPTY_OPENLIST;
 

	
 
	// Check for end node and if found, return that code
 
	if (aystar->EndNodeCheck(aystar, current) == AYSTAR_FOUND_END_NODE) {
 
		if (aystar->FoundEndNode != NULL)
 
			aystar->FoundEndNode(aystar, current);
 
		free(current);
 
		return AYSTAR_FOUND_END_NODE;
 
	}
 

	
 
	// Add the node to the ClosedList
 
	AyStarMain_ClosedList_Add(aystar, &current->path);
 

	
 
	// Load the neighbours
 
	aystar->GetNeighbours(aystar, current);
 

	
 
	// Go through all neighbours
 
	for (i = 0; i < aystar->num_neighbours; i++) {
 
		// Check and add them to the OpenList if needed
 
		r = aystar->checktile(aystar, &aystar->neighbours[i], current);
 
	}
 

	
 
	// Free the node
 
	free(current);
 

	
 
	if (aystar->max_search_nodes != 0 && Hash_Size(&aystar->ClosedListHash) >= aystar->max_search_nodes) {
 
		/* We've expanded enough nodes */
 
		return AYSTAR_LIMIT_REACHED;
 
	} else {
 
		// Return that we are still busy
 
		return AYSTAR_STILL_BUSY;
 
	}
 
}
 

	
 
/*
 
 * This function frees the memory it allocated
 
 */
 
void AyStarMain_Free(AyStar *aystar)
 
{
 
	aystar->OpenListQueue.free(&aystar->OpenListQueue, false);
 
	/* 2nd argument above is false, below is true, to free the values only
 
	 * once */
 
	delete_Hash(&aystar->OpenListHash, true);
 
	delete_Hash(&aystar->ClosedListHash, true);
 
#ifdef AYSTAR_DEBUG
 
	printf("[AyStar] Memory free'd\n");
 
#endif
 
}
 

	
 
/*
 
 * This function make the memory go back to zero
 
 *  This function should be called when you are using the same instance again.
 
 */
 
void AyStarMain_Clear(AyStar *aystar)
 
{
 
	// Clean the Queue, but not the elements within. That will be done by
 
	// the hash.
 
	aystar->OpenListQueue.clear(&aystar->OpenListQueue, false);
 
	// Clean the hashes
 
	clear_Hash(&aystar->OpenListHash, true);
 
	clear_Hash(&aystar->ClosedListHash, true);
 

	
 
#ifdef AYSTAR_DEBUG
 
	printf("[AyStar] Cleared AyStar\n");
 
#endif
 
}
 

	
 
/*
 
 * This is the function you call to run AyStar.
 
 *  return values:
 
 *   AYSTAR_FOUND_END_NODE : indicates we found an end node.
 
 *   AYSTAR_NO_PATH : indicates that there was no path found.
 
 *   AYSTAR_STILL_BUSY : indicates we have done some checked, that we did not found the path yet, and that we still have items left to try.
 
 * When the algorithm is done (when the return value is not AYSTAR_STILL_BUSY)
 
 * aystar->clear() is called. Note that when you stop the algorithm halfway,
 
 * you should still call clear() yourself!
 
 */
 
int AyStarMain_Main(AyStar *aystar) {
 
	int r, i = 0;
 
	// Loop through the OpenList
 
	//  Quit if result is no AYSTAR_STILL_BUSY or is more than loops_per_tick
 
	while ((r = aystar->loop(aystar)) == AYSTAR_STILL_BUSY && (aystar->loops_per_tick == 0 || ++i < aystar->loops_per_tick)) { }
 
#ifdef AYSTAR_DEBUG
 
	switch (r) {
 
		case AYSTAR_FOUND_END_NODE: printf("[AyStar] Found path!\n"); break;
 
		case AYSTAR_EMPTY_OPENLIST: printf("[AyStar] OpenList run dry, no path found\n"); break;
 
		case AYSTAR_LIMIT_REACHED:  printf("[AyStar] Exceeded search_nodes, no path found\n"); break;
 
		default: break;
 
	}
 
#endif
 
	if (r != AYSTAR_STILL_BUSY) {
 
		/* We're done, clean up */
 
		_aystar_stats_open_size = aystar->OpenListHash.size;
 
		_aystar_stats_closed_size = aystar->ClosedListHash.size;
 
		aystar->clear(aystar);
 
	}
 

	
 
	switch (r) {
 
		case AYSTAR_FOUND_END_NODE: return AYSTAR_FOUND_END_NODE;
 
		case AYSTAR_EMPTY_OPENLIST:
 
		case AYSTAR_LIMIT_REACHED:  return AYSTAR_NO_PATH;
 
		default:                    return AYSTAR_STILL_BUSY;
 
	}
 
}
 

	
 
/*
 
 * Adds a node from where to start an algorithm. Multiple nodes can be added
 
 * if wanted. You should make sure that clear() is called before adding nodes
 
 * if the AyStar has been used before (though the normal main loop calls
 
 * clear() automatically when the algorithm finishes
 
 * g is the cost for starting with this node.
 
 */
 
void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g)
 
{
 
#ifdef AYSTAR_DEBUG
 
	printf("[AyStar] Starting A* Algorithm from node (%d, %d, %d)\n",
 
		TileX(start_node->tile), TileY(start_node->tile), start_node->direction);
 
#endif
 
	AyStarMain_OpenList_Add(aystar, NULL, start_node, 0, g);
 
}
 

	
 
void init_AyStar(AyStar *aystar, Hash_HashProc hash, uint num_buckets)
 
{
 
	// Allocated the Hash for the OpenList and ClosedList
 
	init_Hash(&aystar->OpenListHash, hash, num_buckets);
 
	init_Hash(&aystar->ClosedListHash, hash, num_buckets);
 

	
 
	// Set up our sorting queue
 
	//  BinaryHeap allocates a block of 1024 nodes
 
	//  When thatone gets full it reserves an otherone, till this number
 
	//  That is why it can stay this high
 
	init_BinaryHeap(&aystar->OpenListQueue, 102400);
 

	
 
	aystar->addstart  = AyStarMain_AddStartNode;
 
	aystar->main      = AyStarMain_Main;
 
	aystar->loop      = AyStarMain_Loop;
 
	aystar->free      = AyStarMain_Free;
 
	aystar->clear     = AyStarMain_Clear;
 
	aystar->checktile = AyStarMain_CheckTile;
 
}
src/bmp.c
Show inline comments
 
deleted file
src/bmp.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "gfx.h"
 
#include "bmp.h"
 
#include "macros.h"
 

	
 
void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file) {
 
	buffer->pos      = -1;
 
	buffer->file     = file;
 
	buffer->read     = 0;
 
	buffer->real_pos = ftell(file);
 
}
 

	
 
static inline void AdvanceBuffer(BmpBuffer *buffer)
 
{
 
	buffer->read = (int)fread(buffer->data, 1, BMP_BUFFER_SIZE, buffer->file);
 
	buffer->pos  = 0;
 
}
 

	
 
static inline bool EndOfBuffer(BmpBuffer *buffer)
 
{
 
	if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
 
	return buffer->pos == buffer->read;
 
}
 

	
 
static inline byte ReadByte(BmpBuffer *buffer)
 
{
 
	if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
 
	buffer->real_pos++;
 
	return buffer->data[buffer->pos++];
 
}
 

	
 
static inline uint16 ReadWord(BmpBuffer *buffer)
 
{
 
	uint16 var = ReadByte(buffer);
 
	return var | (ReadByte(buffer) << 8);
 
}
 

	
 
static inline uint32 ReadDword(BmpBuffer *buffer)
 
{
 
	uint32 var = ReadWord(buffer);
 
	return var | (ReadWord(buffer) << 16);
 
}
 

	
 
static inline void SkipBytes(BmpBuffer *buffer, int bytes)
 
{
 
	int i;
 
	for (i = 0; i < bytes; i++) ReadByte(buffer);
 
}
 

	
 
static inline void SetStreamOffset(BmpBuffer *buffer, int offset)
 
{
 
	fseek(buffer->file, offset, SEEK_SET);
 
	buffer->pos = -1;
 
	buffer->real_pos = offset;
 
	AdvanceBuffer(buffer);
 
}
 

	
 
/**
 
 * Reads a 1 bpp uncompressed bitmap
 
 * The bitmap is converted to a 8 bpp bitmap
 
 */
 
static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
 
{
 
	uint x, y, i;
 
	byte pad = GB(4 - info->width / 8, 0, 2);
 
	byte *pixel_row;
 
	byte b;
 
	for (y = info->height; y > 0; y--) {
 
		x = 0;
 
		pixel_row = &data->bitmap[(y - 1) * info->width];
 
		while (x < info->width) {
 
			if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
 
			b = ReadByte(buffer);
 
			for (i = 8; i > 0; i--) {
 
				if (x < info->width) *pixel_row++ = GB(b, i - 1, 1);
 
				x++;
 
			}
 
		}
 
		/* Padding for 32 bit align */
 
		SkipBytes(buffer, pad);
 
	}
 
	return true;
 
}
 

	
 
/**
 
 * Reads a 4 bpp uncompressed bitmap
 
 * The bitmap is converted to a 8 bpp bitmap
 
 */
 
static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
 
{
 
	uint x, y;
 
	byte pad = GB(4 - info->width / 2, 0, 2);
 
	byte *pixel_row;
 
	byte b;
 
	for (y = info->height; y > 0; y--) {
 
		x = 0;
 
		pixel_row = &data->bitmap[(y - 1) * info->width];
 
		while (x < info->width) {
 
			if (EndOfBuffer(buffer)) return false;  // the file is shorter than expected
 
			b = ReadByte(buffer);
 
			*pixel_row++ = GB(b, 4, 4);
 
			x++;
 
			if (x < info->width) {
 
				*pixel_row++ = GB(b, 0, 4);
 
				x++;
 
			}
 
		}
 
		/* Padding for 32 bit align */
 
		SkipBytes(buffer, pad);
 
	}
 
	return true;
 
}
 

	
 
/**
 
 * Reads a 4-bit RLE compressed bitmap
 
 * The bitmap is converted to a 8 bpp bitmap
 
 */
 
static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
 
{
 
	uint i;
 
	uint x = 0;
 
	uint y = info->height - 1;
 
	byte n, c, b;
 
	byte *pixel = &data->bitmap[y * info->width];
 
	while (y != 0 || x < info->width) {
 
		if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
 
		n = ReadByte(buffer);
 
		c = ReadByte(buffer);
 
		if (n == 0) {
 
			switch (c) {
 
			case 0: // end of line
 
				x = 0;
 
				pixel = &data->bitmap[--y * info->width];
 
				break;
 
			case 1: // end of bitmap
 
				x = info->width;
 
				y = 0;
 
				pixel = NULL;
 
				break;
 
			case 2: // delta
 
				x += ReadByte(buffer);
 
				i = ReadByte(buffer);
 
				if (x >= info->width || (y == 0 && i > 0)) return false;
 
				y -= i;
 
				pixel = &data->bitmap[y * info->width + x];
 
				break;
 
			default: // uncompressed
 
				i = 0;
 
				while (i++ < c) {
 
					if (EndOfBuffer(buffer) || x >= info->width) return false;
 
					b = ReadByte(buffer);
 
					*pixel++ = GB(b, 4, 4);
 
					x++;
 
					if (x < info->width && i++ < c) {
 
						*pixel++ = GB(b, 0, 4);
 
						x++;
 
					}
 
				}
 
				/* Padding for 16 bit align */
 
				SkipBytes(buffer, ((c + 1) / 2) % 2);
 
				break;
 
			}
 
		} else {
 
			i = 0;
 
			while (i++ < n) {
 
				if (EndOfBuffer(buffer) || x >= info->width) return false;
 
				*pixel++ = GB(c, 4, 4);
 
				x++;
 
				if (x < info->width && i++ < n) {
 
					*pixel++ = GB(c, 0, 4);
 
					x++;
 
				}
 
			}
 
		}
 
	}
 
	return true;
 
}
 

	
 
/**
 
 * Reads a 8 bpp bitmap
 
 */
 
static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
 
{
 
	uint i;
 
	uint y;
 
	byte pad = GB(4 - info->width, 0, 2);
 
	byte *pixel;
 
	for (y = info->height; y > 0; y--) {
 
		if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
 
		pixel = &data->bitmap[(y - 1) * info->width];
 
		for (i = 0; i < info->width; i++) *pixel++ = ReadByte(buffer);
 
		/* Padding for 32 bit align */
 
		SkipBytes(buffer, pad);
 
	}
 
	return true;
 
}
 

	
 
/**
 
 * Reads a 8-bit RLE compressed bpp bitmap
 
 */
 
static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
 
{
 
	uint i;
 
	uint x = 0;
 
	uint y = info->height - 1;
 
	byte n, c;
 
	byte *pixel = &data->bitmap[y * info->width];
 
	while (y != 0 || x < info->width) {
 
		if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
 
		n = ReadByte(buffer);
 
		c = ReadByte(buffer);
 
		if (n == 0) {
 
			switch (c) {
 
			case 0: // end of line
 
				x = 0;
 
				pixel = &data->bitmap[--y * info->width];
 
				break;
 
			case 1: // end of bitmap
 
				x = info->width;
 
				y = 0;
 
				pixel = NULL;
 
				break;
 
			case 2: // delta
 
				x += ReadByte(buffer);
 
				i = ReadByte(buffer);
 
				if (x >= info->width || (y == 0 && i > 0)) return false;
 
				y -= i;
 
				pixel = &data->bitmap[y * info->width + x];
 
				break;
 
			default: // uncompressed
 
				if ((x += c) > info->width) return false;
 
				for (i = 0; i < c; i++) *pixel++ = ReadByte(buffer);
 
				/* Padding for 16 bit align */
 
				SkipBytes(buffer, c % 2);
 
				break;
 
			}
 
		} else {
 
			for (i = 0; i < n; i++) {
 
				if (x >= info->width) return false;
 
				*pixel++ = c;
 
				x++;
 
			}
 
		}
 
	}
 
	return true;
 
}
 

	
 
/**
 
 * Reads a 24 bpp uncompressed bitmap
 
 */
 
static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
 
{
 
	uint x, y;
 
	byte pad = GB(4 - info->width * 3, 0, 2);
 
	byte *pixel_row;
 
	for (y = info->height; y > 0; y--) {
 
		pixel_row = &data->bitmap[(y - 1) * info->width * 3];
 
		for (x = 0; x < info->width; x++) {
 
			if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
 
			*(pixel_row + 2) = ReadByte(buffer); // green
 
			*(pixel_row + 1) = ReadByte(buffer); // blue
 
			*pixel_row       = ReadByte(buffer); // red
 
			pixel_row += 3;
 
		}
 
		/* Padding for 32 bit align */
 
		SkipBytes(buffer, pad);
 
	}
 
	return true;
 
}
 

	
 
/*
 
 * Reads bitmap headers, and palette (if any)
 
 */
 
bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
 
{
 
	uint32 header_size;
 
	assert(info != NULL);
 

	
 
	/* Reading BMP header */
 
	if (ReadWord(buffer) != 0x4D42) return false; // signature should be 'BM'
 
	SkipBytes(buffer, 8); // skip file size and reserved
 
	info->offset = ReadDword(buffer);
 

	
 
	/* Reading info header */
 
	header_size = ReadDword(buffer);
 
	if (header_size < 12) return false; // info header should be at least 12 bytes long
 

	
 
	info->os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long
 

	
 
	if (info->os2_bmp) {
 
		info->width = ReadWord(buffer);
 
		info->height = ReadWord(buffer);
 
		header_size -= 8;
 
	} else {
 
		info->width = ReadDword(buffer);
 
		info->height = ReadDword(buffer);
 
		header_size -= 12;
 
	}
 

	
 
	if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane
 

	
 
	info->bpp = ReadWord(buffer);
 
	if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) {
 
		/* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */
 
		return false;
 
	}
 

	
 
	/* Reads compression method if available in info header*/
 
	if ((header_size -= 4) >= 4) {
 
		info->compression = ReadDword(buffer);
 
		header_size -= 4;
 
	}
 

	
 
	/* Only 4-bit and 8-bit rle compression is supported */
 
	if (info->compression > 2 || (info->compression > 0 && !(info->bpp == 4 || info->bpp == 8))) return false;
 

	
 
	if (info->bpp <= 8) {
 
		uint i;
 

	
 
		/* Reads number of colors if available in info header */
 
		if (header_size >= 16) {
 
			SkipBytes(buffer, 12);                  // skip image size and resolution
 
			info->palette_size = ReadDword(buffer); // number of colors in palette
 
			SkipBytes(buffer, header_size - 16);    // skip the end of info header
 
		}
 
		if (info->palette_size == 0) info->palette_size = 1 << info->bpp;
 

	
 
		data->palette = calloc(info->palette_size, sizeof(*(data->palette)));
 
		if (data->palette == NULL) return false;
 

	
 
		for (i = 0; i < info->palette_size; i++) {
 
			data->palette[i].b = ReadByte(buffer);
 
			data->palette[i].g = ReadByte(buffer);
 
			data->palette[i].r = ReadByte(buffer);
 
			if (!info->os2_bmp) SkipBytes(buffer, 1); // unused
 
		}
 
	}
 

	
 
	return buffer->real_pos <= info->offset;
 
}
 

	
 
/*
 
 * Reads the bitmap
 
 * 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
 
 */
 
bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
 
{
 
	assert(info != NULL && data != NULL);
 

	
 
	data->bitmap = calloc(info->width * info->height, ((info->bpp == 24) ? 3 : 1) * sizeof(byte));
 
	if (data->bitmap == NULL) return false;
 

	
 
	/* Load image */
 
	SetStreamOffset(buffer, info->offset);
 
	switch (info->compression) {
 
	case 0: // no compression
 
		switch (info->bpp) {
 
		case 1:  return BmpRead1(buffer, info, data);
 
		case 4:  return BmpRead4(buffer, info, data);
 
		case 8:  return BmpRead8(buffer, info, data);
 
		case 24: return BmpRead24(buffer, info, data);
 
		default: NOT_REACHED(); return false;
 
		}
 
	case 1:  return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression
 
	case 2:  return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression
 
	default: NOT_REACHED(); return false;
 
	}
 
}
 

	
 
void BmpDestroyData(BmpData *data)
 
{
 
	assert(data != NULL);
 
	free(data->palette);
 
	free(data->bitmap);
 
}
src/bridge_gui.c
Show inline comments
 
deleted file
src/bridge_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/** @file bridge_gui.c Graphical user interface for bridge construction*/
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "command.h"
 
#include "sound.h"
 
#include "variables.h"
 
#include "bridge.h"
 

	
 
static struct BridgeData {
 
	uint count;
 
	TileIndex start_tile;
 
	TileIndex end_tile;
 
	byte type;
 
	byte indexes[MAX_BRIDGES];
 
	int32 costs[MAX_BRIDGES];
 
} _bridgedata;
 

	
 
void CcBuildBridge(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) SndPlayTileFx(SND_27_BLACKSMITH_ANVIL, tile);
 
}
 

	
 
static void BuildBridge(Window *w, int i)
 
{
 
	DeleteWindow(w);
 
	DoCommandP(_bridgedata.end_tile, _bridgedata.start_tile,
 
		_bridgedata.indexes[i] | (_bridgedata.type << 8), CcBuildBridge,
 
		CMD_BUILD_BRIDGE | CMD_AUTO | CMD_MSG(STR_5015_CAN_T_BUILD_BRIDGE_HERE));
 
}
 

	
 
static void BuildBridgeWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		uint i;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		for (i = 0; i < 4 && i + w->vscroll.pos < _bridgedata.count; i++) {
 
			const Bridge *b = &_bridge[_bridgedata.indexes[i + w->vscroll.pos]];
 

	
 
			SetDParam(2, _bridgedata.costs[i + w->vscroll.pos]);
 
			SetDParam(1, b->speed);
 
			SetDParam(0, b->material);
 
			DrawSprite(b->sprite, 3, 15 + i * 22);
 

	
 
			DrawString(44, 15 + i * 22 , STR_500D, 0);
 
		}
 
	} break;
 

	
 
	case WE_KEYPRESS: {
 
		uint i = e->we.keypress.keycode - '1';
 
		if (i < 9 && i < _bridgedata.count) {
 
			e->we.keypress.cont = false;
 
			BuildBridge(w, i);
 
		}
 

	
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		if (e->we.click.widget == 2) {
 
			uint ind = ((int)e->we.click.pt.y - 14) / 22;
 
			if (ind < 4 && (ind += w->vscroll.pos) < _bridgedata.count)
 
				BuildBridge(w, ind);
 
		}
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_bridge_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                    STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   199,     0,    13, STR_100D_SELECT_RAIL_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_MATRIX,   RESIZE_NONE,     7,     0,   187,    14,   101, 0x401,                       STR_101F_BRIDGE_SELECTION_CLICK},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,     7,   188,   199,    14,   101, 0x0,                         STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_bridge_desc = {
 
	WDP_AUTO, WDP_AUTO, 200, 102,
 
	WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_bridge_widgets,
 
	BuildBridgeWndProc
 
};
 

	
 

	
 
static const Widget _build_road_bridge_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                    STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   199,     0,    13, STR_1803_SELECT_ROAD_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_MATRIX,   RESIZE_NONE,     7,     0,   187,    14,   101, 0x401,                       STR_101F_BRIDGE_SELECTION_CLICK},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,     7,   188,   199,    14,   101, 0x0,                         STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_road_bridge_desc = {
 
	WDP_AUTO, WDP_AUTO, 200, 102,
 
	WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_road_bridge_widgets,
 
	BuildBridgeWndProc
 
};
 

	
 

	
 
void ShowBuildBridgeWindow(TileIndex start, TileIndex end, byte bridge_type)
 
{
 
	uint j = 0;
 
	int32 ret;
 
	StringID errmsg;
 

	
 
	DeleteWindowById(WC_BUILD_BRIDGE, 0);
 

	
 
	_bridgedata.type = bridge_type;
 
	_bridgedata.start_tile = start;
 
	_bridgedata.end_tile = end;
 

	
 
	errmsg = INVALID_STRING_ID;
 

	
 
	// only query bridge building possibility once, result is the same for all bridges!
 
	// returns CMD_ERROR on failure, and price on success
 
	ret = DoCommand(end, start, (bridge_type << 8), DC_AUTO | DC_QUERY_COST, CMD_BUILD_BRIDGE);
 

	
 
	if (CmdFailed(ret)) {
 
		errmsg = _error_message;
 
	} else {
 
		// check which bridges can be built
 
		int bridge_len;         // length of the middle parts of the bridge
 
		int tot_bridgedata_len; // total length of bridge
 

	
 
		// get absolute bridge length
 
		bridge_len = GetBridgeLength(start, end);
 
		tot_bridgedata_len = bridge_len + 2;
 

	
 
		tot_bridgedata_len = CalcBridgeLenCostFactor(tot_bridgedata_len);
 

	
 
		for (bridge_type = 0; bridge_type != MAX_BRIDGES; bridge_type++) { // loop for all bridgetypes
 
			if (CheckBridge_Stuff(bridge_type, bridge_len)) {
 
				const Bridge *b = &_bridge[bridge_type];
 
				// bridge is accepted, add to list
 
				// add to terraforming & bulldozing costs the cost of the bridge itself (not computed with DC_QUERY_COST)
 
				_bridgedata.costs[j] = ret + (((int64)tot_bridgedata_len * _price.build_bridge * b->price) >> 8);
 
				_bridgedata.indexes[j] = bridge_type;
 
				j++;
 
			}
 
		}
 
	}
 

	
 
	_bridgedata.count = j;
 

	
 
	if (j != 0) {
 
		Window *w = AllocateWindowDesc((_bridgedata.type & 0x80) ? &_build_road_bridge_desc : &_build_bridge_desc);
 
		w->vscroll.cap = 4;
 
		w->vscroll.count = (byte)j;
 
	} else {
 
		ShowErrorMessage(errmsg, STR_5015_CAN_T_BUILD_BRIDGE_HERE, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE);
 
	}
 
}
src/bridge_map.c
Show inline comments
 
deleted file
src/bridge_map.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "variables.h"
 

	
 

	
 
TileIndex GetBridgeEnd(TileIndex tile, DiagDirection dir)
 
{
 
	TileIndexDiff delta = TileOffsByDiagDir(dir);
 

	
 
	dir = ReverseDiagDir(dir);
 
	do {
 
		tile += delta;
 
	} while (!IsBridgeTile(tile) || GetBridgeRampDirection(tile) != dir);
 

	
 
	return tile;
 
}
 

	
 

	
 
TileIndex GetNorthernBridgeEnd(TileIndex t)
 
{
 
	return GetBridgeEnd(t, ReverseDiagDir(AxisToDiagDir(GetBridgeAxis(t))));
 
}
 

	
 

	
 
TileIndex GetSouthernBridgeEnd(TileIndex t)
 
{
 
	return GetBridgeEnd(t, AxisToDiagDir(GetBridgeAxis(t)));
 
}
 

	
 

	
 
TileIndex GetOtherBridgeEnd(TileIndex tile)
 
{
 
	assert(IsBridgeTile(tile));
 
	return GetBridgeEnd(tile, GetBridgeRampDirection(tile));
 
}
 

	
 
uint GetBridgeHeight(TileIndex t)
 
{
 
	uint h;
 
	uint tileh = GetTileSlope(t, &h);
 
	uint f = GetBridgeFoundation(tileh, DiagDirToAxis(GetBridgeRampDirection(t)));
 

	
 
	// one height level extra if the ramp is on a flat foundation
 
	return
 
		h + TILE_HEIGHT +
 
		(IS_INT_INSIDE(f, 1, 15) ? TILE_HEIGHT : 0) +
 
		(IsSteepSlope(tileh) ? TILE_HEIGHT : 0);
 
}
src/build_vehicle_gui.c
Show inline comments
 
deleted file
src/build_vehicle_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "aircraft.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "vehicle.h"
 
#include "gfx.h"
 
#include "station.h"
 
#include "command.h"
 
#include "engine.h"
 
#include "player.h"
 
#include "depot.h"
 
#include "airport.h"
 
#include "vehicle_gui.h"
 
#include "newgrf_engine.h"
 
#include "date.h"
 
#include "strings.h"
 

	
 

	
 
enum BuildVehicleWidgets {
 
	BUILD_VEHICLE_WIDGET_CLOSEBOX = 0,
 
	BUILD_VEHICLE_WIDGET_CAPTION,
 
	BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING,
 
	BUILD_VEHICLE_WIDGET_SORT_TEXT,
 
	BUILD_VEHICLE_WIDGET_SORT_DROPDOWN,
 
	BUILD_VEHICLE_WIDGET_LIST,
 
	BUILD_VEHICLE_WIDGET_SCROLLBAR,
 
	BUILD_VEHICLE_WIDGET_PANEL,
 
	BUILD_VEHICLE_WIDGET_BUILD,
 
	BUILD_VEHICLE_WIDGET_RENAME,
 
	BUILD_VEHICLE_WIDGET_RESIZE,
 
};
 

	
 
static const Widget _build_vehicle_widgets[] = {
 
	{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW },
 
	{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   239,     0,    13, STR_A005_NEW_AIRCRAFT,   STR_018C_WINDOW_TITLE_DRAG_THIS },
 
	{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    80,    14,    25, STR_SORT_BY,             STR_SORT_ORDER_TIP},
 
	{      WWT_PANEL,   RESIZE_NONE,    14,    81,   227,    14,    25, 0x0,                     STR_SORT_CRITERIA_TIP},
 
	{    WWT_TEXTBTN,   RESIZE_NONE,    14,   228,   239,    14,    25, STR_0225,                STR_SORT_CRITERIA_TIP},
 
	{     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   227,    26,   121, 0x401,                   STR_A025_AIRCRAFT_SELECTION_LIST },
 
	{  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   228,   239,    26,   121, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST },
 
	{      WWT_PANEL,     RESIZE_TB,    14,     0,   239,   122,   213, 0x0,                     STR_NULL },
 

	
 
	{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   114,   214,   225, STR_A006_BUILD_AIRCRAFT, STR_A026_BUILD_THE_HIGHLIGHTED_AIRCRAFT },
 
	{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   115,   227,   214,   225, STR_A037_RENAME,         STR_A038_RENAME_AIRCRAFT_TYPE },
 
	{  WWT_RESIZEBOX,     RESIZE_TB,    14,   228,   239,   214,   225, 0x0,                     STR_RESIZE_BUTTON },
 
	{   WIDGETS_END},
 
};
 

	
 
static bool _internal_sort_order; // descending/ascending
 
static byte _last_sort_criteria = 0;
 
static bool _last_sort_order = false;
 

	
 
static int CDECL EngineNumberSorter(const void *a, const void *b)
 
{
 
	const EngineID va = *(const EngineID*)a;
 
	const EngineID vb = *(const EngineID*)b;
 
	int r = va - vb;
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL EngineIntroDateSorter(const void *a, const void *b)
 
{
 
	const int va = GetEngine(*(const EngineID*)a)->intro_date;
 
	const int vb = GetEngine(*(const EngineID*)b)->intro_date;
 
	const int r = va - vb;
 

	
 
	if (r == 0) {
 
		/* Use EngineID to sort instead since we want consistent sorting */
 
		return EngineNumberSorter(a, b);
 
	}
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL EngineNameSorter(const void *a, const void *b)
 
{
 
	static EngineID last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE };
 
	static char     last_name[2][64] = { "\0", "\0" };
 

	
 
	const EngineID va = *(const EngineID*)a;
 
	const EngineID vb = *(const EngineID*)b;
 
	int r;
 

	
 
	if (va != last_engine[0]) {
 
		last_engine[0] = va;
 
		GetString(last_name[0], GetCustomEngineName(va), lastof(last_name[0]));
 
	}
 

	
 
	if (vb != last_engine[1]) {
 
		last_engine[1] = vb;
 
		GetString(last_name[1], GetCustomEngineName(vb), lastof(last_name[1]));
 
	}
 

	
 
	r = strcmp(last_name[0], last_name[1]); // sort by name
 

	
 
	if (r == 0) {
 
		/* Use EngineID to sort instead since we want consistent sorting */
 
		return EngineNumberSorter(a, b);
 
	}
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL EngineReliabilitySorter(const void *a, const void *b)
 
{
 
	const int va = GetEngine(*(const EngineID*)a)->reliability;
 
	const int vb = GetEngine(*(const EngineID*)b)->reliability;
 
	const int r = va - vb;
 

	
 
	if (r == 0) {
 
		/* Use EngineID to sort instead since we want consistent sorting */
 
		return EngineNumberSorter(a, b);
 
	}
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
/* Aircraft sorting functions */
 

	
 
static int CDECL AircraftEngineCostSorter(const void *a, const void *b)
 
{
 
	const int va = AircraftVehInfo(*(const EngineID*)a)->base_cost;
 
	const int vb = AircraftVehInfo(*(const EngineID*)b)->base_cost;
 
	int r = va - vb;
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL AircraftEngineSpeedSorter(const void *a, const void *b)
 
{
 
	const int va = AircraftVehInfo(*(const EngineID*)a)->max_speed;
 
	const int vb = AircraftVehInfo(*(const EngineID*)b)->max_speed;
 
	const int r = va - vb;
 

	
 
	if (r == 0) {
 
		/* Use EngineID to sort instead since we want consistent sorting */
 
		return EngineNumberSorter(a, b);
 
	}
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL AircraftEngineRunningCostSorter(const void *a, const void *b)
 
{
 
	const int va = AircraftVehInfo(*(const EngineID*)a)->running_cost;
 
	const int vb = AircraftVehInfo(*(const EngineID*)b)->running_cost;
 
	const int r = va - vb;
 

	
 
	if (r == 0) {
 
		/* Use EngineID to sort instead since we want consistent sorting */
 
		return EngineNumberSorter(a, b);
 
	}
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL AircraftEngineCargoSorter(const void *a, const void *b)
 
{
 
	const int va = AircraftVehInfo(*(const EngineID*)a)->passenger_capacity;
 
	const int vb = AircraftVehInfo(*(const EngineID*)b)->passenger_capacity;
 
	const int r = va - vb;
 

	
 
	if (r == 0) {
 
		/* Use EngineID to sort instead since we want consistent sorting */
 
		return EngineNumberSorter(a, b);
 
	}
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static EngList_SortTypeFunction * const _aircraft_sorter[] = {
 
	&EngineNumberSorter,
 
	&AircraftEngineCostSorter,
 
	&AircraftEngineSpeedSorter,
 
	&EngineIntroDateSorter,
 
	&EngineNameSorter,
 
	&AircraftEngineRunningCostSorter,
 
	&EngineReliabilitySorter,
 
	&AircraftEngineCargoSorter,
 
};
 

	
 
static const StringID _aircraft_sort_listing[] = {
 
	STR_ENGINE_SORT_ENGINE_ID,
 
	STR_ENGINE_SORT_COST,
 
	STR_SORT_BY_MAX_SPEED,
 
	STR_ENGINE_SORT_INTRO_DATE,
 
	STR_SORT_BY_DROPDOWN_NAME,
 
	STR_ENGINE_SORT_RUNNING_COST,
 
	STR_SORT_BY_RELIABILITY,
 
	STR_ENGINE_SORT_CARGO_CAPACITY,
 
	INVALID_STRING_ID
 
};
 

	
 

	
 
/**
 
* Draw the purchase info details of an aircraft at a given location.
 
 * @param x,y location where to draw the info
 
 * @param engine_number the engine of which to draw the info of
 
 */
 
void DrawAircraftPurchaseInfo(int x, int y, uint w, EngineID engine_number)
 
{
 
	const AircraftVehicleInfo *avi = AircraftVehInfo(engine_number);
 
	const Engine *e = GetEngine(engine_number);
 
	CargoID cargo;
 
	YearMonthDay ymd;
 
	ConvertDateToYMD(e->intro_date, &ymd);
 

	
 
	/* Purchase cost - Max speed */
 
	SetDParam(0, avi->base_cost * (_price.aircraft_base>>3)>>5);
 
	SetDParam(1, avi->max_speed * 128 / 10);
 
	DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, 0);
 
	y += 10;
 

	
 
	/* Cargo capacity */
 
	cargo = FindFirstRefittableCargo(engine_number);
 
	if (cargo == CT_INVALID || cargo == CT_PASSENGERS) {
 
		SetDParam(0, avi->passenger_capacity);
 
		SetDParam(1, avi->mail_capacity);
 
		DrawString(x, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, 0);
 
	} else {
 
		/* Note, if the default capacity is selected by the refit capacity
 
		* callback, then the capacity shown is likely to be incorrect. */
 
		SetDParam(0, cargo);
 
		SetDParam(1, AircraftDefaultCargoCapacity(cargo, engine_number));
 
		SetDParam(2, STR_9842_REFITTABLE);
 
		DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, 0);
 
	}
 
	y += 10;
 

	
 
	/* Running cost */
 
	SetDParam(0, avi->running_cost * _price.aircraft_running >> 8);
 
	DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, 0);
 
	y += 10;
 

	
 
	/* Design date - Life length */
 
	SetDParam(0, ymd.year);
 
	SetDParam(1, e->lifelength);
 
	DrawString(x, y, STR_PURCHASE_INFO_DESIGNED_LIFE, 0);
 
	y += 10;
 

	
 
	/* Reliability */
 
	SetDParam(0, e->reliability * 100 >> 16);
 
	DrawString(x, y, STR_PURCHASE_INFO_RELIABILITY, 0);
 
	y += 10;
 

	
 
	/* Additional text from NewGRF */
 
	y += ShowAdditionalText(x, y, w, engine_number);
 
	y += ShowRefitOptionsList(x, y, w, engine_number);
 
}
 

	
 
void DrawAircraftImage(const Vehicle *v, int x, int y, VehicleID selection)
 
{
 
	PalSpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
 
	DrawSprite(GetAircraftImage(v, DIR_W) | pal, x + 25, y + 10);
 
	if (v->subtype == 0) {
 
		SpriteID rotor_sprite = GetCustomRotorSprite(v, true);
 
		if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
 
		DrawSprite(rotor_sprite, x + 25, y + 5);
 
	}
 
	if (v->index == selection) {
 
		DrawFrameRect(x - 1, y - 1, x + 58, y + 21, 0xF, FR_BORDERONLY);
 
	}
 
}
 

	
 
void CcBuildAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		const Vehicle *v = GetVehicle(_new_vehicle_id);
 

	
 
		if (v->tile == _backup_orders_tile) {
 
			_backup_orders_tile = 0;
 
			RestoreVehicleOrders(v, _backup_orders_data);
 
		}
 
		ShowAircraftViewWindow(v);
 
	}
 
}
 

	
 
static void GenerateBuildAircraftList(Window *w)
 
{
 
	EngineID eid, sel_id;
 
	buildvehicle_d *bv = &WP(w, buildvehicle_d);
 

	
 
	EngList_RemoveAll(&bv->eng_list);
 

	
 
	/* Make list of all available planes.
 
	 * Also check to see if the previously selected plane is still available,
 
	 * and if not, reset selection to INVALID_ENGINE. This could be the case
 
	 * when planes become obsolete and are removed */
 
	sel_id = INVALID_ENGINE;
 
	for (eid = AIRCRAFT_ENGINES_INDEX; eid < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES; eid++) {
 
		if (IsEngineBuildable(eid, VEH_Aircraft, _local_player)) {
 
			const AircraftVehicleInfo *avi = AircraftVehInfo(eid);
 
			switch (bv->filter.acc_planes) {
 
				case HELICOPTERS_ONLY:
 
					if (avi->subtype != 0) continue; // if not helicopter
 
					break;
 

	
 
				case AIRCRAFT_ONLY:
 
					if (avi->subtype == 0) continue; // if helicopter
 
					break;
 

	
 
				case ALL: break;
 
			}
 
			EngList_Add(&bv->eng_list, eid);
 

	
 
			if (eid == bv->sel_engine) sel_id = eid;
 
		}
 
	}
 

	
 
	bv->sel_engine = sel_id;
 
}
 

	
 
static void GenerateBuildList(Window *w)
 
{
 
	buildvehicle_d *bv = &WP(w, buildvehicle_d);
 

	
 
	switch (bv->vehicle_type) {
 
		case VEH_Aircraft:
 
			GenerateBuildAircraftList(w);
 
			_internal_sort_order = bv->descending_sort_order;
 
			EngList_Sort(&bv->eng_list, _aircraft_sorter[bv->sort_criteria]);
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
static void DrawBuildAircraftWindow(Window *w)
 
{
 
	const buildvehicle_d *bv = &WP(w, buildvehicle_d);
 

	
 
	SetWindowWidgetDisabledState(w, BUILD_VEHICLE_WIDGET_BUILD, w->window_number == 0);
 

	
 
	SetVScrollCount(w, EngList_Count(&bv->eng_list));
 
	DrawWindowWidgets(w);
 

	
 
	{
 
		int x = 2;
 
		int y = 27;
 
		EngineID selected_id = bv->sel_engine;
 
		EngineID eid = w->vscroll.pos;
 
		uint16 max = min(w->vscroll.pos + w->vscroll.cap, EngList_Count(&bv->eng_list));
 

	
 
		for (; eid < max; eid++) {
 
			const EngineID engine = bv->eng_list[eid];
 

	
 
			DrawString(x + 62, y + 7, GetCustomEngineName(engine), engine == selected_id ? 0xC : 0x10);
 
			DrawAircraftEngine(x + 29, y + 10, engine, GetEnginePalette(engine, _local_player));
 
			y += 24;
 
		}
 

	
 
		if (selected_id != INVALID_ENGINE) {
 
			const Widget *wi = &w->widget[BUILD_VEHICLE_WIDGET_PANEL];
 
			DrawAircraftPurchaseInfo(x, wi->top + 1, wi->right - wi->left - 2, selected_id);
 
		}
 
	}
 
	DrawString(85, 15, _aircraft_sort_listing[bv->sort_criteria], 0x10);
 
	DoDrawString(bv->descending_sort_order ? DOWNARROW : UPARROW, 69, 15, 0x10);
 
}
 

	
 
static void BuildAircraftClickEvent(Window *w, WindowEvent *e)
 
{
 
	buildvehicle_d *bv = &WP(w, buildvehicle_d);
 

	
 
	switch (e->we.click.widget) {
 
		case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING:
 
			bv->descending_sort_order ^= true;
 
			_last_sort_order = bv->descending_sort_order;
 
			GenerateBuildList(w);
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case BUILD_VEHICLE_WIDGET_LIST: {
 
			uint i = (e->we.click.pt.y - 26) / 24 + w->vscroll.pos;
 
			uint num_items = EngList_Count(&bv->eng_list);
 
			bv->sel_engine = (i < num_items) ? bv->eng_list[i] : INVALID_ENGINE;
 
			SetWindowDirty(w);
 
			break;
 
		}
 

	
 
		case BUILD_VEHICLE_WIDGET_SORT_TEXT: case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:/* Select sorting criteria dropdown menu */
 
			ShowDropDownMenu(w, _aircraft_sort_listing, bv->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, 0);
 
			return;
 

	
 
		case BUILD_VEHICLE_WIDGET_BUILD: {
 
			EngineID sel_eng = bv->sel_engine;
 
			if (sel_eng != INVALID_ENGINE) {
 
				DoCommandP(w->window_number, sel_eng, 0, CcBuildAircraft, CMD_BUILD_AIRCRAFT | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT));
 
			}
 
			break;
 
		}
 

	
 
		case BUILD_VEHICLE_WIDGET_RENAME: {
 
			EngineID sel_eng = bv->sel_engine;
 
			if (sel_eng != INVALID_ENGINE) {
 
				bv->rename_engine = sel_eng;
 
				ShowQueryString(GetCustomEngineName(sel_eng), STR_A039_RENAME_AIRCRAFT_TYPE, 31, 160, w, CS_ALPHANUMERAL);
 
			}
 
			break;
 
		}
 
	}
 
}
 

	
 
static void NewAircraftWndProc(Window *w, WindowEvent *e)
 
{
 
	buildvehicle_d *bv = &WP(w, buildvehicle_d);
 

	
 
	switch (e->event) {
 
		case WE_INVALIDATE_DATA:
 
			GenerateBuildList(w);
 
			break;
 

	
 
		case WE_DESTROY:
 
			EngList_Destroy(&bv->eng_list);
 
			break;
 

	
 
		case WE_PAINT:
 
			DrawBuildAircraftWindow(w);
 
			break;
 

	
 
		case WE_CLICK:
 
			BuildAircraftClickEvent(w, e);
 
			break;
 

	
 
		case WE_ON_EDIT_TEXT: {
 
			if (e->we.edittext.str[0] != '\0') {
 
				_cmd_text = e->we.edittext.str;
 
				DoCommandP(0, bv->rename_engine, 0, NULL, CMD_RENAME_ENGINE | CMD_MSG(STR_A03A_CAN_T_RENAME_AIRCRAFT_TYPE));
 
			}
 
			break;
 
		}
 

	
 
		case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
			if (bv->sort_criteria != e->we.dropdown.index) {
 
				bv->sort_criteria = _last_sort_criteria = e->we.dropdown.index;
 
				GenerateBuildList(w);
 
			}
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case WE_RESIZE:
 
			w->vscroll.cap += e->we.sizing.diff.y / 24;
 
			w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
 
			break;
 
	}
 
}
 

	
 
static const WindowDesc _build_vehicle_desc = {
 
	WDP_AUTO, WDP_AUTO, 240, 226,
 
	WC_BUILD_VEHICLE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_build_vehicle_widgets,
 
	NewAircraftWndProc
 
};
 

	
 
void ShowBuildVehicleWindow(TileIndex tile, byte type)
 
{
 
	buildvehicle_d *bv;
 
	Window *w;
 

	
 
	DeleteWindowById(WC_BUILD_VEHICLE, tile);
 
	w = AllocateWindowDescFront(&_build_vehicle_desc, tile);
 
	if (w == NULL) return;
 

	
 
	w->caption_color = (tile != 0) ? GetTileOwner(tile) : _local_player;
 
	w->resize.step_height = GetVehicleListHeight(type);
 
	w->vscroll.cap = 4;
 
	w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
 

	
 
	bv = &WP(w, buildvehicle_d);
 
	EngList_Create(&bv->eng_list);
 
	bv->sel_engine            = INVALID_ENGINE;
 
	bv->sort_criteria         = _last_sort_criteria;
 
	bv->descending_sort_order = _last_sort_order;
 

	
 
	bv->vehicle_type = type;
 

	
 
	switch (type) {
 
		case VEH_Aircraft: {
 
			byte acc_planes = (tile == 0) ? ALL : GetAirport(GetStationByTile(tile)->airport_type)->acc_planes;
 
			bv->filter.acc_planes = acc_planes;
 
			break;
 
		}
 
		default: NOT_REACHED();
 
	}
 

	
 
	GenerateBuildList(w);
 
	/* Select the first plane in the list as default when opening the window */
 
	if (EngList_Count(&bv->eng_list) > 0) bv->sel_engine = bv->eng_list[0];
 
}
src/callback_table.c
Show inline comments
 
deleted file
src/callback_table.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "callback_table.h"
 
#include "functions.h"
 

	
 
// If you add a callback for DoCommandP, also add the callback in here
 
//   see below for the full list!
 
// If you don't do it, it won't work across the network!!
 

	
 
/* aircraft_gui.c */
 
CommandCallback CcBuildAircraft;
 
CommandCallback CcCloneAircraft;
 

	
 
/* airport_gui.c */
 
CommandCallback CcBuildAirport;
 

	
 
/* bridge_gui.c */
 
CommandCallback CcBuildBridge;
 

	
 
/* dock_gui.c */
 
CommandCallback CcBuildDocks;
 
CommandCallback CcBuildCanal;
 

	
 
/* depot_gui.c */
 
CommandCallback CcCloneVehicle;
 

	
 
/* main_gui.c */
 
CommandCallback CcPlaySound10;
 
CommandCallback CcPlaceSign;
 
CommandCallback CcTerraform;
 
CommandCallback CcBuildTown;
 

	
 
/* rail_gui.c */
 
CommandCallback CcPlaySound1E;
 
CommandCallback CcRailDepot;
 
CommandCallback CcStation;
 
CommandCallback CcBuildRailTunnel;
 

	
 
/* road_gui.c */
 
CommandCallback CcPlaySound1D;
 
CommandCallback CcBuildRoadTunnel;
 
CommandCallback CcRoadDepot;
 

	
 
/* roadveh_gui.c */
 
CommandCallback CcBuildRoadVeh;
 
CommandCallback CcCloneRoadVeh;
 

	
 
/* ship_gui.c */
 
CommandCallback CcBuildShip;
 
CommandCallback CcCloneShip;
 

	
 
/* train_gui.c */
 
CommandCallback CcBuildWagon;
 
CommandCallback CcBuildLoco;
 
CommandCallback CcCloneTrain;
 

	
 
CommandCallback CcAI;
 

	
 
CommandCallback *_callback_table[] = {
 
	/* 0x00 */ NULL,
 
	/* 0x01 */ CcBuildAircraft,
 
	/* 0x02 */ CcBuildAirport,
 
	/* 0x03 */ CcBuildBridge,
 
	/* 0x04 */ CcBuildCanal,
 
	/* 0x05 */ CcBuildDocks,
 
	/* 0x06 */ CcBuildLoco,
 
	/* 0x07 */ CcBuildRoadVeh,
 
	/* 0x08 */ CcBuildShip,
 
	/* 0x09 */ CcBuildTown,
 
	/* 0x0A */ CcBuildRoadTunnel,
 
	/* 0x0B */ CcBuildRailTunnel,
 
	/* 0x0C */ CcBuildWagon,
 
	/* 0x0D */ CcRoadDepot,
 
	/* 0x0E */ CcRailDepot,
 
	/* 0x0F */ CcPlaceSign,
 
	/* 0x10 */ CcPlaySound10,
 
	/* 0x11 */ CcPlaySound1D,
 
	/* 0x12 */ CcPlaySound1E,
 
	/* 0x13 */ CcStation,
 
	/* 0x14 */ CcTerraform,
 
	/* 0x15 */ CcCloneAircraft,
 
	/* 0x16 */ CcCloneRoadVeh,
 
	/* 0x17 */ CcCloneShip,
 
	/* 0x18 */ CcCloneTrain,
 
	/* 0x19 */ CcAI,
 
	/* 0x1A */ CcCloneVehicle
 
};
 

	
 
const int _callback_table_count = lengthof(_callback_table);
src/clear_cmd.c
Show inline comments
 
deleted file
src/clear_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "clear_map.h"
 
#include "rail_map.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "player.h"
 
#include "tile.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "tunnel_map.h"
 
#include "bridge_map.h"
 
#include "variables.h"
 
#include "table/sprites.h"
 
#include "unmovable_map.h"
 
#include "genworld.h"
 
#include "industry.h"
 

	
 
typedef struct TerraformerHeightMod {
 
	TileIndex tile;
 
	byte height;
 
} TerraformerHeightMod;
 

	
 
typedef struct TerraformerState {
 
	int height[4];
 
	uint32 flags;
 

	
 
	int direction;
 
	int modheight_count;
 
	int tile_table_count;
 

	
 
	int32 cost;
 

	
 
	TileIndex *tile_table;
 
	TerraformerHeightMod *modheight;
 

	
 
} TerraformerState;
 

	
 
static int TerraformAllowTileProcess(TerraformerState *ts, TileIndex tile)
 
{
 
	TileIndex *t;
 
	int count;
 

	
 
	if (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY()) return -1;
 

	
 
	t = ts->tile_table;
 
	for (count = ts->tile_table_count; count != 0; count--, t++) {
 
		if (*t == tile) return 0;
 
	}
 

	
 
	return 1;
 
}
 

	
 
static int TerraformGetHeightOfTile(TerraformerState *ts, TileIndex tile)
 
{
 
	TerraformerHeightMod *mod = ts->modheight;
 
	int count;
 

	
 
	for (count = ts->modheight_count; count != 0; count--, mod++) {
 
		if (mod->tile == tile) return mod->height;
 
	}
 

	
 
	return TileHeight(tile);
 
}
 

	
 
static void TerraformAddDirtyTile(TerraformerState *ts, TileIndex tile)
 
{
 
	int count;
 
	TileIndex *t;
 

	
 
	count = ts->tile_table_count;
 

	
 
	if (count >= 625) return;
 

	
 
	for (t = ts->tile_table; count != 0; count--,t++) {
 
		if (*t == tile) return;
 
	}
 

	
 
	ts->tile_table[ts->tile_table_count++] = tile;
 
}
 

	
 
static void TerraformAddDirtyTileAround(TerraformerState *ts, TileIndex tile)
 
{
 
	TerraformAddDirtyTile(ts, tile + TileDiffXY( 0, -1));
 
	TerraformAddDirtyTile(ts, tile + TileDiffXY(-1, -1));
 
	TerraformAddDirtyTile(ts, tile + TileDiffXY(-1,  0));
 
	TerraformAddDirtyTile(ts, tile);
 
}
 

	
 
static int TerraformProc(TerraformerState *ts, TileIndex tile, int mode)
 
{
 
	int r;
 
	int32 ret;
 

	
 
	assert(tile < MapSize());
 

	
 
	r = TerraformAllowTileProcess(ts, tile);
 
	if (r <= 0) return r;
 

	
 
	if (IsTileType(tile, MP_RAILWAY)) {
 
		static const TrackBits safe_track[] = { TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER, TRACK_BIT_RIGHT };
 
		static const Slope unsafe_slope[] = { SLOPE_S, SLOPE_W, SLOPE_N, SLOPE_E };
 

	
 
		Slope tileh;
 
		uint z;
 

	
 
		// Nothing could be built at the steep slope - this avoids a bug
 
		// when you have a single diagonal track in one corner on a
 
		// basement and then you raise/lower the other corner.
 
		tileh = GetTileSlope(tile, &z);
 
		if (tileh == unsafe_slope[mode] ||
 
				tileh == ComplementSlope(unsafe_slope[mode])) {
 
			_terraform_err_tile = tile;
 
			_error_message = STR_1008_MUST_REMOVE_RAILROAD_TRACK;
 
			return -1;
 
		}
 

	
 
		// If we have a single diagonal track there, the other side of
 
		// tile can be terraformed.
 
		if (IsPlainRailTile(tile) && GetTrackBits(tile) == safe_track[mode]) {
 
			/* If terraforming downwards prevent damaging a potential tunnel below.
 
			 * This check is only necessary for flat tiles, because if the tile is
 
			 * non-flat, then the corner opposing the rail is raised. Only this corner
 
			 * can be lowered and this is a safe action
 
			 */
 
			if (tileh == SLOPE_FLAT &&
 
					ts->direction == -1 &&
 
					IsTunnelInWay(tile, z - TILE_HEIGHT)) {
 
				_terraform_err_tile = tile;
 
				_error_message = STR_1002_EXCAVATION_WOULD_DAMAGE;
 
				return -1;
 
			}
 
			return 0;
 
		}
 
	}
 

	
 
	ret = DoCommand(tile, 0,0, ts->flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR);
 

	
 
	if (CmdFailed(ret)) {
 
		_terraform_err_tile = tile;
 
		return -1;
 
	}
 

	
 
	ts->cost += ret;
 

	
 
	if (ts->tile_table_count >= 625) return -1;
 
	ts->tile_table[ts->tile_table_count++] = tile;
 

	
 
	return 0;
 
}
 

	
 
static bool TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height)
 
{
 
	int nh;
 
	TerraformerHeightMod *mod;
 
	int count;
 

	
 
	assert(tile < MapSize());
 

	
 
	if (height < 0) {
 
		_error_message = STR_1003_ALREADY_AT_SEA_LEVEL;
 
		return false;
 
	}
 

	
 
	_error_message = STR_1004_TOO_HIGH;
 

	
 
	if (height > 15) return false;
 

	
 
	nh = TerraformGetHeightOfTile(ts, tile);
 
	if (nh < 0 || height == nh) return false;
 

	
 
	if (TerraformProc(ts, tile, 0) < 0) return false;
 
	if (TerraformProc(ts, tile + TileDiffXY( 0, -1), 1) < 0) return false;
 
	if (TerraformProc(ts, tile + TileDiffXY(-1, -1), 2) < 0) return false;
 
	if (TerraformProc(ts, tile + TileDiffXY(-1,  0), 3) < 0) return false;
 

	
 
	mod = ts->modheight;
 
	count = ts->modheight_count;
 

	
 
	for (;;) {
 
		if (count == 0) {
 
			if (ts->modheight_count >= 576) return false;
 
			ts->modheight_count++;
 
			break;
 
		}
 
		if (mod->tile == tile) break;
 
		mod++;
 
		count--;
 
	}
 

	
 
	mod->tile = tile;
 
	mod->height = (byte)height;
 

	
 
	ts->cost += _price.terraform;
 

	
 
	{
 
		int direction = ts->direction, r;
 
		const TileIndexDiffC *ttm;
 

	
 
		static const TileIndexDiffC _terraform_tilepos[] = {
 
			{ 1,  0},
 
			{-2,  0},
 
			{ 1,  1},
 
			{ 0, -2}
 
		};
 

	
 
		for (ttm = _terraform_tilepos; ttm != endof(_terraform_tilepos); ttm++) {
 
			tile += ToTileIndexDiff(*ttm);
 

	
 
			r = TerraformGetHeightOfTile(ts, tile);
 
			if (r != height && r-direction != height && r+direction != height) {
 
				if (!TerraformTileHeight(ts, tile, r+direction))
 
					return false;
 
			}
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
/** Terraform land
 
 * @param tile tile to terraform
 
 * @param p1 corners to terraform.
 
 * @param p2 direction; eg up or down
 
 */
 
int32 CmdTerraformLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	TerraformerState ts;
 
	TileIndex t;
 
	int direction;
 

	
 
	TerraformerHeightMod modheight_data[576];
 
	TileIndex tile_table_data[625];
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	_terraform_err_tile = 0;
 

	
 
	ts.direction = direction = p2 ? 1 : -1;
 
	ts.flags = flags;
 
	ts.modheight_count = ts.tile_table_count = 0;
 
	ts.cost = 0;
 
	ts.modheight = modheight_data;
 
	ts.tile_table = tile_table_data;
 

	
 
	/* Make an extra check for map-bounds cause we add tiles to the originating tile */
 
	if (tile + TileDiffXY(1, 1) >= MapSize()) return CMD_ERROR;
 

	
 
	if (p1 & 1) {
 
		t = tile + TileDiffXY(1, 0);
 
		if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	if (p1 & 2) {
 
		t = tile + TileDiffXY(1, 1);
 
		if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	if (p1 & 4) {
 
		t = tile + TileDiffXY(0, 1);
 
		if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	if (p1 & 8) {
 
		t = tile + TileDiffXY(0, 0);
 
		if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	{
 
		/* Check if tunnel would take damage */
 
		int count;
 
		TileIndex *ti = ts.tile_table;
 

	
 
		for (count = ts.tile_table_count; count != 0; count--, ti++) {
 
			TileIndex tile = *ti;
 

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

	
 
			if (direction == -1) {
 
				uint z, t;
 

	
 
				z = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0));
 
				t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0));
 
				if (t <= z) z = t;
 
				t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1));
 
				if (t <= z) z = t;
 
				t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 1));
 
				if (t <= z) z = t;
 

	
 
				if (IsTunnelInWay(tile, z * TILE_HEIGHT)) {
 
					return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE);
 
				}
 
			}
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		/* Clear the landscape at the tiles */
 
		{
 
			int count;
 
			TileIndex *ti = ts.tile_table;
 
			for (count = ts.tile_table_count; count != 0; count--, ti++) {
 
				DoCommand(*ti, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
			}
 
		}
 

	
 
		/* change the height */
 
		{
 
			int count;
 
			TerraformerHeightMod *mod;
 

	
 
			mod = ts.modheight;
 
			for (count = ts.modheight_count; count != 0; count--, mod++) {
 
				TileIndex til = mod->tile;
 

	
 
				SetTileHeight(til, mod->height);
 
				TerraformAddDirtyTileAround(&ts, til);
 
			}
 
		}
 

	
 
		/* finally mark the dirty tiles dirty */
 
		{
 
			int count;
 
			TileIndex *ti = ts.tile_table;
 
			for (count = ts.tile_table_count; count != 0; count--, ti++) {
 
				MarkTileDirtyByTile(*ti);
 
			}
 
		}
 
	}
 
	return ts.cost;
 
}
 

	
 

	
 
/** Levels a selected (rectangle) area of land
 
 * @param tile end tile of area-drag
 
 * @param p1 start tile of area drag
 
 * @param p2 unused
 
 */
 
int32 CmdLevelLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int size_x, size_y;
 
	int ex;
 
	int ey;
 
	int sx, sy;
 
	uint h, curh;
 
	int32 ret, cost, money;
 

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

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	// remember level height
 
	h = TileHeight(p1);
 

	
 
	// make sure sx,sy are smaller than ex,ey
 
	ex = TileX(tile);
 
	ey = TileY(tile);
 
	sx = TileX(p1);
 
	sy = TileY(p1);
 
	if (ex < sx) intswap(ex, sx);
 
	if (ey < sy) intswap(ey, sy);
 
	tile = TileXY(sx, sy);
 

	
 
	size_x = ex-sx+1;
 
	size_y = ey-sy+1;
 

	
 
	money = GetAvailableMoneyForCommand();
 
	cost = 0;
 

	
 
	BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) {
 
		curh = TileHeight(tile2);
 
		while (curh != h) {
 
			ret = DoCommand(tile2, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND);
 
			if (CmdFailed(ret)) break;
 
			cost += ret;
 

	
 
			if (flags & DC_EXEC) {
 
				if ((money -= ret) < 0) {
 
					_additional_cash_required = ret;
 
					return cost - ret;
 
				}
 
				DoCommand(tile2, 8, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND);
 
			}
 

	
 
			curh += (curh > h) ? -1 : 1;
 
		}
 
	} END_TILE_LOOP(tile2, size_x, size_y, tile)
 

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

	
 
/** Purchase a land area. Actually you only purchase one tile, so
 
 * the name is a bit confusing ;p
 
 * @param tile the tile the player is purchasing
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdPurchaseLandArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 cost;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	if (IsOwnedLandTile(tile) && IsTileOwner(tile, _current_player)) {
 
		return_cmd_error(STR_5807_YOU_ALREADY_OWN_IT);
 
	}
 

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

	
 
	if (flags & DC_EXEC) {
 
		MakeOwnedLand(tile, _current_player);
 
		MarkTileDirtyByTile(tile);
 
	}
 

	
 
	return cost + _price.purchase_land * 10;
 
}
 

	
 

	
 
static int32 ClearTile_Clear(TileIndex tile, byte flags)
 
{
 
	static const int32* clear_price_table[] = {
 
		&_price.clear_1,
 
		&_price.purchase_land,
 
		&_price.clear_2,
 
		&_price.clear_3,
 
		&_price.purchase_land,
 
		&_price.purchase_land,
 
		&_price.clear_2, // XXX unused?
 
	};
 
	int32 price;
 

	
 
	if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) {
 
		price = 0;
 
	} else {
 
		price = *clear_price_table[GetClearGround(tile)];
 
	}
 

	
 
	if (flags & DC_EXEC) DoClearSquare(tile);
 

	
 
	return price;
 
}
 

	
 
/** Sell a land area. Actually you only sell one tile, so
 
 * the name is a bit confusing ;p
 
 * @param tile the tile the player is selling
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdSellLandArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!IsOwnedLandTile(tile)) return CMD_ERROR;
 
	if (!CheckTileOwnership(tile) && _current_player != OWNER_WATER) return CMD_ERROR;
 

	
 

	
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) DoClearSquare(tile);
 

	
 
	return - _price.purchase_land * 2;
 
}
 

	
 

	
 
#include "table/clear_land.h"
 

	
 

	
 
void DrawClearLandTile(const TileInfo *ti, byte set)
 
{
 
	DrawGroundSprite(SPR_FLAT_BARE_LAND + _tileh_to_sprite[ti->tileh] + set * 19);
 
}
 

	
 
void DrawHillyLandTile(const TileInfo *ti)
 
{
 
	if (ti->tileh != SLOPE_FLAT) {
 
		DrawGroundSprite(SPR_FLAT_ROUGH_LAND + _tileh_to_sprite[ti->tileh]);
 
	} else {
 
		DrawGroundSprite(_landscape_clear_sprites[GB(ti->x ^ ti->y, 4, 3)]);
 
	}
 
}
 

	
 
void DrawClearLandFence(const TileInfo *ti)
 
{
 
	byte z = ti->z;
 

	
 
	if (ti->tileh & SLOPE_S) {
 
		z += TILE_HEIGHT;
 
		if (ti->tileh == SLOPE_STEEP_S) z += TILE_HEIGHT;
 
	}
 

	
 
	if (GetFenceSW(ti->tile) != 0) {
 
		DrawGroundSpriteAt(_clear_land_fence_sprites_1[GetFenceSW(ti->tile) - 1] + _fence_mod_by_tileh[ti->tileh], ti->x, ti->y, z);
 
	}
 

	
 
	if (GetFenceSE(ti->tile) != 0) {
 
		DrawGroundSpriteAt(_clear_land_fence_sprites_1[GetFenceSE(ti->tile) - 1] + _fence_mod_by_tileh_2[ti->tileh], ti->x, ti->y, z);
 
	}
 
}
 

	
 
static void DrawTile_Clear(TileInfo *ti)
 
{
 
	switch (GetClearGround(ti->tile)) {
 
		case CLEAR_GRASS:
 
			DrawClearLandTile(ti, GetClearDensity(ti->tile));
 
			break;
 

	
 
		case CLEAR_ROUGH:
 
			DrawHillyLandTile(ti);
 
			break;
 

	
 
		case CLEAR_ROCKS:
 
			DrawGroundSprite(SPR_FLAT_ROCKY_LAND_1 + _tileh_to_sprite[ti->tileh]);
 
			break;
 

	
 
		case CLEAR_FIELDS:
 
			DrawGroundSprite(_clear_land_sprites_1[GetFieldType(ti->tile)] + _tileh_to_sprite[ti->tileh]);
 
			break;
 

	
 
		case CLEAR_SNOW:
 
			DrawGroundSprite(_clear_land_sprites_2[GetClearDensity(ti->tile)] + _tileh_to_sprite[ti->tileh]);
 
			break;
 

	
 
		case CLEAR_DESERT:
 
			DrawGroundSprite(_clear_land_sprites_3[GetClearDensity(ti->tile)] + _tileh_to_sprite[ti->tileh]);
 
			break;
 
	}
 

	
 
	DrawClearLandFence(ti);
 
	DrawBridgeMiddle(ti);
 
}
 

	
 
static uint GetSlopeZ_Clear(TileIndex tile, uint x, uint y)
 
{
 
	uint z;
 
	uint tileh = GetTileSlope(tile, &z);
 

	
 
	return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
 
}
 

	
 
static Slope GetSlopeTileh_Clear(TileIndex tile, Slope tileh)
 
{
 
	return tileh;
 
}
 

	
 
static void GetAcceptedCargo_Clear(TileIndex tile, AcceptedCargo ac)
 
{
 
	/* unused */
 
}
 

	
 
static void AnimateTile_Clear(TileIndex tile)
 
{
 
	/* unused */
 
}
 

	
 
void TileLoopClearHelper(TileIndex tile)
 
{
 
	byte self;
 
	byte neighbour;
 
	TileIndex dirty = INVALID_TILE;
 

	
 
	self = (IsTileType(tile, MP_CLEAR) && IsClearGround(tile, CLEAR_FIELDS));
 

	
 
	neighbour = (IsTileType(TILE_ADDXY(tile, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 1, 0), CLEAR_FIELDS));
 
	if (GetFenceSW(tile) == 0) {
 
		if (self != neighbour) {
 
			SetFenceSW(tile, 3);
 
			dirty = tile;
 
		}
 
	} else {
 
		if (self == 0 && neighbour == 0) {
 
			SetFenceSW(tile, 0);
 
			dirty = tile;
 
		}
 
	}
 

	
 
	neighbour = (IsTileType(TILE_ADDXY(tile, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, 1), CLEAR_FIELDS));
 
	if (GetFenceSE(tile) == 0) {
 
		if (self != neighbour) {
 
			SetFenceSE(tile, 3);
 
			dirty = tile;
 
		}
 
	} else {
 
		if (self == 0 && neighbour == 0) {
 
			SetFenceSE(tile, 0);
 
			dirty = tile;
 
		}
 
	}
 

	
 
	if (dirty != INVALID_TILE) MarkTileDirtyByTile(dirty);
 
}
 

	
 

	
 
/* convert into snowy tiles */
 
static void TileLoopClearAlps(TileIndex tile)
 
{
 
	int k = GetTileZ(tile) - _opt.snow_line + TILE_HEIGHT;
 

	
 
	if (k < 0) { // well below the snow line
 
		if (!IsClearGround(tile, CLEAR_SNOW)) return;
 
		if (GetClearDensity(tile) == 0) SetClearGroundDensity(tile, CLEAR_GRASS, 3);
 
	} else {
 
		if (!IsClearGround(tile, CLEAR_SNOW)) {
 
			SetClearGroundDensity(tile, CLEAR_SNOW, 0);
 
		} else {
 
			uint density = min((uint)k / TILE_HEIGHT, 3);
 

	
 
			if (GetClearDensity(tile) < density) {
 
				AddClearDensity(tile, 1);
 
			} else if (GetClearDensity(tile) > density) {
 
				AddClearDensity(tile, -1);
 
			} else {
 
				return;
 
			}
 
		}
 
	}
 

	
 
	MarkTileDirtyByTile(tile);
 
}
 

	
 
static void TileLoopClearDesert(TileIndex tile)
 
{
 
	if (IsClearGround(tile, CLEAR_DESERT)) return;
 

	
 
	if (GetTropicZone(tile) == TROPICZONE_DESERT) {
 
		SetClearGroundDensity(tile, CLEAR_DESERT, 3);
 
	} else {
 
		if (GetTropicZone(tile + TileDiffXY( 1,  0)) != TROPICZONE_DESERT &&
 
				GetTropicZone(tile + TileDiffXY(-1,  0)) != TROPICZONE_DESERT &&
 
				GetTropicZone(tile + TileDiffXY( 0,  1)) != TROPICZONE_DESERT &&
 
				GetTropicZone(tile + TileDiffXY( 0, -1)) != TROPICZONE_DESERT)
 
			return;
 
		SetClearGroundDensity(tile, CLEAR_DESERT, 1);
 
	}
 

	
 
	MarkTileDirtyByTile(tile);
 
}
 

	
 
static void TileLoop_Clear(TileIndex tile)
 
{
 
	TileLoopClearHelper(tile);
 

	
 
	switch (_opt.landscape) {
 
		case LT_DESERT: TileLoopClearDesert(tile); break;
 
		case LT_HILLY:  TileLoopClearAlps(tile);   break;
 
	}
 

	
 
	switch (GetClearGround(tile)) {
 
		case CLEAR_GRASS:
 
			if (GetClearDensity(tile) == 3) return;
 

	
 
			if (_game_mode != GM_EDITOR) {
 
				if (GetClearCounter(tile) < 7) {
 
					AddClearCounter(tile, 1);
 
					return;
 
				} else {
 
					SetClearCounter(tile, 0);
 
					AddClearDensity(tile, 1);
 
				}
 
			} else {
 
				SetClearGroundDensity(tile, GB(Random(), 0, 8) > 21 ? CLEAR_GRASS : CLEAR_ROUGH, 3);
 
			}
 
			break;
 

	
 
		case CLEAR_FIELDS: {
 
			uint field_type;
 

	
 
			if (_game_mode == GM_EDITOR) return;
 

	
 
			if (GetClearCounter(tile) < 7) {
 
				AddClearCounter(tile, 1);
 
				return;
 
			} else {
 
				SetClearCounter(tile, 0);
 
			}
 

	
 
			if (GetIndustryIndexOfField(tile) == INVALID_INDUSTRY && GetFieldType(tile) >= 7) {
 
				/* This farmfield is no longer farmfield, so make it grass again */
 
				MakeClear(tile, CLEAR_GRASS, 2);
 
			} else {
 
				field_type = GetFieldType(tile);
 
				field_type = (field_type < 8) ? field_type + 1 : 0;
 
				SetFieldType(tile, field_type);
 
			}
 
			break;
 
		}
 

	
 
		default:
 
			return;
 
	}
 

	
 
	MarkTileDirtyByTile(tile);
 
}
 

	
 
void GenerateClearTile(void)
 
{
 
	uint i, gi;
 
	TileIndex tile;
 

	
 
	/* add rough tiles */
 
	i = ScaleByMapSize(GB(Random(), 0, 10) + 0x400);
 
	gi = ScaleByMapSize(GB(Random(), 0, 7) + 0x80);
 

	
 
	SetGeneratingWorldProgress(GWP_ROUGH_ROCKY, gi + i);
 
	do {
 
		IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
 
		tile = RandomTile();
 
		if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) SetClearGroundDensity(tile, CLEAR_ROUGH, 3);
 
	} while (--i);
 

	
 
	/* add rocky tiles */
 
	i = gi;
 
	do {
 
		uint32 r = Random();
 
		tile = RandomTileSeed(r);
 

	
 
		IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
 
		if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) {
 
			uint j = GB(r, 16, 4) + 5;
 
			for (;;) {
 
				TileIndex tile_new;
 

	
 
				SetClearGroundDensity(tile, CLEAR_ROCKS, 3);
 
				do {
 
					if (--j == 0) goto get_out;
 
					tile_new = tile + TileOffsByDiagDir(GB(Random(), 0, 2));
 
				} while (!IsTileType(tile_new, MP_CLEAR) || IsClearGround(tile_new, CLEAR_DESERT));
 
				tile = tile_new;
 
			}
 
get_out:;
 
		}
 
	} while (--i);
 
}
 

	
 
static void ClickTile_Clear(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static uint32 GetTileTrackStatus_Clear(TileIndex tile, TransportType mode)
 
{
 
	return 0;
 
}
 

	
 
static const StringID _clear_land_str[] = {
 
	STR_080D_GRASS,
 
	STR_080B_ROUGH_LAND,
 
	STR_080A_ROCKS,
 
	STR_080E_FIELDS,
 
	STR_080F_SNOW_COVERED_LAND,
 
	STR_0810_DESERT
 
};
 

	
 
static void GetTileDesc_Clear(TileIndex tile, TileDesc *td)
 
{
 
	if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) {
 
		td->str = STR_080C_BARE_LAND;
 
	} else {
 
		td->str = _clear_land_str[GetClearGround(tile)];
 
	}
 
	td->owner = GetTileOwner(tile);
 
}
 

	
 
static void ChangeTileOwner_Clear(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	return;
 
}
 

	
 
void InitializeClearLand(void)
 
{
 
	_opt.snow_line = _patches.snow_line_height * TILE_HEIGHT;
 
}
 

	
 
const TileTypeProcs _tile_type_clear_procs = {
 
	DrawTile_Clear,           /* draw_tile_proc */
 
	GetSlopeZ_Clear,          /* get_slope_z_proc */
 
	ClearTile_Clear,          /* clear_tile_proc */
 
	GetAcceptedCargo_Clear,   /* get_accepted_cargo_proc */
 
	GetTileDesc_Clear,        /* get_tile_desc_proc */
 
	GetTileTrackStatus_Clear, /* get_tile_track_status_proc */
 
	ClickTile_Clear,          /* click_tile_proc */
 
	AnimateTile_Clear,        /* animate_tile_proc */
 
	TileLoop_Clear,           /* tile_loop_clear */
 
	ChangeTileOwner_Clear,    /* change_tile_owner_clear */
 
	NULL,                     /* get_produced_cargo_proc */
 
	NULL,                     /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_Clear,      /* get_slope_tileh_proc */
 
};
src/command.c
Show inline comments
 
deleted file
src/command.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "gui.h"
 
#include "command.h"
 
#include "player.h"
 
#include "network/network.h"
 
#include "variables.h"
 
#include "genworld.h"
 

	
 
const char* _cmd_text = NULL;
 

	
 
#define DEF_COMMAND(yyyy) int32 yyyy(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 

	
 
DEF_COMMAND(CmdBuildRailroadTrack);
 
DEF_COMMAND(CmdRemoveRailroadTrack);
 
DEF_COMMAND(CmdBuildSingleRail);
 
DEF_COMMAND(CmdRemoveSingleRail);
 

	
 
DEF_COMMAND(CmdLandscapeClear);
 

	
 
DEF_COMMAND(CmdBuildBridge);
 

	
 
DEF_COMMAND(CmdBuildRailroadStation);
 
DEF_COMMAND(CmdRemoveFromRailroadStation);
 
DEF_COMMAND(CmdConvertRail);
 

	
 
DEF_COMMAND(CmdBuildSingleSignal);
 
DEF_COMMAND(CmdRemoveSingleSignal);
 

	
 
DEF_COMMAND(CmdTerraformLand);
 

	
 
DEF_COMMAND(CmdPurchaseLandArea);
 
DEF_COMMAND(CmdSellLandArea);
 

	
 
DEF_COMMAND(CmdBuildTunnel);
 

	
 
DEF_COMMAND(CmdBuildTrainDepot);
 
DEF_COMMAND(CmdBuildTrainWaypoint);
 
DEF_COMMAND(CmdRenameWaypoint);
 
DEF_COMMAND(CmdRemoveTrainWaypoint);
 

	
 
DEF_COMMAND(CmdBuildRoadStop);
 

	
 
DEF_COMMAND(CmdBuildLongRoad);
 
DEF_COMMAND(CmdRemoveLongRoad);
 
DEF_COMMAND(CmdBuildRoad);
 
DEF_COMMAND(CmdRemoveRoad);
 

	
 
DEF_COMMAND(CmdBuildRoadDepot);
 

	
 
DEF_COMMAND(CmdBuildAirport);
 

	
 
DEF_COMMAND(CmdBuildDock);
 

	
 
DEF_COMMAND(CmdBuildShipDepot);
 

	
 
DEF_COMMAND(CmdBuildBuoy);
 

	
 
DEF_COMMAND(CmdPlantTree);
 

	
 
DEF_COMMAND(CmdBuildRailVehicle);
 
DEF_COMMAND(CmdMoveRailVehicle);
 

	
 
DEF_COMMAND(CmdStartStopTrain);
 

	
 
DEF_COMMAND(CmdSellRailWagon);
 

	
 
DEF_COMMAND(CmdSendTrainToDepot);
 
DEF_COMMAND(CmdForceTrainProceed);
 
DEF_COMMAND(CmdReverseTrainDirection);
 

	
 
DEF_COMMAND(CmdModifyOrder);
 
DEF_COMMAND(CmdSkipOrder);
 
DEF_COMMAND(CmdDeleteOrder);
 
DEF_COMMAND(CmdInsertOrder);
 
DEF_COMMAND(CmdChangeServiceInt);
 
DEF_COMMAND(CmdRestoreOrderIndex);
 

	
 
DEF_COMMAND(CmdBuildIndustry);
 

	
 
DEF_COMMAND(CmdBuildCompanyHQ);
 
DEF_COMMAND(CmdSetPlayerFace);
 
DEF_COMMAND(CmdSetPlayerColor);
 

	
 
DEF_COMMAND(CmdIncreaseLoan);
 
DEF_COMMAND(CmdDecreaseLoan);
 

	
 
DEF_COMMAND(CmdWantEnginePreview);
 

	
 
DEF_COMMAND(CmdNameVehicle);
 
DEF_COMMAND(CmdRenameEngine);
 

	
 
DEF_COMMAND(CmdChangeCompanyName);
 
DEF_COMMAND(CmdChangePresidentName);
 

	
 
DEF_COMMAND(CmdRenameStation);
 

	
 
DEF_COMMAND(CmdSellAircraft);
 
DEF_COMMAND(CmdStartStopAircraft);
 
DEF_COMMAND(CmdBuildAircraft);
 
DEF_COMMAND(CmdSendAircraftToHangar);
 
DEF_COMMAND(CmdRefitAircraft);
 

	
 
DEF_COMMAND(CmdPlaceSign);
 
DEF_COMMAND(CmdRenameSign);
 

	
 
DEF_COMMAND(CmdBuildRoadVeh);
 
DEF_COMMAND(CmdStartStopRoadVeh);
 
DEF_COMMAND(CmdSellRoadVeh);
 
DEF_COMMAND(CmdSendRoadVehToDepot);
 
DEF_COMMAND(CmdTurnRoadVeh);
 
DEF_COMMAND(CmdRefitRoadVeh);
 

	
 
DEF_COMMAND(CmdPause);
 

	
 
DEF_COMMAND(CmdBuyShareInCompany);
 
DEF_COMMAND(CmdSellShareInCompany);
 
DEF_COMMAND(CmdBuyCompany);
 

	
 
DEF_COMMAND(CmdBuildTown);
 

	
 
DEF_COMMAND(CmdRenameTown);
 
DEF_COMMAND(CmdDoTownAction);
 

	
 
DEF_COMMAND(CmdSetRoadDriveSide);
 

	
 
DEF_COMMAND(CmdChangeDifficultyLevel);
 
DEF_COMMAND(CmdChangePatchSetting);
 

	
 
DEF_COMMAND(CmdStartStopShip);
 
DEF_COMMAND(CmdSellShip);
 
DEF_COMMAND(CmdBuildShip);
 
DEF_COMMAND(CmdSendShipToDepot);
 
DEF_COMMAND(CmdRefitShip);
 

	
 
DEF_COMMAND(CmdOrderRefit);
 
DEF_COMMAND(CmdCloneOrder);
 

	
 
DEF_COMMAND(CmdClearArea);
 

	
 
DEF_COMMAND(CmdGiveMoney);
 
DEF_COMMAND(CmdMoneyCheat);
 
DEF_COMMAND(CmdBuildCanal);
 
DEF_COMMAND(CmdBuildLock);
 

	
 
DEF_COMMAND(CmdPlayerCtrl);
 

	
 
DEF_COMMAND(CmdLevelLand);
 

	
 
DEF_COMMAND(CmdRefitRailVehicle);
 

	
 
DEF_COMMAND(CmdBuildSignalTrack);
 
DEF_COMMAND(CmdRemoveSignalTrack);
 

	
 
DEF_COMMAND(CmdSetAutoReplace);
 

	
 
DEF_COMMAND(CmdCloneVehicle);
 
DEF_COMMAND(CmdMassStartStopVehicle);
 
DEF_COMMAND(CmdDepotSellAllVehicles);
 
DEF_COMMAND(CmdDepotMassAutoReplace);
 

	
 
/* The master command table */
 
static const Command _command_proc_table[] = {
 
	{CmdBuildRailroadTrack,                  0}, /*   0 */
 
	{CmdRemoveRailroadTrack,                 0}, /*   1 */
 
	{CmdBuildSingleRail,                     0}, /*   2 */
 
	{CmdRemoveSingleRail,                    0}, /*   3 */
 
	{CmdLandscapeClear,                      0}, /*   4 */
 
	{CmdBuildBridge,                         0}, /*   5 */
 
	{CmdBuildRailroadStation,                0}, /*   6 */
 
	{CmdBuildTrainDepot,                     0}, /*   7 */
 
	{CmdBuildSingleSignal,                   0}, /*   8 */
 
	{CmdRemoveSingleSignal,                  0}, /*   9 */
 
	{CmdTerraformLand,                       0}, /*  10 */
 
	{CmdPurchaseLandArea,                    0}, /*  11 */
 
	{CmdSellLandArea,                        0}, /*  12 */
 
	{CmdBuildTunnel,                         0}, /*  13 */
 
	{CmdRemoveFromRailroadStation,           0}, /*  14 */
 
	{CmdConvertRail,                         0}, /*  15 */
 
	{CmdBuildTrainWaypoint,                  0}, /*  16 */
 
	{CmdRenameWaypoint,                      0}, /*  17 */
 
	{CmdRemoveTrainWaypoint,                 0}, /*  18 */
 
	{NULL,                                   0}, /*  19 */
 
	{NULL,                                   0}, /*  20 */
 
	{CmdBuildRoadStop,                       0}, /*  21 */
 
	{NULL,                                   0}, /*  22 */
 
	{CmdBuildLongRoad,                       0}, /*  23 */
 
	{CmdRemoveLongRoad,                      0}, /*  24 */
 
	{CmdBuildRoad,                           0}, /*  25 */
 
	{CmdRemoveRoad,                          0}, /*  26 */
 
	{CmdBuildRoadDepot,                      0}, /*  27 */
 
	{NULL,                                   0}, /*  28 */
 
	{CmdBuildAirport,                        0}, /*  29 */
 
	{CmdBuildDock,                           0}, /*  30 */
 
	{CmdBuildShipDepot,                      0}, /*  31 */
 
	{CmdBuildBuoy,                           0}, /*  32 */
 
	{CmdPlantTree,                           0}, /*  33 */
 
	{CmdBuildRailVehicle,                    0}, /*  34 */
 
	{CmdMoveRailVehicle,                     0}, /*  35 */
 
	{CmdStartStopTrain,                      0}, /*  36 */
 
	{NULL,                                   0}, /*  37 */
 
	{CmdSellRailWagon,                       0}, /*  38 */
 
	{CmdSendTrainToDepot,                    0}, /*  39 */
 
	{CmdForceTrainProceed,                   0}, /*  40 */
 
	{CmdReverseTrainDirection,               0}, /*  41 */
 

	
 
	{CmdModifyOrder,                         0}, /*  42 */
 
	{CmdSkipOrder,                           0}, /*  43 */
 
	{CmdDeleteOrder,                         0}, /*  44 */
 
	{CmdInsertOrder,                         0}, /*  45 */
 

	
 
	{CmdChangeServiceInt,                    0}, /*  46 */
 

	
 
	{CmdBuildIndustry,                       0}, /*  47 */
 
	{CmdBuildCompanyHQ,                      0}, /*  48 */
 
	{CmdSetPlayerFace,                       0}, /*  49 */
 
	{CmdSetPlayerColor,                      0}, /*  50 */
 

	
 
	{CmdIncreaseLoan,                        0}, /*  51 */
 
	{CmdDecreaseLoan,                        0}, /*  52 */
 

	
 
	{CmdWantEnginePreview,                   0}, /*  53 */
 

	
 
	{CmdNameVehicle,                         0}, /*  54 */
 
	{CmdRenameEngine,                        0}, /*  55 */
 

	
 
	{CmdChangeCompanyName,                   0}, /*  56 */
 
	{CmdChangePresidentName,                 0}, /*  57 */
 

	
 
	{CmdRenameStation,                       0}, /*  58 */
 

	
 
	{CmdSellAircraft,                        0}, /*  59 */
 
	{CmdStartStopAircraft,                   0}, /*  60 */
 

	
 
	{CmdBuildAircraft,                       0}, /*  61 */
 
	{CmdSendAircraftToHangar,                0}, /*  62 */
 
	{NULL,                                   0}, /*  63 */
 
	{CmdRefitAircraft,                       0}, /*  64 */
 

	
 
	{CmdPlaceSign,                           0}, /*  65 */
 
	{CmdRenameSign,                          0}, /*  66 */
 

	
 
	{CmdBuildRoadVeh,                        0}, /*  67 */
 
	{CmdStartStopRoadVeh,                    0}, /*  68 */
 
	{CmdSellRoadVeh,                         0}, /*  69 */
 
	{CmdSendRoadVehToDepot,                  0}, /*  70 */
 
	{CmdTurnRoadVeh,                         0}, /*  71 */
 
	{CmdRefitRoadVeh,                        0}, /*  72 */
 

	
 
	{CmdPause,                      CMD_SERVER}, /*  73 */
 

	
 
	{CmdBuyShareInCompany,                   0}, /*  74 */
 
	{CmdSellShareInCompany,                  0}, /*  75 */
 
	{CmdBuyCompany,                          0}, /*  76 */
 

	
 
	{CmdBuildTown,                 CMD_OFFLINE}, /*  77 */
 
	{NULL,                                   0}, /*  78 */
 
	{NULL,                                   0}, /*  79 */
 
	{CmdRenameTown,                 CMD_SERVER}, /*  80 */
 
	{CmdDoTownAction,                        0}, /*  81 */
 

	
 
	{CmdSetRoadDriveSide,           CMD_SERVER}, /*  82 */
 
	{NULL,                                   0}, /*  83 */
 
	{NULL,                                   0}, /*  84 */
 
	{CmdChangeDifficultyLevel,      CMD_SERVER}, /*  85 */
 

	
 
	{CmdStartStopShip,                       0}, /*  86 */
 
	{CmdSellShip,                            0}, /*  87 */
 
	{CmdBuildShip,                           0}, /*  88 */
 
	{CmdSendShipToDepot,                     0}, /*  89 */
 
	{NULL,                                   0}, /*  90 */
 
	{CmdRefitShip,                           0}, /*  91 */
 

	
 
	{NULL,                                   0}, /*  92 */
 
	{NULL,                                   0}, /*  93 */
 
	{NULL,                                   0}, /*  94 */
 
	{NULL,                                   0}, /*  95 */
 
	{NULL,                                   0}, /*  96 */
 
	{NULL,                                   0}, /*  97 */
 

	
 
	{CmdOrderRefit,                          0}, /*  98 */
 
	{CmdCloneOrder,                          0}, /*  99 */
 

	
 
	{CmdClearArea,                           0}, /* 100 */
 
	{NULL,                                   0}, /* 101 */
 

	
 
	{CmdMoneyCheat,                CMD_OFFLINE}, /* 102 */
 
	{CmdBuildCanal,                          0}, /* 103 */
 
	{CmdPlayerCtrl,                          0}, /* 104 */
 

	
 
	{CmdLevelLand,                           0}, /* 105 */
 

	
 
	{CmdRefitRailVehicle,                    0}, /* 106 */
 
	{CmdRestoreOrderIndex,                   0}, /* 107 */
 
	{CmdBuildLock,                           0}, /* 108 */
 
	{NULL,                                   0}, /* 109 */
 
	{CmdBuildSignalTrack,                    0}, /* 110 */
 
	{CmdRemoveSignalTrack,                   0}, /* 111 */
 
	{NULL,                                   0}, /* 112 */
 
	{CmdGiveMoney,                           0}, /* 113 */
 
	{CmdChangePatchSetting,         CMD_SERVER}, /* 114 */
 
	{CmdSetAutoReplace,                      0}, /* 115 */
 
	{CmdCloneVehicle,                        0}, /* 116 */
 
	{CmdMassStartStopVehicle,                0}, /* 117 */
 
	{CmdDepotSellAllVehicles,                0}, /* 118 */
 
	{CmdDepotMassAutoReplace,                0}, /* 119 */
 
};
 

	
 
/* This function range-checks a cmd, and checks if the cmd is not NULL */
 
bool IsValidCommand(uint cmd)
 
{
 
	cmd &= 0xFF;
 

	
 
	return
 
		cmd < lengthof(_command_proc_table) &&
 
		_command_proc_table[cmd].proc != NULL;
 
}
 

	
 
byte GetCommandFlags(uint cmd)
 
{
 
	return _command_proc_table[cmd & 0xFF].flags;
 
}
 

	
 

	
 
static int _docommand_recursive;
 

	
 
int32 DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
 
{
 
	int32 res;
 
	CommandProc *proc;
 

	
 
	/* Do not even think about executing out-of-bounds tile-commands */
 
	if (tile >= MapSize()) {
 
		_cmd_text = NULL;
 
		return CMD_ERROR;
 
	}
 

	
 
	proc = _command_proc_table[procc].proc;
 

	
 
	if (_docommand_recursive == 0) _error_message = INVALID_STRING_ID;
 

	
 
	_docommand_recursive++;
 

	
 
	// only execute the test call if it's toplevel, or we're not execing.
 
	if (_docommand_recursive == 1 || !(flags & DC_EXEC) || (flags & DC_FORCETEST) ) {
 
		res = proc(tile, flags & ~DC_EXEC, p1, p2);
 
		if (CmdFailed(res)) {
 
			if (res & 0xFFFF) _error_message = res & 0xFFFF;
 
			goto error;
 
		}
 

	
 
		if (_docommand_recursive == 1 &&
 
				!(flags & DC_QUERY_COST) &&
 
				res != 0 &&
 
				!CheckPlayerHasMoney(res)) {
 
			goto error;
 
		}
 

	
 
		if (!(flags & DC_EXEC)) {
 
			_docommand_recursive--;
 
			_cmd_text = NULL;
 
			return res;
 
		}
 
	}
 

	
 
	/* Execute the command here. All cost-relevant functions set the expenses type
 
	 * themselves with "SET_EXPENSES_TYPE(...);" at the beginning of the function */
 
	res = proc(tile, flags, p1, p2);
 
	if (CmdFailed(res)) {
 
		if (res & 0xFFFF) _error_message = res & 0xFFFF;
 
error:
 
		_docommand_recursive--;
 
		_cmd_text = NULL;
 
		return CMD_ERROR;
 
	}
 

	
 
	// if toplevel, subtract the money.
 
	if (--_docommand_recursive == 0) {
 
		SubtractMoneyFromPlayer(res);
 
		// XXX - Old AI hack which doesn't use DoCommandDP; update last build coord of player
 
		if (tile != 0 && IsValidPlayer(_current_player)) {
 
			GetPlayer(_current_player)->last_build_coordinate = tile;
 
		}
 
	}
 

	
 
	_cmd_text = NULL;
 
	return res;
 
}
 

	
 
int32 GetAvailableMoneyForCommand(void)
 
{
 
	PlayerID pid = _current_player;
 
	if (!IsValidPlayer(pid)) return 0x7FFFFFFF; // max int
 
	return GetPlayer(pid)->player_money;
 
}
 

	
 
// toplevel network safe docommand function for the current player. must not be called recursively.
 
// the callback is called when the command succeeded or failed.
 
bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd)
 
{
 
	int32 res = 0,res2;
 
	CommandProc *proc;
 
	uint32 flags;
 
	bool notest;
 
	StringID error_part1;
 

	
 
	int x = TileX(tile) * TILE_SIZE;
 
	int y = TileY(tile) * TILE_SIZE;
 

	
 
	/* Do not even think about executing out-of-bounds tile-commands */
 
	if (tile >= MapSize()) {
 
		_cmd_text = NULL;
 
		return false;
 
	}
 

	
 
	assert(_docommand_recursive == 0);
 

	
 
	_error_message = INVALID_STRING_ID;
 
	error_part1 = GB(cmd, 16, 16);
 
	_additional_cash_required = 0;
 

	
 
	/** Spectator has no rights except for the (dedicated) server which
 
	 * is/can be a spectator but as the server it can do anything */
 
	if (_current_player == PLAYER_SPECTATOR && !_network_server) {
 
		ShowErrorMessage(_error_message, error_part1, x, y);
 
		_cmd_text = NULL;
 
		return false;
 
	}
 

	
 
	flags = 0;
 
	if (cmd & CMD_AUTO) flags |= DC_AUTO;
 
	if (cmd & CMD_NO_WATER) flags |= DC_NO_WATER;
 

	
 
	// get pointer to command handler
 
	assert((cmd & 0xFF) < lengthof(_command_proc_table));
 
	proc = _command_proc_table[cmd & 0xFF].proc;
 
	if (proc == NULL) {
 
		_cmd_text = NULL;
 
		return false;
 
	}
 

	
 
	// Some commands have a different output in dryrun than the realrun
 
	//  e.g.: if you demolish a whole town, the dryrun would say okay.
 
	//  but by really destroying, your rating drops and at a certain point
 
	//  it will fail. so res and res2 are different
 
	// CMD_REMOVE_ROAD: This command has special local authority
 
	// restrictions which may cause the test run to fail (the previous
 
	// road fragments still stay there and the town won't let you
 
	// disconnect the road system), but the exec will succeed and this
 
	// fact will trigger an assertion failure. --pasky
 
	notest =
 
		(cmd & 0xFF) == CMD_CLEAR_AREA ||
 
		(cmd & 0xFF) == CMD_CONVERT_RAIL ||
 
		(cmd & 0xFF) == CMD_LEVEL_LAND ||
 
		(cmd & 0xFF) == CMD_REMOVE_ROAD ||
 
		(cmd & 0xFF) == CMD_REMOVE_LONG_ROAD;
 

	
 
	_docommand_recursive = 1;
 

	
 
	// cost estimation only?
 
	if (!IsGeneratingWorld() &&
 
			_shift_pressed &&
 
			IsLocalPlayer() &&
 
			!(cmd & (CMD_NETWORK_COMMAND | CMD_SHOW_NO_ERROR)) &&
 
			(cmd & 0xFF) != CMD_PAUSE) {
 
		// estimate the cost.
 
		res = proc(tile, flags, p1, p2);
 
		if (CmdFailed(res)) {
 
			if (res & 0xFFFF) _error_message = res & 0xFFFF;
 
			ShowErrorMessage(_error_message, error_part1, x, y);
 
		} else {
 
			ShowEstimatedCostOrIncome(res, x, y);
 
		}
 

	
 
		_docommand_recursive = 0;
 
		_cmd_text = NULL;
 
		return false;
 
	}
 

	
 

	
 
	if (!((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) {
 
		// first test if the command can be executed.
 
		res = proc(tile, flags, p1, p2);
 
		if (CmdFailed(res)) {
 
			if (res & 0xFFFF) _error_message = res & 0xFFFF;
 
			goto show_error;
 
		}
 
		// no money? Only check if notest is off
 
		if (!notest && res != 0 && !CheckPlayerHasMoney(res)) goto show_error;
 
	}
 

	
 
#ifdef ENABLE_NETWORK
 
	/** If we are in network, and the command is not from the network
 
	 * send it to the command-queue and abort execution
 
	 * If we are a dedicated server temporarily switch local player, otherwise
 
	 * the other parties won't be able to execute our command and will desync.
 
	 * We also need to do this if the server's company has gone bankrupt
 
	 * @todo Rewrite (dedicated) server to something more than a dirty hack!
 
	 */
 
	if (_networking && !(cmd & CMD_NETWORK_COMMAND)) {
 
		PlayerID pbck = _local_player;
 
		if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = 0;
 
		NetworkSend_Command(tile, p1, p2, cmd, callback);
 
		if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = pbck;
 
		_docommand_recursive = 0;
 
		_cmd_text = NULL;
 
		return true;
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	// update last build coordinate of player.
 
	if (tile != 0 && IsValidPlayer(_current_player)) {
 
		GetPlayer(_current_player)->last_build_coordinate = tile;
 
	}
 

	
 
	/* Actually try and execute the command. If no cost-type is given
 
	 * use the construction one */
 
	_yearly_expenses_type = EXPENSES_CONSTRUCTION;
 
	res2 = proc(tile, flags | DC_EXEC, p1, p2);
 

	
 
	// If notest is on, it means the result of the test can be different than
 
	//   the real command.. so ignore the test
 
	if (!notest && !((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) {
 
		assert(res == res2); // sanity check
 
	} else {
 
		if (CmdFailed(res2)) {
 
			if (res2 & 0xFFFF) _error_message = res2 & 0xFFFF;
 
			goto show_error;
 
		}
 
	}
 

	
 
	SubtractMoneyFromPlayer(res2);
 

	
 
	if (IsLocalPlayer() && _game_mode != GM_EDITOR) {
 
		if (res2 != 0) ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2);
 
		if (_additional_cash_required) {
 
			SetDParam(0, _additional_cash_required);
 
			ShowErrorMessage(STR_0003_NOT_ENOUGH_CASH_REQUIRES, error_part1, x,y);
 
			if (res2 == 0) goto callb_err;
 
		}
 
	}
 

	
 
	_docommand_recursive = 0;
 

	
 
	if (callback) callback(true, tile, p1, p2);
 
	_cmd_text = NULL;
 
	return true;
 

	
 
show_error:
 
	// show error message if the command fails?
 
	if (IsLocalPlayer() && error_part1 != 0) {
 
		ShowErrorMessage(_error_message, error_part1, x,y);
 
	}
 

	
 
callb_err:
 
	_docommand_recursive = 0;
 

	
 
	if (callback) callback(false, tile, p1, p2);
 
	_cmd_text = NULL;
 
	return false;
 
}
src/console.c
Show inline comments
 
deleted file
src/console.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "player.h"
 
#include "variables.h"
 
#include "string.h"
 
#include <stdarg.h>
 
#include <string.h>
 
#include "console.h"
 
#include "network/network.h"
 
#include "network/network_data.h"
 
#include "network/network_server.h"
 

	
 
#define ICON_BUFFER 79
 
#define ICON_HISTORY_SIZE 20
 
#define ICON_LINE_HEIGHT 12
 
#define ICON_RIGHT_BORDERWIDTH 10
 
#define ICON_BOTTOM_BORDERWIDTH 12
 
#define ICON_MAX_ALIAS_LINES 40
 
#define ICON_TOKEN_COUNT 20
 

	
 
// ** main console ** //
 
static char *_iconsole_buffer[ICON_BUFFER + 1];
 
static uint16 _iconsole_cbuffer[ICON_BUFFER + 1];
 
static Textbuf _iconsole_cmdline;
 

	
 
// ** stdlib ** //
 
byte _stdlib_developer = 1;
 
bool _stdlib_con_developer = false;
 
FILE *_iconsole_output_file;
 

	
 
// ** main console cmd buffer
 
static char *_iconsole_history[ICON_HISTORY_SIZE];
 
static byte _iconsole_historypos;
 

	
 
/* *************** */
 
/*  end of header  */
 
/* *************** */
 

	
 
static void IConsoleClearCommand(void)
 
{
 
	memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
 
	_iconsole_cmdline.length = 0;
 
	_iconsole_cmdline.width = 0;
 
	_iconsole_cmdline.caretpos = 0;
 
	_iconsole_cmdline.caretxoffs = 0;
 
	SetWindowDirty(FindWindowById(WC_CONSOLE, 0));
 
}
 

	
 
static inline void IConsoleResetHistoryPos(void) {_iconsole_historypos = ICON_HISTORY_SIZE - 1;}
 

	
 

	
 
static void IConsoleHistoryAdd(const char *cmd);
 
static void IConsoleHistoryNavigate(int direction);
 

	
 
// ** console window ** //
 
static void IConsoleWndProc(Window *w, WindowEvent *e)
 
{
 
	static byte iconsole_scroll = ICON_BUFFER;
 

	
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			int i = iconsole_scroll;
 
			int max = (w->height / ICON_LINE_HEIGHT) - 1;
 
			int delta = 0;
 
			GfxFillRect(w->left, w->top, w->width, w->height - 1, 0);
 
			while ((i > 0) && (i > iconsole_scroll - max) && (_iconsole_buffer[i] != NULL)) {
 
				DoDrawString(_iconsole_buffer[i], 5,
 
					w->height - (iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]);
 
				i--;
 
			}
 
			/* If the text is longer than the window, don't show the starting ']' */
 
			delta = w->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH;
 
			if (delta > 0) {
 
				DoDrawString("]", 5, w->height - ICON_LINE_HEIGHT, _icolour_cmd);
 
				delta = 0;
 
			}
 

	
 
			DoDrawString(_iconsole_cmdline.buf, 10 + delta, w->height - ICON_LINE_HEIGHT, _icolour_cmd);
 

	
 
			if (_iconsole_cmdline.caret)
 
				DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, w->height - ICON_LINE_HEIGHT, 12);
 
			break;
 
		}
 
		case WE_MOUSELOOP:
 
			if (HandleCaret(&_iconsole_cmdline))
 
				SetWindowDirty(w);
 
			break;
 
		case WE_DESTROY:
 
			_iconsole_mode = ICONSOLE_CLOSED;
 
			break;
 
		case WE_KEYPRESS:
 
			e->we.keypress.cont = false;
 
			switch (e->we.keypress.keycode) {
 
				case WKC_UP:
 
					IConsoleHistoryNavigate(+1);
 
					SetWindowDirty(w);
 
					break;
 
				case WKC_DOWN:
 
					IConsoleHistoryNavigate(-1);
 
					SetWindowDirty(w);
 
					break;
 
				case WKC_SHIFT | WKC_PAGEUP:
 
					if (iconsole_scroll - (w->height / ICON_LINE_HEIGHT) - 1 < 0) {
 
						iconsole_scroll = 0;
 
					} else {
 
						iconsole_scroll -= (w->height / ICON_LINE_HEIGHT) - 1;
 
					}
 
					SetWindowDirty(w);
 
					break;
 
				case WKC_SHIFT | WKC_PAGEDOWN:
 
					if (iconsole_scroll + (w->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER) {
 
						iconsole_scroll = ICON_BUFFER;
 
					} else {
 
						iconsole_scroll += (w->height / ICON_LINE_HEIGHT) - 1;
 
					}
 
					SetWindowDirty(w);
 
					break;
 
				case WKC_SHIFT | WKC_UP:
 
					if (iconsole_scroll <= 0) {
 
						iconsole_scroll = 0;
 
					} else {
 
						--iconsole_scroll;
 
					}
 
					SetWindowDirty(w);
 
					break;
 
				case WKC_SHIFT | WKC_DOWN:
 
					if (iconsole_scroll >= ICON_BUFFER) {
 
						iconsole_scroll = ICON_BUFFER;
 
					} else {
 
						++iconsole_scroll;
 
					}
 
					SetWindowDirty(w);
 
					break;
 
				case WKC_BACKQUOTE:
 
					IConsoleSwitch();
 
					break;
 
				case WKC_RETURN: case WKC_NUM_ENTER:
 
					IConsolePrintF(_icolour_cmd, "] %s", _iconsole_cmdline.buf);
 
					IConsoleHistoryAdd(_iconsole_cmdline.buf);
 

	
 
					IConsoleCmdExec(_iconsole_cmdline.buf);
 
					IConsoleClearCommand();
 
					break;
 
				case WKC_CTRL | WKC_RETURN:
 
					_iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
 
					IConsoleResize(w);
 
					MarkWholeScreenDirty();
 
					break;
 
				case (WKC_CTRL | 'V'):
 
					if (InsertTextBufferClipboard(&_iconsole_cmdline)) {
 
						IConsoleResetHistoryPos();
 
						SetWindowDirty(w);
 
					}
 
					break;
 
				case (WKC_CTRL | 'L'):
 
					IConsoleCmdExec("clear");
 
					break;
 
				case (WKC_CTRL | 'U'):
 
					DeleteTextBufferAll(&_iconsole_cmdline);
 
					SetWindowDirty(w);
 
					break;
 
				case WKC_BACKSPACE: case WKC_DELETE:
 
					if (DeleteTextBufferChar(&_iconsole_cmdline, e->we.keypress.keycode)) {
 
						IConsoleResetHistoryPos();
 
						SetWindowDirty(w);
 
					}
 
					break;
 
				case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
 
					if (MoveTextBufferPos(&_iconsole_cmdline, e->we.keypress.keycode)) {
 
						IConsoleResetHistoryPos();
 
						SetWindowDirty(w);
 
					}
 
					break;
 
				default:
 
					if (IsValidChar(e->we.keypress.key, CS_ALPHANUMERAL)) {
 
						iconsole_scroll = ICON_BUFFER;
 
						InsertTextBufferChar(&_iconsole_cmdline, e->we.keypress.key);
 
						IConsoleResetHistoryPos();
 
						SetWindowDirty(w);
 
					} else {
 
						e->we.keypress.cont = true;
 
					}
 
			break;
 
		}
 
	}
 
}
 

	
 
static const Widget _iconsole_window_widgets[] = {
 
	{WIDGETS_END}
 
};
 

	
 
static const WindowDesc _iconsole_window_desc = {
 
	0, 0, 2, 2,
 
	WC_CONSOLE, 0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_iconsole_window_widgets,
 
	IConsoleWndProc,
 
};
 

	
 
void IConsoleInit(void)
 
{
 
	extern const char _openttd_revision[];
 
	_iconsole_output_file = NULL;
 
	_icolour_def  =  1;
 
	_icolour_err  =  3;
 
	_icolour_warn = 13;
 
	_icolour_dbg  =  5;
 
	_icolour_cmd  =  2;
 
	_iconsole_historypos = ICON_HISTORY_SIZE - 1;
 
	_iconsole_mode = ICONSOLE_CLOSED;
 

	
 
#ifdef ENABLE_NETWORK /* Initialize network only variables */
 
	_redirect_console_to_client = 0;
 
#endif
 

	
 
	memset(_iconsole_history, 0, sizeof(_iconsole_history));
 
	memset(_iconsole_buffer, 0, sizeof(_iconsole_buffer));
 
	memset(_iconsole_cbuffer, 0, sizeof(_iconsole_cbuffer));
 
	_iconsole_cmdline.buf = calloc(ICON_CMDLN_SIZE, sizeof(*_iconsole_cmdline.buf)); // create buffer and zero it
 
	_iconsole_cmdline.maxlength = ICON_CMDLN_SIZE;
 

	
 
	IConsolePrintF(13, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
 
	IConsolePrint(12,  "------------------------------------");
 
	IConsolePrint(12,  "use \"help\" for more information");
 
	IConsolePrint(12,  "");
 
	IConsoleStdLibRegister();
 
	IConsoleClearCommand();
 
	IConsoleHistoryAdd("");
 
}
 

	
 
void IConsoleClearBuffer(void)
 
{
 
	uint i;
 
	for (i = 0; i <= ICON_BUFFER; i++) {
 
		free(_iconsole_buffer[i]);
 
		_iconsole_buffer[i] = NULL;
 
	}
 
}
 

	
 
static void IConsoleClear(void)
 
{
 
	free(_iconsole_cmdline.buf);
 
	IConsoleClearBuffer();
 
}
 

	
 
static void IConsoleWriteToLogFile(const char *string)
 
{
 
	if (_iconsole_output_file != NULL) {
 
		// if there is an console output file ... also print it there
 
		fwrite(string, strlen(string), 1, _iconsole_output_file);
 
		fwrite("\n", 1, 1, _iconsole_output_file);
 
	}
 
}
 

	
 
bool CloseConsoleLogIfActive(void)
 
{
 
	if (_iconsole_output_file != NULL) {
 
		IConsolePrintF(_icolour_def, "file output complete");
 
		fclose(_iconsole_output_file);
 
		_iconsole_output_file = NULL;
 
		return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
void IConsoleFree(void)
 
{
 
	IConsoleClear();
 
	CloseConsoleLogIfActive();
 
}
 

	
 
void IConsoleResize(Window *w)
 
{
 
	switch (_iconsole_mode) {
 
		case ICONSOLE_OPENED:
 
			w->height = _screen.height / 3;
 
			w->width = _screen.width;
 
			break;
 
		case ICONSOLE_FULL:
 
			w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
 
			w->width = _screen.width;
 
			break;
 
		default: return;
 
	}
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
void IConsoleSwitch(void)
 
{
 
	switch (_iconsole_mode) {
 
		case ICONSOLE_CLOSED: {
 
			Window *w = AllocateWindowDesc(&_iconsole_window_desc);
 
			w->height = _screen.height / 3;
 
			w->width = _screen.width;
 
			_iconsole_mode = ICONSOLE_OPENED;
 
			SETBIT(_no_scroll, SCROLL_CON); // override cursor arrows; the gamefield will not scroll
 
		} break;
 
		case ICONSOLE_OPENED: case ICONSOLE_FULL:
 
			DeleteWindowById(WC_CONSOLE, 0);
 
			_iconsole_mode = ICONSOLE_CLOSED;
 
			CLRBIT(_no_scroll, SCROLL_CON);
 
			break;
 
	}
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
void IConsoleClose(void) {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();}
 
void IConsoleOpen(void)  {if (_iconsole_mode == ICONSOLE_CLOSED) IConsoleSwitch();}
 

	
 
/**
 
 * Add the entered line into the history so you can look it back
 
 * scroll, etc. Put it to the beginning as it is the latest text
 
 * @param cmd Text to be entered into the 'history'
 
 */
 
static void IConsoleHistoryAdd(const char *cmd)
 
{
 
	free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
 

	
 
	memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
 
	_iconsole_history[0] = strdup(cmd);
 
	IConsoleResetHistoryPos();
 
}
 

	
 
/**
 
 * Navigate Up/Down in the history of typed commands
 
 * @param direction Go further back in history (+1), go to recently typed commands (-1)
 
 */
 
static void IConsoleHistoryNavigate(int direction)
 
{
 
	int i = _iconsole_historypos + direction;
 

	
 
	// watch out for overflows, just wrap around
 
	if (i < 0) i = ICON_HISTORY_SIZE - 1;
 
	if (i >= ICON_HISTORY_SIZE) i = 0;
 

	
 
	if (direction > 0)
 
		if (_iconsole_history[i] == NULL) i = 0;
 

	
 
	if (direction < 0) {
 
		while (i > 0 && _iconsole_history[i] == NULL) i--;
 
	}
 

	
 
	_iconsole_historypos = i;
 
	IConsoleClearCommand();
 
	// copy history to 'command prompt / bash'
 
	assert(_iconsole_history[i] != NULL && IS_INT_INSIDE(i, 0, ICON_HISTORY_SIZE));
 
	ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxlength);
 
	UpdateTextBufferSize(&_iconsole_cmdline);
 
}
 

	
 
/**
 
 * Handle the printing of text entered into the console or redirected there
 
 * by any other means. Text can be redirected to other players in a network game
 
 * as well as to a logfile. If the network server is a dedicated server, all activities
 
 * are also logged. All lines to print are added to a temporary buffer which can be
 
 * used as a history to print them onscreen
 
 * @param color_code the colour of the command. Red in case of errors, etc.
 
 * @param string the message entered or output on the console (notice, error, etc.)
 
 */
 
void IConsolePrint(uint16 color_code, const char *string)
 
{
 
	char *str;
 
#ifdef ENABLE_NETWORK
 
	if (_redirect_console_to_client != 0) {
 
		/* Redirect the string to the client */
 
		SEND_COMMAND(PACKET_SERVER_RCON)(NetworkFindClientStateFromIndex(_redirect_console_to_client), color_code, string);
 
		return;
 
	}
 
#endif
 

	
 
	/* Create a copy of the string, strip if of colours and invalid
 
	 * characters and (when applicable) assign it to the console buffer */
 
	str = strdup(string);
 
	str_strip_colours(str);
 
	str_validate(str);
 

	
 
	if (_network_dedicated) {
 
		printf("%s\n", str);
 
		IConsoleWriteToLogFile(str);
 
		free(str); // free duplicated string since it's not used anymore
 
		return;
 
	}
 

	
 
	/* move up all the strings in the buffer one place and do the same for colour
 
	 * to accomodate for the new command/message */
 
	free(_iconsole_buffer[0]);
 
	memmove(&_iconsole_buffer[0], &_iconsole_buffer[1], sizeof(_iconsole_buffer[0]) * ICON_BUFFER);
 
	_iconsole_buffer[ICON_BUFFER] = str;
 

	
 
	memmove(&_iconsole_cbuffer[0], &_iconsole_cbuffer[1], sizeof(_iconsole_cbuffer[0]) * ICON_BUFFER);
 
	_iconsole_cbuffer[ICON_BUFFER] = color_code;
 

	
 
	IConsoleWriteToLogFile(_iconsole_buffer[ICON_BUFFER]);
 

	
 
	SetWindowDirty(FindWindowById(WC_CONSOLE, 0));
 
}
 

	
 
/**
 
 * Handle the printing of text entered into the console or redirected there
 
 * by any other means. Uses printf() style format, for more information look
 
 * at @IConsolePrint()
 
 */
 
void CDECL IConsolePrintF(uint16 color_code, const char *s, ...)
 
{
 
	va_list va;
 
	char buf[ICON_MAX_STREAMSIZE];
 

	
 
	va_start(va, s);
 
	vsnprintf(buf, sizeof(buf), s, va);
 
	va_end(va);
 

	
 
	IConsolePrint(color_code, buf);
 
}
 

	
 
/**
 
 * It is possible to print debugging information to the console,
 
 * which is achieved by using this function. Can only be used by
 
 * @debug() in debug.c. You need at least a level 2 (developer) for debugging
 
 * messages to show up
 
 * @param dbg debugging category
 
 * @param string debugging message
 
 */
 
void IConsoleDebug(const char *dbg, const char *string)
 
{
 
	if (_stdlib_developer > 1)
 
		IConsolePrintF(_icolour_dbg, "dbg: [%s] %s", dbg, string);
 
}
 

	
 
/**
 
 * It is possible to print warnings to the console. These are mostly
 
 * errors or mishaps, but non-fatal. You need at least a level 1 (developer) for
 
 * debugging messages to show up
 
 */
 
void IConsoleWarning(const char *string)
 
{
 
	if (_stdlib_developer > 0)
 
		IConsolePrintF(_icolour_warn, "WARNING: %s", string);
 
}
 

	
 
/**
 
 * It is possible to print error information to the console. This can include
 
 * game errors, or errors in general you would want the user to notice
 
 */
 
void IConsoleError(const char *string)
 
{
 
	IConsolePrintF(_icolour_err, "ERROR: %s", string);
 
}
 

	
 
/**
 
 * Change a string into its number representation. Supports
 
 * decimal and hexadecimal numbers as well as 'on'/'off' 'true'/'false'
 
 * @param *value the variable a successfull conversion will be put in
 
 * @param *arg the string to be converted
 
 * @return Return true on success or false on failure
 
 */
 
bool GetArgumentInteger(uint32 *value, const char *arg)
 
{
 
	char *endptr;
 

	
 
	if (strcmp(arg, "on") == 0 || strcmp(arg, "true") == 0) {
 
		*value = 1;
 
		return true;
 
	}
 
	if (strcmp(arg, "off") == 0 || strcmp(arg, "false") == 0) {
 
		*value = 0;
 
		return true;
 
	}
 

	
 
	*value = strtoul(arg, &endptr, 0);
 
	return arg != endptr;
 
}
 

	
 
// * ************************* * //
 
// * hooking code              * //
 
// * ************************* * //
 
/**
 
 * General internal hooking code that is the same for both commands and variables
 
 * @param hooks @IConsoleHooks structure that will be set according to
 
 * @param type type access trigger
 
 * @param proc function called when the hook criteria is met
 
 */
 
static void IConsoleHookAdd(IConsoleHooks *hooks, IConsoleHookTypes type, IConsoleHook *proc)
 
{
 
	if (hooks == NULL || proc == NULL) return;
 

	
 
	switch (type) {
 
		case ICONSOLE_HOOK_ACCESS:
 
			hooks->access = proc;
 
			break;
 
		case ICONSOLE_HOOK_PRE_ACTION:
 
			hooks->pre = proc;
 
			break;
 
		case ICONSOLE_HOOK_POST_ACTION:
 
			hooks->post = proc;
 
			break;
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
/**
 
 * Handle any special hook triggers. If the hook type is met check if
 
 * there is a function associated with that and if so, execute it
 
 * @param hooks @IConsoleHooks structure that will be checked
 
 * @param type type of hook, trigger that needs to be activated
 
 * @return true on a successfull execution of the hook command or if there
 
 * is no hook/trigger present at all. False otherwise
 
 */
 
static bool IConsoleHookHandle(const IConsoleHooks *hooks, IConsoleHookTypes type)
 
{
 
	IConsoleHook *proc = NULL;
 
	if (hooks == NULL) return false;
 

	
 
	switch (type) {
 
		case ICONSOLE_HOOK_ACCESS:
 
			proc = hooks->access;
 
			break;
 
		case ICONSOLE_HOOK_PRE_ACTION:
 
			proc = hooks->pre;
 
			break;
 
		case ICONSOLE_HOOK_POST_ACTION:
 
			proc = hooks->post;
 
			break;
 
		default: NOT_REACHED();
 
	}
 

	
 
	return (proc == NULL) ? true : proc();
 
}
 

	
 
/**
 
 * Add a hook to a command that will be triggered at certain points
 
 * @param name name of the command that the hook is added to
 
 * @param type type of hook that is added (ACCESS, BEFORE and AFTER change)
 
 * @param proc function called when the hook criteria is met
 
 */
 
void IConsoleCmdHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc)
 
{
 
	IConsoleCmd *cmd = IConsoleCmdGet(name);
 
	if (cmd == NULL) return;
 
	IConsoleHookAdd(&cmd->hook, type, proc);
 
}
 

	
 
/**
 
 * Add a hook to a variable that will be triggered at certain points
 
 * @param name name of the variable that the hook is added to
 
 * @param type type of hook that is added (ACCESS, BEFORE and AFTER change)
 
 * @param proc function called when the hook criteria is met
 
 */
 
void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc)
 
{
 
	IConsoleVar *var = IConsoleVarGet(name);
 
	if (var == NULL) return;
 
	IConsoleHookAdd(&var->hook, type, proc);
 
}
 

	
 
/**
 
 * Perhaps ugly macro, but this saves us the trouble of writing the same function
 
 * three types, just with different variables. Yes, templates would be handy. It was
 
 * either this define or an even more ugly void* magic function
 
 */
 
#define IConsoleAddSorted(_base, item_new, IConsoleType, type)                 \
 
{                                                                              \
 
	IConsoleType *item, *item_before;                                            \
 
	/* first command */                                                          \
 
	if (_base == NULL) {                                                         \
 
		_base = item_new;                                                          \
 
		return;                                                                    \
 
	}                                                                            \
 
                                                                               \
 
	item_before = NULL;                                                          \
 
	item = _base;                                                                \
 
                                                                               \
 
	/* BEGIN - Alphabetically insert the commands into the linked list */        \
 
	while (item != NULL) {                                                       \
 
		int i = strcmp(item->name, item_new->name);                                \
 
		if (i == 0) {                                                              \
 
			IConsoleError(type " with this name already exists; insertion aborted"); \
 
			free(item_new);                                                          \
 
			return;                                                                  \
 
		}                                                                          \
 
                                                                               \
 
		if (i > 0) break; /* insert at this position */                            \
 
                                                                               \
 
		item_before = item;                                                        \
 
		item = item->next;                                                         \
 
	}                                                                            \
 
                                                                               \
 
	if (item_before == NULL) {                                                   \
 
		_base = item_new;                                                          \
 
	} else {                                                                     \
 
		item_before->next = item_new;                                              \
 
  }                                                                            \
 
                                                                               \
 
	item_new->next = item;                                                       \
 
	/* END - Alphabetical insert */                                              \
 
}
 

	
 
/**
 
 * Register a new command to be used in the console
 
 * @param name name of the command that will be used
 
 * @param proc function that will be called upon execution of command
 
 */
 
void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc)
 
{
 
	char *new_cmd = strdup(name);
 
	IConsoleCmd *item_new = malloc(sizeof(IConsoleCmd));
 

	
 
	item_new->next = NULL;
 
	item_new->proc = proc;
 
	item_new->name = new_cmd;
 

	
 
	item_new->hook.access = NULL;
 
	item_new->hook.pre = NULL;
 
	item_new->hook.post = NULL;
 

	
 
	IConsoleAddSorted(_iconsole_cmds, item_new, IConsoleCmd, "a command");
 
}
 

	
 
/**
 
 * Find the command pointed to by its string
 
 * @param name command to be found
 
 * @return return Cmdstruct of the found command, or NULL on failure
 
 */
 
IConsoleCmd *IConsoleCmdGet(const char *name)
 
{
 
	IConsoleCmd *item;
 

	
 
	for (item = _iconsole_cmds; item != NULL; item = item->next) {
 
		if (strcmp(item->name, name) == 0) return item;
 
	}
 
	return NULL;
 
}
 

	
 
/**
 
 * Register a an alias for an already existing command in the console
 
 * @param name name of the alias that will be used
 
 * @param cmd name of the command that 'name' will be alias of
 
 */
 
void IConsoleAliasRegister(const char *name, const char *cmd)
 
{
 
	char *new_alias = strdup(name);
 
	char *cmd_aliased = strdup(cmd);
 
	IConsoleAlias *item_new = malloc(sizeof(IConsoleAlias));
 

	
 
	item_new->next = NULL;
 
	item_new->cmdline = cmd_aliased;
 
	item_new->name = new_alias;
 

	
 
	IConsoleAddSorted(_iconsole_aliases, item_new, IConsoleAlias, "an alias");
 
}
 

	
 
/**
 
 * Find the alias pointed to by its string
 
 * @param name alias to be found
 
 * @return return Aliasstruct of the found alias, or NULL on failure
 
 */
 
IConsoleAlias *IConsoleAliasGet(const char *name)
 
{
 
	IConsoleAlias* item;
 

	
 
	for (item = _iconsole_aliases; item != NULL; item = item->next) {
 
		if (strcmp(item->name, name) == 0) return item;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/** copy in an argument into the aliasstream */
 
static inline int IConsoleCopyInParams(char *dst, const char *src, uint bufpos)
 
{
 
	int len = min(ICON_MAX_STREAMSIZE - bufpos, (uint)strlen(src));
 
	strncpy(dst, src, len);
 

	
 
	return len;
 
}
 

	
 
/**
 
 * An alias is just another name for a command, or for more commands
 
 * Execute it as well.
 
 * @param *alias is the alias of the command
 
 * @param tokencount the number of parameters passed
 
 * @param *tokens are the parameters given to the original command (0 is the first param)
 
 */
 
static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT])
 
{
 
	const char *cmdptr;
 
	char *aliases[ICON_MAX_ALIAS_LINES], aliasstream[ICON_MAX_STREAMSIZE];
 
	uint i;
 
	uint a_index, astream_i;
 

	
 
	memset(&aliases, 0, sizeof(aliases));
 
	memset(&aliasstream, 0, sizeof(aliasstream));
 

	
 
	if (_stdlib_con_developer)
 
		IConsolePrintF(_icolour_dbg, "condbg: requested command is an alias; parsing...");
 

	
 
	aliases[0] = aliasstream;
 
	for (cmdptr = alias->cmdline, a_index = 0, astream_i = 0; *cmdptr != '\0'; cmdptr++) {
 
		if (a_index >= lengthof(aliases) || astream_i >= lengthof(aliasstream)) break;
 

	
 
		switch (*cmdptr) {
 
		case '\'': /* ' will double for "" */
 
			aliasstream[astream_i++] = '"';
 
			break;
 
		case ';': /* Cmd seperator, start new command */
 
			aliasstream[astream_i] = '\0';
 
			aliases[++a_index] = &aliasstream[++astream_i];
 
			cmdptr++;
 
			break;
 
		case '%': /* Some or all parameters */
 
			cmdptr++;
 
			switch (*cmdptr) {
 
			case '+': { /* All parameters seperated: "[param 1]" "[param 2]" */
 
				for (i = 0; i != tokencount; i++) {
 
					aliasstream[astream_i++] = '"';
 
					astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
 
					aliasstream[astream_i++] = '"';
 
					aliasstream[astream_i++] = ' ';
 
				}
 
			} break;
 
			case '!': { /* Merge the parameters to one: "[param 1] [param 2] [param 3...]" */
 
				aliasstream[astream_i++] = '"';
 
				for (i = 0; i != tokencount; i++) {
 
					astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
 
					aliasstream[astream_i++] = ' ';
 
				}
 
				aliasstream[astream_i++] = '"';
 

	
 
			} break;
 
				default: { /* One specific parameter: %A = [param 1] %B = [param 2] ... */
 
				int param = *cmdptr - 'A';
 

	
 
				if (param < 0 || param >= tokencount) {
 
					IConsoleError("too many or wrong amount of parameters passed to alias, aborting");
 
					IConsolePrintF(_icolour_warn, "Usage of alias '%s': %s", alias->name, alias->cmdline);
 
					return;
 
				}
 

	
 
				aliasstream[astream_i++] = '"';
 
				astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[param], astream_i);
 
				aliasstream[astream_i++] = '"';
 
			} break;
 
			} break;
 

	
 
		default:
 
			aliasstream[astream_i++] = *cmdptr;
 
			break;
 
		}
 
	}
 

	
 
	for (i = 0; i <= a_index; i++) IConsoleCmdExec(aliases[i]); // execute each alias in turn
 
}
 

	
 
/**
 
 * Special function for adding string-type variables. They in addition
 
 * also need a 'size' value saying how long their string buffer is.
 
 * @param size the length of the string buffer
 
 * For more information see @IConsoleVarRegister()
 
 */
 
void IConsoleVarStringRegister(const char *name, void *addr, uint32 size, const char *help)
 
{
 
	IConsoleVar *var;
 
	IConsoleVarRegister(name, addr, ICONSOLE_VAR_STRING, help);
 
	var = IConsoleVarGet(name);
 
	var->size = size;
 
}
 

	
 
/**
 
 * Register a new variable to be used in the console
 
 * @param name name of the variable that will be used
 
 * @param addr memory location the variable will point to
 
 * @param help the help string shown for the variable
 
 * @param type the type of the variable (simple atomic) so we know which values it can get
 
 */
 
void IConsoleVarRegister(const char *name, void *addr, IConsoleVarTypes type, const char *help)
 
{
 
	char *new_cmd = strdup(name);
 
	IConsoleVar *item_new = malloc(sizeof(IConsoleVar));
 

	
 
	item_new->help = (help != NULL) ? strdup(help) : NULL;
 

	
 
	item_new->next = NULL;
 
	item_new->name = new_cmd;
 
	item_new->addr = addr;
 
	item_new->proc = NULL;
 
	item_new->type = type;
 

	
 
	item_new->hook.access = NULL;
 
	item_new->hook.pre = NULL;
 
	item_new->hook.post = NULL;
 

	
 
	IConsoleAddSorted(_iconsole_vars, item_new, IConsoleVar, "a variable");
 
}
 

	
 
/**
 
 * Find the variable pointed to by its string
 
 * @param name variable to be found
 
 * @return return Varstruct of the found variable, or NULL on failure
 
 */
 
IConsoleVar *IConsoleVarGet(const char *name)
 
{
 
	IConsoleVar *item;
 
	for (item = _iconsole_vars; item != NULL; item = item->next) {
 
		if (strcmp(item->name, name) == 0) return item;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 * Set a new value to a console variable
 
 * @param *var the variable being set/changed
 
 * @param value the new value given to the variable, cast properly
 
 */
 
static void IConsoleVarSetValue(const IConsoleVar *var, uint32 value)
 
{
 
	IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
 
	switch (var->type) {
 
		case ICONSOLE_VAR_BOOLEAN:
 
			*(bool*)var->addr = (value != 0);
 
			break;
 
		case ICONSOLE_VAR_BYTE:
 
			*(byte*)var->addr = (byte)value;
 
			break;
 
		case ICONSOLE_VAR_UINT16:
 
			*(uint16*)var->addr = (uint16)value;
 
			break;
 
		case ICONSOLE_VAR_INT16:
 
			*(int16*)var->addr = (int16)value;
 
			break;
 
		case ICONSOLE_VAR_UINT32:
 
			*(uint32*)var->addr = (uint32)value;
 
			break;
 
		case ICONSOLE_VAR_INT32:
 
			*(int32*)var->addr = (int32)value;
 
			break;
 
		default: NOT_REACHED();
 
	}
 

	
 
	IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
 
	IConsoleVarPrintSetValue(var);
 
}
 

	
 
/**
 
 * Set a new value to a string-type variable. Basically this
 
 * means to copy the new value over to the container.
 
 * @param *var the variable in question
 
 * @param *value the new value
 
 */
 
static void IConsoleVarSetStringvalue(const IConsoleVar *var, const char *value)
 
{
 
	if (var->type != ICONSOLE_VAR_STRING || var->addr == NULL) return;
 

	
 
	IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
 
	ttd_strlcpy(var->addr, value, var->size);
 
	IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
 
	IConsoleVarPrintSetValue(var); // print out the new value, giving feedback
 
	return;
 
}
 

	
 
/**
 
 * Query the current value of a variable and return it
 
 * @param *var the variable queried
 
 * @return current value of the variable
 
 */
 
static uint32 IConsoleVarGetValue(const IConsoleVar *var)
 
{
 
	uint32 result = 0;
 

	
 
	switch (var->type) {
 
		case ICONSOLE_VAR_BOOLEAN:
 
			result = *(bool*)var->addr;
 
			break;
 
		case ICONSOLE_VAR_BYTE:
 
			result = *(byte*)var->addr;
 
			break;
 
		case ICONSOLE_VAR_UINT16:
 
			result = *(uint16*)var->addr;
 
			break;
 
		case ICONSOLE_VAR_INT16:
 
			result = *(int16*)var->addr;
 
			break;
 
		case ICONSOLE_VAR_UINT32:
 
			result = *(uint32*)var->addr;
 
			break;
 
		case ICONSOLE_VAR_INT32:
 
			result = *(int32*)var->addr;
 
			break;
 
		default: NOT_REACHED();
 
	}
 
	return result;
 
}
 

	
 
/**
 
 * Get the value of the variable and put it into a printable
 
 * string form so we can use it for printing
 
 */
 
static char *IConsoleVarGetStringValue(const IConsoleVar *var)
 
{
 
	static char tempres[50];
 
	char *value = tempres;
 

	
 
	switch (var->type) {
 
		case ICONSOLE_VAR_BOOLEAN:
 
			snprintf(tempres, sizeof(tempres), "%s", (*(bool*)var->addr) ? "on" : "off");
 
			break;
 
		case ICONSOLE_VAR_BYTE:
 
			snprintf(tempres, sizeof(tempres), "%u", *(byte*)var->addr);
 
			break;
 
		case ICONSOLE_VAR_UINT16:
 
			snprintf(tempres, sizeof(tempres), "%u", *(uint16*)var->addr);
 
			break;
 
		case ICONSOLE_VAR_UINT32:
 
			snprintf(tempres, sizeof(tempres), "%u",  *(uint32*)var->addr);
 
			break;
 
		case ICONSOLE_VAR_INT16:
 
			snprintf(tempres, sizeof(tempres), "%i", *(int16*)var->addr);
 
			break;
 
		case ICONSOLE_VAR_INT32:
 
			snprintf(tempres, sizeof(tempres), "%i",  *(int32*)var->addr);
 
			break;
 
		case ICONSOLE_VAR_STRING:
 
			value = (char*)var->addr;
 
			break;
 
		default: NOT_REACHED();
 
	}
 

	
 
	return value;
 
}
 

	
 
/**
 
 * Print out the value of the variable when asked
 
 */
 
void IConsoleVarPrintGetValue(const IConsoleVar *var)
 
{
 
	char *value;
 
	/* Some variables need really specific handling, handle this in its
 
	 * callback function */
 
	if (var->proc != NULL) {
 
		var->proc(0, NULL);
 
		return;
 
	}
 

	
 
	value = IConsoleVarGetStringValue(var);
 
	IConsolePrintF(_icolour_warn, "Current value for '%s' is:  %s", var->name, value);
 
}
 

	
 
/**
 
 * Print out the value of the variable after it has been assigned
 
 * a new value, thus giving us feedback on the action
 
 */
 
void IConsoleVarPrintSetValue(const IConsoleVar *var)
 
{
 
	char *value = IConsoleVarGetStringValue(var);
 
	IConsolePrintF(_icolour_warn, "'%s' changed to:  %s", var->name, value);
 
}
 

	
 
/**
 
 * Execute a variable command. Without any parameters, print out its value
 
 * with parameters it assigns a new value to the variable
 
 * @param *var the variable that we will be querying/changing
 
 * @param tokencount how many additional parameters have been given to the commandline
 
 * @param *token the actual parameters the variable was called with
 
 */
 
void IConsoleVarExec(const IConsoleVar *var, byte tokencount, char *token[ICON_TOKEN_COUNT])
 
{
 
	const char *tokenptr = token[0];
 
	byte t_index = tokencount;
 
	uint32 value;
 

	
 
	if (_stdlib_con_developer)
 
		IConsolePrintF(_icolour_dbg, "condbg: requested command is a variable");
 

	
 
	if (tokencount == 0) { /* Just print out value */
 
		IConsoleVarPrintGetValue(var);
 
		return;
 
	}
 

	
 
	/* Use of assignment sign is not mandatory but supported, so just 'ignore it appropiately' */
 
	if (strcmp(tokenptr, "=") == 0) tokencount--;
 

	
 
	if (tokencount == 1) {
 
		/* Some variables need really special handling, handle it in their callback procedure */
 
		if (var->proc != NULL) {
 
			var->proc(tokencount, &token[t_index - tokencount]); // set the new value
 
			return;
 
		}
 
		/* Strings need special processing. No need to convert the argument to
 
		 * an integer value, just copy over the argument on a one-by-one basis */
 
		if (var->type == ICONSOLE_VAR_STRING) {
 
			IConsoleVarSetStringvalue(var, token[t_index - tokencount]);
 
			return;
 
		} else if (GetArgumentInteger(&value, token[t_index - tokencount])) {
 
			IConsoleVarSetValue(var, value);
 
			return;
 
		}
 

	
 
		/* Increase or decrease the value by one. This of course can only happen to 'number' types */
 
		if (strcmp(tokenptr, "++") == 0 && var->type != ICONSOLE_VAR_STRING) {
 
			IConsoleVarSetValue(var, IConsoleVarGetValue(var) + 1);
 
			return;
 
		}
 

	
 
		if (strcmp(tokenptr, "--") == 0 && var->type != ICONSOLE_VAR_STRING) {
 
			IConsoleVarSetValue(var, IConsoleVarGetValue(var) - 1);
 
			return;
 
		}
 
	}
 

	
 
	IConsoleError("invalid variable assignment");
 
}
 

	
 
/**
 
 * Add a callback function to the variable. Some variables need
 
 * very special processing, which can only be done with custom code
 
 * @param name name of the variable the callback function is added to
 
 * @param proc the function called
 
 */
 
void IConsoleVarProcAdd(const char *name, IConsoleCmdProc *proc)
 
{
 
	IConsoleVar *var = IConsoleVarGet(name);
 
	if (var == NULL) return;
 
	var->proc = proc;
 
}
 

	
 
/**
 
 * Execute a given command passed to us. First chop it up into
 
 * individual tokens (seperated by spaces), then execute it if possible
 
 * @param cmdstr string to be parsed and executed
 
 */
 
void IConsoleCmdExec(const char *cmdstr)
 
{
 
	IConsoleCmd   *cmd    = NULL;
 
	IConsoleAlias *alias  = NULL;
 
	IConsoleVar   *var    = NULL;
 

	
 
	const char *cmdptr;
 
	char *tokens[ICON_TOKEN_COUNT], tokenstream[ICON_MAX_STREAMSIZE];
 
	uint t_index, tstream_i;
 

	
 
	bool longtoken = false;
 
	bool foundtoken = false;
 

	
 
	if (cmdstr[0] == '#') return; // comments
 

	
 
	for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) {
 
		if (!IsValidChar(*cmdptr, CS_ALPHANUMERAL)) {
 
			IConsoleError("command contains malformed characters, aborting");
 
			IConsolePrintF(_icolour_err, "ERROR: command was: '%s'", cmdstr);
 
			return;
 
		}
 
	}
 

	
 
	if (_stdlib_con_developer)
 
		IConsolePrintF(_icolour_dbg, "condbg: executing cmdline: '%s'", cmdstr);
 

	
 
	memset(&tokens, 0, sizeof(tokens));
 
	memset(&tokenstream, 0, sizeof(tokenstream));
 

	
 
	/* 1. Split up commandline into tokens, seperated by spaces, commands
 
	 * enclosed in "" are taken as one token. We can only go as far as the amount
 
	 * of characters in our stream or the max amount of tokens we can handle */
 
	for (cmdptr = cmdstr, t_index = 0, tstream_i = 0; *cmdptr != '\0'; cmdptr++) {
 
		if (t_index >= lengthof(tokens) || tstream_i >= lengthof(tokenstream)) break;
 

	
 
		switch (*cmdptr) {
 
		case ' ': /* Token seperator */
 
			if (!foundtoken) break;
 

	
 
			if (longtoken) {
 
				tokenstream[tstream_i] = *cmdptr;
 
			} else {
 
				tokenstream[tstream_i] = '\0';
 
				foundtoken = false;
 
			}
 

	
 
			tstream_i++;
 
			break;
 
		case '"': /* Tokens enclosed in "" are one token */
 
			longtoken = !longtoken;
 
			break;
 
		case '\\': /* Escape character for "" */
 
			if (cmdptr[1] == '"' && tstream_i + 1 < lengthof(tokenstream)) {
 
				tokenstream[tstream_i++] = *++cmdptr;
 
				break;
 
			}
 
			/* fallthrough */
 
		default: /* Normal character */
 
			tokenstream[tstream_i++] = *cmdptr;
 

	
 
			if (!foundtoken) {
 
				tokens[t_index++] = &tokenstream[tstream_i - 1];
 
				foundtoken = true;
 
			}
 
			break;
 
		}
 
	}
 

	
 
	if (_stdlib_con_developer) {
 
		uint i;
 

	
 
		for (i = 0; tokens[i] != NULL; i++) {
 
			IConsolePrintF(_icolour_dbg, "condbg: token %d is: '%s'", i, tokens[i]);
 
		}
 
	}
 

	
 
	if (tokens[0] == '\0') return; // don't execute empty commands
 
	/* 2. Determine type of command (cmd, alias or variable) and execute
 
	 * First try commands, then aliases, and finally variables. Execute
 
	 * the found action taking into account its hooking code
 
	 */
 
	cmd = IConsoleCmdGet(tokens[0]);
 
	if (cmd != NULL) {
 
		if (IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_ACCESS)) {
 
			IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_PRE_ACTION);
 
			if (cmd->proc(t_index, tokens)) { // index started with 0
 
				IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_POST_ACTION);
 
			} else {
 
				cmd->proc(0, NULL); // if command failed, give help
 
			}
 
		}
 
		return;
 
	}
 

	
 
	t_index--; // ignore the variable-name for comfort for both aliases and variaables
 
	alias = IConsoleAliasGet(tokens[0]);
 
	if (alias != NULL) {
 
		IConsoleAliasExec(alias, t_index, &tokens[1]);
 
		return;
 
	}
 

	
 
	var = IConsoleVarGet(tokens[0]);
 
	if (var != NULL) {
 
		if (IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_ACCESS)) {
 
			IConsoleVarExec(var, t_index, &tokens[1]);
 
		}
 
		return;
 
	}
 

	
 
	IConsoleError("command or variable not found");
 
}
src/console_cmds.c
Show inline comments
 
deleted file
src/console_cmds.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "console.h"
 
#include "debug.h"
 
#include "engine.h"
 
#include "functions.h"
 
#include "saveload.h"
 
#include "string.h"
 
#include "variables.h"
 
#include "network/network_data.h"
 
#include "network/network_client.h"
 
#include "network/network_server.h"
 
#include "network/network_udp.h"
 
#include "command.h"
 
#include "settings.h"
 
#include "fios.h"
 
#include "vehicle.h"
 
#include "station.h"
 
#include "strings.h"
 
#include "screenshot.h"
 
#include "genworld.h"
 
#include "date.h"
 
#include "network/network.h"
 

	
 
// ** scriptfile handling ** //
 
static FILE *_script_file;
 
static bool _script_running;
 

	
 
// ** console command / variable defines ** //
 
#define DEF_CONSOLE_CMD(function) static bool function(byte argc, char *argv[])
 
#define DEF_CONSOLE_HOOK(function) static bool function(void)
 

	
 

	
 
/* **************************** */
 
/* variable and command hooks   */
 
/* **************************** */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
static inline bool NetworkAvailable(void)
 
{
 
	if (!_network_available) {
 
		IConsoleError("You cannot use this command because there is no network available.");
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookServerOnly)
 
{
 
	if (!NetworkAvailable()) return false;
 

	
 
	if (!_network_server) {
 
		IConsoleError("This command/variable is only available to a network server.");
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookClientOnly)
 
{
 
	if (!NetworkAvailable()) return false;
 

	
 
	if (_network_server) {
 
		IConsoleError("This command/variable is not available to a network server.");
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookNeedNetwork)
 
{
 
	if (!NetworkAvailable()) return false;
 

	
 
	if (!_networking) {
 
		IConsoleError("Not connected. This command/variable is only available in multiplayer.");
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookNoNetwork)
 
{
 
	if (_networking) {
 
		IConsoleError("This command/variable is forbidden in multiplayer.");
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
static void IConsoleHelp(const char *str)
 
{
 
	IConsolePrintF(_icolour_warn, "- %s", str);
 
}
 

	
 
DEF_CONSOLE_CMD(ConResetEngines)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Reset status data of all engines. This might solve some issues with 'lost' engines. Usage: 'resetengines'");
 
		return true;
 
	}
 

	
 
	StartupEngines();
 
	return true;
 
}
 

	
 
#ifdef _DEBUG
 
DEF_CONSOLE_CMD(ConResetTile)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Reset a tile to bare land. Usage: 'resettile <tile>'");
 
		IConsoleHelp("Tile can be either decimal (34161) or hexadecimal (0x4a5B)");
 
		return true;
 
	}
 

	
 
	if (argc == 2) {
 
		uint32 result;
 
		if (GetArgumentInteger(&result, argv[1])) {
 
			DoClearSquare((TileIndex)result);
 
			return true;
 
		}
 
	}
 

	
 
	return false;
 
}
 

	
 
DEF_CONSOLE_CMD(ConStopAllVehicles)
 
{
 
	Vehicle* v;
 
	if (argc == 0) {
 
		IConsoleHelp("Stops all vehicles in the game. For debugging only! Use at your own risk... Usage: 'stopall'");
 
		return true;
 
	}
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		/* Code ripped from CmdStartStopTrain. Can't call it, because of
 
		 * ownership problems, so we'll duplicate some code, for now */
 
		if (v->type == VEH_Train)
 
			v->u.rail.days_since_order_progr = 0;
 
		v->vehstatus |= VS_STOPPED;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
	}
 
	return true;
 
}
 
#endif /* _DEBUG */
 

	
 
DEF_CONSOLE_CMD(ConScrollToTile)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Center the screen on a given tile. Usage: 'scrollto <tile>'");
 
		IConsoleHelp("Tile can be either decimal (34161) or hexadecimal (0x4a5B)");
 
		return true;
 
	}
 

	
 
	if (argc == 2) {
 
		uint32 result;
 
		if (GetArgumentInteger(&result, argv[1])) {
 
			if (result >= MapSize()) {
 
				IConsolePrint(_icolour_err, "Tile does not exist");
 
				return true;
 
			}
 
			ScrollMainWindowToTile((TileIndex)result);
 
			return true;
 
		}
 
	}
 

	
 
	return false;
 
}
 

	
 
extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm);
 
extern void BuildFileList(void);
 
extern void SetFiosType(const byte fiostype);
 

	
 
/* Save the map to a file */
 
DEF_CONSOLE_CMD(ConSave)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Save the current game. Usage: 'save <filename>'");
 
		return true;
 
	}
 

	
 
	if (argc == 2) {
 
		char buf[200];
 

	
 
		snprintf(buf, lengthof(buf), "%s%s%s.sav", _paths.save_dir, PATHSEP, argv[1]);
 
		IConsolePrint(_icolour_def, "Saving map...");
 

	
 
		if (SaveOrLoad(buf, SL_SAVE) != SL_OK) {
 
			IConsolePrint(_icolour_err, "SaveMap failed");
 
		} else {
 
			IConsolePrintF(_icolour_def, "Map sucessfully saved to %s", buf);
 
		}
 
		return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
/* Explicitly save the configuration */
 
DEF_CONSOLE_CMD(ConSaveConfig)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Saves the current config, typically to 'openttd.cfg'.");
 
		return true;
 
	}
 

	
 
	SaveToConfig();
 
	IConsolePrint(_icolour_def, "Saved config.");
 
	return true;
 
}
 

	
 
static const FiosItem* GetFiosItem(const char* file)
 
{
 
	int i;
 

	
 
	_saveload_mode = SLD_LOAD_GAME;
 
	BuildFileList();
 

	
 
	for (i = 0; i < _fios_num; i++) {
 
		if (strcmp(file, _fios_list[i].name) == 0) break;
 
		if (strcmp(file, _fios_list[i].title) == 0) break;
 
	}
 

	
 
	if (i == _fios_num) { /* If no name matches, try to parse it as number */
 
		char* endptr;
 

	
 
		i = strtol(file, &endptr, 10);
 
		if (file == endptr || *endptr != '\0') i = -1;
 
	}
 

	
 
	return IS_INT_INSIDE(i, 0, _fios_num) ? &_fios_list[i] : NULL;
 
}
 

	
 

	
 
DEF_CONSOLE_CMD(ConLoad)
 
{
 
	const FiosItem *item;
 
	const char *file;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Load a game by name or index. Usage: 'load <file | number>'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	file = argv[1];
 
	item = GetFiosItem(file);
 
	if (item != NULL) {
 
		switch (item->type) {
 
			case FIOS_TYPE_FILE: case FIOS_TYPE_OLDFILE: {
 
				_switch_mode = SM_LOAD;
 
				SetFiosType(item->type);
 

	
 
				ttd_strlcpy(_file_to_saveload.name, FiosBrowseTo(item), sizeof(_file_to_saveload.name));
 
				ttd_strlcpy(_file_to_saveload.title, item->title, sizeof(_file_to_saveload.title));
 
			} break;
 
			default: IConsolePrintF(_icolour_err, "%s: Not a savegame.", file);
 
		}
 
	} else {
 
		IConsolePrintF(_icolour_err, "%s: No such file or directory.", file);
 
	}
 

	
 
	FiosFreeSavegameList();
 
	return true;
 
}
 

	
 

	
 
DEF_CONSOLE_CMD(ConRemove)
 
{
 
	const FiosItem* item;
 
	const char* file;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Remove a savegame by name or index. Usage: 'rm <file | number>'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	file = argv[1];
 
	item = GetFiosItem(file);
 
	if (item != NULL) {
 
		if (!FiosDelete(item->name))
 
			IConsolePrintF(_icolour_err, "%s: Failed to delete file", file);
 
	} else {
 
		IConsolePrintF(_icolour_err, "%s: No such file or directory.", file);
 
	}
 

	
 
	FiosFreeSavegameList();
 
	return true;
 
}
 

	
 

	
 
/* List all the files in the current dir via console */
 
DEF_CONSOLE_CMD(ConListFiles)
 
{
 
	int i;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("List all loadable savegames and directories in the current dir via console. Usage: 'ls | dir'");
 
		return true;
 
	}
 

	
 
	BuildFileList();
 

	
 
	for (i = 0; i < _fios_num; i++) {
 
		const FiosItem *item = &_fios_list[i];
 
		IConsolePrintF(_icolour_def, "%d) %s", i, item->title);
 
	}
 

	
 
	FiosFreeSavegameList();
 
	return true;
 
}
 

	
 
/* Change the dir via console */
 
DEF_CONSOLE_CMD(ConChangeDirectory)
 
{
 
	const FiosItem *item;
 
	const char *file;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Change the dir via console. Usage: 'cd <directory | number>'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	file = argv[1];
 
	item = GetFiosItem(file);
 
	if (item != NULL) {
 
		switch (item->type) {
 
			case FIOS_TYPE_DIR: case FIOS_TYPE_DRIVE: case FIOS_TYPE_PARENT:
 
				FiosBrowseTo(item);
 
				break;
 
			default: IConsolePrintF(_icolour_err, "%s: Not a directory.", file);
 
		}
 
	} else {
 
		IConsolePrintF(_icolour_err, "%s: No such file or directory.", file);
 
	}
 

	
 
	FiosFreeSavegameList();
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConPrintWorkingDirectory)
 
{
 
	const char *path;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Print out the current working directory. Usage: 'pwd'");
 
		return true;
 
	}
 

	
 
	// XXX - Workaround for broken file handling
 
	FiosGetSavegameList(SLD_LOAD_GAME);
 
	FiosFreeSavegameList();
 

	
 
	FiosGetDescText(&path, NULL);
 
	IConsolePrint(_icolour_def, path);
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConClearBuffer)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Clear the console buffer. Usage: 'clear'");
 
		return true;
 
	}
 

	
 
	IConsoleClearBuffer();
 
	InvalidateWindow(WC_CONSOLE, 0);
 
	return true;
 
}
 

	
 

	
 
// ********************************* //
 
// * Network Core Console Commands * //
 
// ********************************* //
 
#ifdef ENABLE_NETWORK
 

	
 
DEF_CONSOLE_CMD(ConBan)
 
{
 
	NetworkClientInfo *ci;
 
	const char *banip = NULL;
 
	uint32 index;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Ban a player from a network game. Usage: 'ban <ip | client-id>'");
 
		IConsoleHelp("For client-id's, see the command 'clients'");
 
		IConsoleHelp("If the client is no longer online, you can still ban his/her IP");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	if (strchr(argv[1], '.') == NULL) { // banning with ID
 
		index = atoi(argv[1]);
 
		ci = NetworkFindClientInfoFromIndex(index);
 
	} else { // banning IP
 
		ci = NetworkFindClientInfoFromIP(argv[1]);
 
		if (ci == NULL) {
 
			banip = argv[1];
 
			index = (uint32)-1;
 
		} else {
 
			index = ci->client_index;
 
		}
 
	}
 

	
 
	if (index == NETWORK_SERVER_INDEX) {
 
		IConsoleError("Silly boy, you can not ban yourself!");
 
		return true;
 
	}
 

	
 
	if (index == 0 || (ci == NULL && index != (uint32)-1)) {
 
		IConsoleError("Invalid client");
 
		return true;
 
	}
 

	
 
	if (ci != NULL) {
 
		banip = inet_ntoa(*(struct in_addr *)&ci->client_ip);
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(NetworkFindClientStateFromIndex(index), NETWORK_ERROR_KICKED);
 
		IConsolePrint(_icolour_def, "Client banned");
 
	} else {
 
		IConsolePrint(_icolour_def, "Client not online, banned IP");
 
	}
 

	
 
	/* Add user to ban-list */
 
	for (index = 0; index < lengthof(_network_ban_list); index++) {
 
		if (_network_ban_list[index] == NULL) {
 
			_network_ban_list[index] = strdup(banip);
 
			break;
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConUnBan)
 
{
 
	uint i, index;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Unban a player from a network game. Usage: 'unban <ip | client-id>'");
 
		IConsoleHelp("For a list of banned IP's, see the command 'banlist'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	index = (strchr(argv[1], '.') == NULL) ? atoi(argv[1]) : 0;
 
	index--;
 

	
 
	for (i = 0; i < lengthof(_network_ban_list); i++) {
 
		if (_network_ban_list[i] == NULL) continue;
 

	
 
		if (strcmp(_network_ban_list[i], argv[1]) == 0 || index == i) {
 
			free(_network_ban_list[i]);
 
			_network_ban_list[i] = NULL;
 
			IConsolePrint(_icolour_def, "IP unbanned.");
 
			return true;
 
		}
 
	}
 

	
 
	IConsolePrint(_icolour_def, "IP not in ban-list.");
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConBanList)
 
{
 
	uint i;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("List the IP's of banned clients: Usage 'banlist'");
 
		return true;
 
	}
 

	
 
	IConsolePrint(_icolour_def, "Banlist: ");
 

	
 
	for (i = 0; i < lengthof(_network_ban_list); i++) {
 
		if (_network_ban_list[i] != NULL)
 
			IConsolePrintF(_icolour_def, "  %d) %s", i + 1, _network_ban_list[i]);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConPauseGame)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Pause a network game. Usage: 'pause'");
 
		return true;
 
	}
 

	
 
	if (_pause == 0) {
 
		DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
 
		IConsolePrint(_icolour_def, "Game paused.");
 
	} else {
 
		IConsolePrint(_icolour_def, "Game is already paused.");
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConUnPauseGame)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Unpause a network game. Usage: 'unpause'");
 
		return true;
 
	}
 

	
 
	if (_pause != 0) {
 
		DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
 
		IConsolePrint(_icolour_def, "Game unpaused.");
 
	} else {
 
		IConsolePrint(_icolour_def, "Game is already unpaused.");
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConRcon)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Remote control the server from another client. Usage: 'rcon <password> <command>'");
 
		IConsoleHelp("Remember to enclose the command in quotes, otherwise only the first parameter is sent");
 
		return true;
 
	}
 

	
 
	if (argc < 3) return false;
 

	
 
	SEND_COMMAND(PACKET_CLIENT_RCON)(argv[1], argv[2]);
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConStatus)
 
{
 
	static const char* const stat_str[] = {
 
		"inactive",
 
		"authorized",
 
		"waiting",
 
		"loading map",
 
		"map done",
 
		"ready",
 
		"active"
 
	};
 

	
 
	const NetworkClientState *cs;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("List the status of all clients connected to the server. Usage 'status'");
 
		return true;
 
	}
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		int lag = NetworkCalculateLag(cs);
 
		const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
 
		const char* status;
 

	
 
		status = (cs->status < lengthof(stat_str) ? stat_str[cs->status] : "unknown");
 
		IConsolePrintF(8, "Client #%1d  name: '%s'  status: '%s'  frame-lag: %3d  company: %1d  IP: %s  unique-id: '%s'",
 
			cs->index, ci->client_name, status, lag,
 
			ci->client_playas + (IsValidPlayer(ci->client_playas) ? 1 : 0),
 
			GetPlayerIP(ci), ci->unique_id);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConServerInfo)
 
{
 
	const NetworkGameInfo *gi;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("List current and maximum client/player limits. Usage 'server_info'");
 
		IConsoleHelp("You can change these values by setting the variables 'max_clients', 'max_companies' and 'max_spectators'");
 
		return true;
 
	}
 

	
 
	gi = &_network_game_info;
 
	IConsolePrintF(_icolour_def, "Current/maximum clients:    %2d/%2d", gi->clients_on, gi->clients_max);
 
	IConsolePrintF(_icolour_def, "Current/maximum companies:  %2d/%2d", ActivePlayerCount(), gi->companies_max);
 
	IConsolePrintF(_icolour_def, "Current/maximum spectators: %2d/%2d", NetworkSpectatorCount(), gi->spectators_max);
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookValidateMaxClientsCount)
 
{
 
	/* XXX - hardcoded, string limiation -- TrueLight
 
	 * XXX - also see network.c:NetworkStartup ~1356 */
 
	if (_network_game_info.clients_max > 10) {
 
		_network_game_info.clients_max = 10;
 
		IConsoleError("Maximum clients out of bounds, truncating to limit.");
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookValidateMaxCompaniesCount)
 
{
 
	if (_network_game_info.companies_max > MAX_PLAYERS) {
 
		_network_game_info.companies_max = MAX_PLAYERS;
 
		IConsoleError("Maximum companies out of bounds, truncating to limit.");
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookValidateMaxSpectatorsCount)
 
{
 
	/* XXX @see ConHookValidateMaxClientsCount */
 
	if (_network_game_info.spectators_max > 10) {
 
		_network_game_info.spectators_max = 10;
 
		IConsoleError("Maximum spectators out of bounds, truncating to limit.");
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookCheckMinPlayers)
 
{
 
	CheckMinPlayers();
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConKick)
 
{
 
	NetworkClientInfo *ci;
 
	uint32 index;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Kick a player from a network game. Usage: 'kick <ip | client-id>'");
 
		IConsoleHelp("For client-id's, see the command 'clients'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	if (strchr(argv[1], '.') == NULL) {
 
		index = atoi(argv[1]);
 
		ci = NetworkFindClientInfoFromIndex(index);
 
	} else {
 
		ci = NetworkFindClientInfoFromIP(argv[1]);
 
		index = (ci == NULL) ? 0 : ci->client_index;
 
	}
 

	
 
	if (index == NETWORK_SERVER_INDEX) {
 
		IConsoleError("Silly boy, you can not kick yourself!");
 
		return true;
 
	}
 

	
 
	if (index == 0) {
 
		IConsoleError("Invalid client");
 
		return true;
 
	}
 

	
 
	if (ci != NULL) {
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(NetworkFindClientStateFromIndex(index), NETWORK_ERROR_KICKED);
 
	} else {
 
		IConsoleError("Client not found");
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConResetCompany)
 
{
 
	const Player *p;
 
	const NetworkClientState *cs;
 
	const NetworkClientInfo *ci;
 
	PlayerID index;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Remove an idle company from the game. Usage: 'reset_company <company-id>'");
 
		IConsoleHelp("For company-id's, see the list of companies from the dropdown menu. Player 1 is 1, etc.");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	index = atoi(argv[1]) - 1;
 

	
 
	/* Check valid range */
 
	if (!IsValidPlayer(index)) {
 
		IConsolePrintF(_icolour_err, "Company does not exist. Company-id must be between 1 and %d.", MAX_PLAYERS);
 
		return true;
 
	}
 

	
 
	/* Check if company does exist */
 
	p = GetPlayer(index);
 
	if (!p->is_active) {
 
		IConsoleError("Company does not exist.");
 
		return true;
 
	}
 

	
 
	if (p->is_ai) {
 
		IConsoleError("Company is owned by an AI.");
 
		return true;
 
	}
 

	
 
	/* Check if the company has active players */
 
	FOR_ALL_CLIENTS(cs) {
 
		ci = DEREF_CLIENT_INFO(cs);
 
		if (ci->client_playas == index) {
 
			IConsoleError("Cannot remove company: a client is connected to that company.");
 
			return true;
 
		}
 
	}
 
	ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 
	if (ci->client_playas == index) {
 
		IConsoleError("Cannot remove company: the server is connected to that company.");
 
		return true;
 
	}
 

	
 
	/* It is safe to remove this company */
 
	DoCommandP(0, 2, index, NULL, CMD_PLAYER_CTRL);
 
	IConsolePrint(_icolour_def, "Company deleted.");
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConNetworkClients)
 
{
 
	NetworkClientInfo *ci;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Get a list of connected clients including their ID, name, company-id, and IP. Usage: 'clients'");
 
		return true;
 
	}
 

	
 
	FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
 
		IConsolePrintF(8, "Client #%1d  name: '%s'  company: %1d  IP: %s",
 
		               ci->client_index, ci->client_name,
 
		               ci->client_playas + (IsValidPlayer(ci->client_playas) ? 1 : 0),
 
		               GetPlayerIP(ci));
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConNetworkConnect)
 
{
 
	char *ip;
 
	const char *port = NULL;
 
	const char *player = NULL;
 
	uint16 rport;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Connect to a remote OTTD server and join the game. Usage: 'connect <ip>'");
 
		IConsoleHelp("IP can contain port and player: 'IP[[#Player]:Port]', eg: 'server.ottd.org#2:443'");
 
		IConsoleHelp("Player #255 is spectator all others are a certain company with Company 1 being #1");
 
		return true;
 
	}
 

	
 
	if (argc < 2) return false;
 
	if (_networking) NetworkDisconnect(); // we are in network-mode, first close it!
 

	
 
	ip = argv[1];
 
	/* Default settings: default port and new company */
 
	rport = NETWORK_DEFAULT_PORT;
 
	_network_playas = PLAYER_NEW_COMPANY;
 

	
 
	ParseConnectionString(&player, &port, ip);
 

	
 
	IConsolePrintF(_icolour_def, "Connecting to %s...", ip);
 
	if (player != NULL) {
 
		_network_playas = atoi(player);
 
		IConsolePrintF(_icolour_def, "    player-no: %d", _network_playas);
 

	
 
		/* From a user pov 0 is a new player, internally it's different and all
 
		 * players are offset by one to ease up on users (eg players 1-8 not 0-7) */
 
		if (_network_playas != PLAYER_SPECTATOR) {
 
			_network_playas--;
 
			if (!IsValidPlayer(_network_playas)) return false;
 
		}
 
	}
 
	if (port != NULL) {
 
		rport = atoi(port);
 
		IConsolePrintF(_icolour_def, "    port: %s", port);
 
	}
 

	
 
	NetworkClientConnectGame(ip, rport);
 

	
 
	return true;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
/* ******************************** */
 
/*   script file console commands   */
 
/* ******************************** */
 

	
 
DEF_CONSOLE_CMD(ConExec)
 
{
 
	char cmdline[ICON_CMDLN_SIZE];
 
	char *cmdptr;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Execute a local script file. Usage: 'exec <script> <?>'");
 
		return true;
 
	}
 

	
 
	if (argc < 2) return false;
 

	
 
	_script_file = fopen(argv[1], "r");
 

	
 
	if (_script_file == NULL) {
 
		if (argc == 2 || atoi(argv[2]) != 0) IConsoleError("script file not found");
 
		return true;
 
	}
 

	
 
	_script_running = true;
 

	
 
	while (_script_running && fgets(cmdline, sizeof(cmdline), _script_file) != NULL) {
 
		/* Remove newline characters from the executing script */
 
		for (cmdptr = cmdline; *cmdptr != '\0'; cmdptr++) {
 
			if (*cmdptr == '\n' || *cmdptr == '\r') {
 
				*cmdptr = '\0';
 
				break;
 
			}
 
		}
 
		IConsoleCmdExec(cmdline);
 
	}
 

	
 
	if (ferror(_script_file))
 
		IConsoleError("Encountered errror while trying to read from script file");
 

	
 
	_script_running = false;
 
	fclose(_script_file);
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConReturn)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Stop executing a running script. Usage: 'return'");
 
		return true;
 
	}
 

	
 
	_script_running = false;
 
	return true;
 
}
 

	
 
/* **************************** */
 
/*   default console commands   */
 
/* **************************** */
 
extern bool CloseConsoleLogIfActive(void);
 

	
 
DEF_CONSOLE_CMD(ConScript)
 
{
 
	extern FILE* _iconsole_output_file;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Start or stop logging console output to a file. Usage: 'script <filename>'");
 
		IConsoleHelp("If filename is omitted, a running log is stopped if it is active");
 
		return true;
 
	}
 

	
 
	if (!CloseConsoleLogIfActive()) {
 
		if (argc < 2) return false;
 

	
 
		IConsolePrintF(_icolour_def, "file output started to: %s", argv[1]);
 
		_iconsole_output_file = fopen(argv[1], "ab");
 
		if (_iconsole_output_file == NULL) IConsoleError("could not open file");
 
	}
 

	
 
	return true;
 
}
 

	
 

	
 
DEF_CONSOLE_CMD(ConEcho)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Print back the first argument to the console. Usage: 'echo <arg>'");
 
		return true;
 
	}
 

	
 
	if (argc < 2) return false;
 
	IConsolePrint(_icolour_def, argv[1]);
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConEchoC)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Print back the first argument to the console in a given colour. Usage: 'echoc <colour> <arg2>'");
 
		return true;
 
	}
 

	
 
	if (argc < 3) return false;
 
	IConsolePrint(atoi(argv[1]), argv[2]);
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConNewGame)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Start a new game. Usage: 'newgame [seed]'");
 
		IConsoleHelp("The server can force a new game using 'newgame'; any client joined will rejoin after the server is done generating the new game.");
 
		return true;
 
	}
 

	
 
	StartNewGameWithoutGUI((argc == 2) ? (uint)atoi(argv[1]) : GENERATE_NEW_SEED);
 
	return true;
 
}
 

	
 
extern void SwitchMode(int new_mode);
 

	
 
DEF_CONSOLE_CMD(ConRestart)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Restart game. Usage: 'restart'");
 
		IConsoleHelp("Restarts a game. It tries to reproduce the exact same map as the game started with.");
 
		return true;
 
	}
 

	
 
	/* Don't copy the _newgame pointers to the real pointers, so call SwitchMode directly */
 
	_patches.map_x = MapLogX();
 
	_patches.map_y = FindFirstBit(MapSizeY());
 
	SwitchMode(SM_NEWGAME);
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConGetSeed)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Returns the seed used to create this game. Usage: 'getseed'");
 
		IConsoleHelp("The seed can be used to reproduce the exact same map as the game started with.");
 
		return true;
 
	}
 

	
 
	IConsolePrintF(_icolour_def, "Generation Seed: %u", _patches.generation_seed);
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConAlias)
 
{
 
	IConsoleAlias *alias;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Add a new alias, or redefine the behaviour of an existing alias . Usage: 'alias <name> <command>'");
 
		return true;
 
	}
 

	
 
	if (argc < 3) return false;
 

	
 
	alias = IConsoleAliasGet(argv[1]);
 
	if (alias == NULL) {
 
		IConsoleAliasRegister(argv[1], argv[2]);
 
	} else {
 
		free(alias->cmdline);
 
		alias->cmdline = strdup(argv[2]);
 
	}
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConScreenShot)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Create a screenshot of the game. Usage: 'screenshot [big | no_con]'");
 
		IConsoleHelp("'big' makes a screenshot of the whole map, 'no_con' hides the console to create the screenshot");
 
		return true;
 
	}
 

	
 
	if (argc > 3) return false;
 

	
 
	SetScreenshotType(SC_VIEWPORT);
 
	if (argc > 1) {
 
		if (strcmp(argv[1], "big") == 0 || (argc == 3 && strcmp(argv[2], "big") == 0))
 
			SetScreenshotType(SC_WORLD);
 

	
 
		if (strcmp(argv[1], "no_con") == 0 || (argc == 3 && strcmp(argv[2], "no_con") == 0))
 
			IConsoleClose();
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConInfoVar)
 
{
 
	static const char *_icon_vartypes[] = {"boolean", "byte", "uint16", "uint32", "int16", "int32", "string"};
 
	const IConsoleVar *var;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Print out debugging information about a variable. Usage: 'info_var <var>'");
 
		return true;
 
	}
 

	
 
	if (argc < 2) return false;
 

	
 
	var = IConsoleVarGet(argv[1]);
 
	if (var == NULL) {
 
		IConsoleError("the given variable was not found");
 
		return true;
 
	}
 

	
 
	IConsolePrintF(_icolour_def, "variable name: %s", var->name);
 
	IConsolePrintF(_icolour_def, "variable type: %s", _icon_vartypes[var->type]);
 
	IConsolePrintF(_icolour_def, "variable addr: 0x%X", var->addr);
 

	
 
	if (var->hook.access) IConsoleWarning("variable is access hooked");
 
	if (var->hook.pre) IConsoleWarning("variable is pre hooked");
 
	if (var->hook.post) IConsoleWarning("variable is post hooked");
 
	return true;
 
}
 

	
 

	
 
DEF_CONSOLE_CMD(ConInfoCmd)
 
{
 
	const IConsoleCmd *cmd;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Print out debugging information about a command. Usage: 'info_cmd <cmd>'");
 
		return true;
 
	}
 

	
 
	if (argc < 2) return false;
 

	
 
	cmd = IConsoleCmdGet(argv[1]);
 
	if (cmd == NULL) {
 
		IConsoleError("the given command was not found");
 
		return true;
 
	}
 

	
 
	IConsolePrintF(_icolour_def, "command name: %s", cmd->name);
 
	IConsolePrintF(_icolour_def, "command proc: 0x%X", cmd->proc);
 

	
 
	if (cmd->hook.access) IConsoleWarning("command is access hooked");
 
	if (cmd->hook.pre) IConsoleWarning("command is pre hooked");
 
	if (cmd->hook.post) IConsoleWarning("command is post hooked");
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConDebugLevel)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Get/set the default debugging level for the game. Usage: 'debug_level [<level>]'");
 
		IConsoleHelp("Level can be any combination of names, levels. Eg 'net=5 ms=4'. Remember to enclose it in \"'s");
 
		return true;
 
	}
 

	
 
	if (argc > 2) return false;
 

	
 
	if (argc == 1) {
 
		IConsolePrintF(_icolour_def, "Current debug-level: '%s'", GetDebugString());
 
	} else {
 
		SetDebugString(argv[1]);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConExit)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Exit the game. Usage: 'exit'");
 
		return true;
 
	}
 

	
 
	_exit_game = true;
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConPart)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Leave the currently joined/running game (only ingame). Usage: 'part'");
 
		return true;
 
	}
 

	
 
	if (_game_mode != GM_NORMAL) return false;
 

	
 
	_switch_mode = SM_MENU;
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConHelp)
 
{
 
	if (argc == 2) {
 
		const IConsoleCmd *cmd;
 
		const IConsoleVar *var;
 
		const IConsoleAlias *alias;
 

	
 
		cmd = IConsoleCmdGet(argv[1]);
 
		if (cmd != NULL) {
 
			cmd->proc(0, NULL);
 
			return true;
 
		}
 

	
 
		alias = IConsoleAliasGet(argv[1]);
 
		if (alias != NULL) {
 
			cmd = IConsoleCmdGet(alias->cmdline);
 
			if (cmd != NULL) {
 
				cmd->proc(0, NULL);
 
				return true;
 
			}
 
			IConsolePrintF(_icolour_err, "ERROR: alias is of special type, please see its execution-line: '%s'", alias->cmdline);
 
			return true;
 
		}
 

	
 
		var = IConsoleVarGet(argv[1]);
 
		if (var != NULL && var->help != NULL) {
 
			IConsoleHelp(var->help);
 
			return true;
 
		}
 

	
 
		IConsoleError("command or variable not found");
 
		return true;
 
	}
 

	
 
	IConsolePrint(13, " ---- OpenTTD Console Help ---- ");
 
	IConsolePrint( 1, " - variables: [command to list all variables: list_vars]");
 
	IConsolePrint( 1, " set value with '<var> = <value>', use '++/--' to in-or decrement");
 
	IConsolePrint( 1, " or omit '=' and just '<var> <value>'. get value with typing '<var>'");
 
	IConsolePrint( 1, " - commands: [command to list all commands: list_cmds]");
 
	IConsolePrint( 1, " call commands with '<command> <arg2> <arg3>...'");
 
	IConsolePrint( 1, " - to assign strings, or use them as arguments, enclose it within quotes");
 
	IConsolePrint( 1, " like this: '<command> \"string argument with spaces\"'");
 
	IConsolePrint( 1, " - use 'help <command> | <variable>' to get specific information");
 
	IConsolePrint( 1, " - scroll console output with shift + (up | down) | (pageup | pagedown))");
 
	IConsolePrint( 1, " - scroll console input history with the up | down arrows");
 
	IConsolePrint( 1, "");
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConListCommands)
 
{
 
	const IConsoleCmd *cmd;
 
	size_t l = 0;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("List all registered commands. Usage: 'list_cmds [<pre-filter>]'");
 
		return true;
 
	}
 

	
 
	if (argv[1] != NULL) l = strlen(argv[1]);
 

	
 
	for (cmd = _iconsole_cmds; cmd != NULL; cmd = cmd->next) {
 
		if (argv[1] == NULL || strncmp(cmd->name, argv[1], l) == 0) {
 
				IConsolePrintF(_icolour_def, "%s", cmd->name);
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConListVariables)
 
{
 
	const IConsoleVar *var;
 
	size_t l = 0;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("List all registered variables. Usage: 'list_vars [<pre-filter>]'");
 
		return true;
 
	}
 

	
 
	if (argv[1] != NULL) l = strlen(argv[1]);
 

	
 
	for (var = _iconsole_vars; var != NULL; var = var->next) {
 
		if (argv[1] == NULL || strncmp(var->name, argv[1], l) == 0)
 
			IConsolePrintF(_icolour_def, "%s", var->name);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConListAliases)
 
{
 
	const IConsoleAlias *alias;
 
	size_t l = 0;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("List all registered aliases. Usage: 'list_aliases [<pre-filter>]'");
 
		return true;
 
	}
 

	
 
	if (argv[1] != NULL) l = strlen(argv[1]);
 

	
 
	for (alias = _iconsole_aliases; alias != NULL; alias = alias->next) {
 
		if (argv[1] == NULL || strncmp(alias->name, argv[1], l) == 0)
 
			IConsolePrintF(_icolour_def, "%s => %s", alias->name, alias->cmdline);
 
	}
 

	
 
	return true;
 
}
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
DEF_CONSOLE_CMD(ConSay)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Chat to your fellow players in a multiplayer game. Usage: 'say \"<msg>\"'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	if (!_network_server) {
 
		SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0 /* param does not matter */, argv[1]);
 
	} else {
 
		NetworkServer_HandleChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0, argv[1], NETWORK_SERVER_INDEX);
 
	}
 

	
 
	return true;
 
}
 

	
 
#ifdef ENABLE_NETWORK
 
	#include "table/strings.h"
 
#endif /* ENABLE_NETWORK */
 

	
 
DEF_CONSOLE_CMD(ConPlayers)
 
{
 
	Player *p;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("List the in-game details of all clients connected to the server. Usage 'players'");
 
		return true;
 
	}
 
	NetworkPopulateCompanyInfo();
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		char buffer[512];
 

	
 
		if (!p->is_active) continue;
 

	
 
		GetString(buffer, STR_00D1_DARK_BLUE + _player_colors[p->index], lastof(buffer));
 
		IConsolePrintF(8, "#:%d(%s) Company Name: '%s'  Year Founded: %d  Money: %d  Loan: %d  Value: %" OTTD_PRINTF64 "d  (T:%d, R:%d, P:%d, S:%d)",
 
			p->index + 1, buffer, _network_player_info[p->index].company_name, p->inaugurated_year, p->player_money, p->current_loan, CalculateCompanyValue(p),
 
			/* trains      */ _network_player_info[p->index].num_vehicle[0],
 
			/* lorry + bus */ _network_player_info[p->index].num_vehicle[1] + _network_player_info[p->index].num_vehicle[2],
 
			/* planes      */ _network_player_info[p->index].num_vehicle[3],
 
			/* ships       */ _network_player_info[p->index].num_vehicle[4]);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConSayPlayer)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Chat to a certain player in a multiplayer game. Usage: 'say_player <player-no> \"<msg>\"'");
 
		IConsoleHelp("PlayerNo is the player that plays as company <playerno>, 1 through max_players");
 
		return true;
 
	}
 

	
 
	if (argc != 3) return false;
 

	
 
	if (atoi(argv[1]) < 1 || atoi(argv[1]) > MAX_PLAYERS) {
 
		IConsolePrintF(_icolour_def, "Unknown player. Player range is between 1 and %d.", MAX_PLAYERS);
 
		return true;
 
	}
 

	
 
	if (!_network_server) {
 
		SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT_COMPANY, DESTTYPE_TEAM, atoi(argv[1]), argv[2]);
 
	} else {
 
		NetworkServer_HandleChat(NETWORK_ACTION_CHAT_COMPANY, DESTTYPE_TEAM, atoi(argv[1]), argv[2], NETWORK_SERVER_INDEX);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConSayClient)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Chat to a certain player in a multiplayer game. Usage: 'say_client <client-no> \"<msg>\"'");
 
		IConsoleHelp("For client-id's, see the command 'clients'");
 
		return true;
 
	}
 

	
 
	if (argc != 3) return false;
 

	
 
	if (!_network_server) {
 
		SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, atoi(argv[1]), argv[2]);
 
	} else {
 
		NetworkServer_HandleChat(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, atoi(argv[1]), argv[2], NETWORK_SERVER_INDEX);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookServerPW)
 
{
 
	if (strcmp(_network_server_password, "*") == 0) {
 
		_network_server_password[0] = '\0';
 
		_network_game_info.use_password = 0;
 
	} else {
 
		ttd_strlcpy(_network_game_info.server_password, _network_server_password, sizeof(_network_server_password));
 
		_network_game_info.use_password = 1;
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookRconPW)
 
{
 
	if (strcmp(_network_rcon_password, "*") == 0)
 
		_network_rcon_password[0] = '\0';
 

	
 
	ttd_strlcpy(_network_game_info.rcon_password, _network_rcon_password, sizeof(_network_game_info.rcon_password));
 

	
 
	return true;
 
}
 

	
 
/* Also use from within player_gui to change the password graphically */
 
bool NetworkChangeCompanyPassword(byte argc, char *argv[])
 
{
 
	if (argc == 0) {
 
		if (!IsValidPlayer(_local_player)) return true; // dedicated server
 
		IConsolePrintF(_icolour_warn, "Current value for 'company_pw': %s", _network_player_info[_local_player].password);
 
		return true;
 
	}
 

	
 
	if (!IsValidPlayer(_local_player)) {
 
		IConsoleError("You have to own a company to make use of this command.");
 
		return false;
 
	}
 

	
 
	if (argc != 1) return false;
 

	
 
	if (strcmp(argv[0], "*") == 0) argv[0][0] = '\0';
 

	
 
	ttd_strlcpy(_network_player_info[_local_player].password, argv[0], sizeof(_network_player_info[_local_player].password));
 

	
 
	if (!_network_server)
 
		SEND_COMMAND(PACKET_CLIENT_SET_PASSWORD)(_network_player_info[_local_player].password);
 

	
 
	IConsolePrintF(_icolour_warn, "'company_pw' changed to:  %s", _network_player_info[_local_player].password);
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConProcPlayerName)
 
{
 
	NetworkClientInfo *ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
 

	
 
	if (ci == NULL) return false;
 

	
 
	// Don't change the name if it is the same as the old name
 
	if (strcmp(ci->client_name, _network_player_name) != 0) {
 
		if (!_network_server) {
 
			SEND_COMMAND(PACKET_CLIENT_SET_NAME)(_network_player_name);
 
		} else {
 
			if (NetworkFindName(_network_player_name)) {
 
				NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, 1, false, ci->client_name, "%s", _network_player_name);
 
				ttd_strlcpy(ci->client_name, _network_player_name, sizeof(ci->client_name));
 
				NetworkUpdateClientInfo(NETWORK_SERVER_INDEX);
 
			}
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookServerName)
 
{
 
	ttd_strlcpy(_network_game_info.server_name, _network_server_name, sizeof(_network_game_info.server_name));
 
	return true;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookServerAdvertise)
 
{
 
	if (!_network_advertise) // remove us from advertising
 
		NetworkUDPRemoveAdvertise();
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConProcServerIP)
 
{
 
	if (argc == 0) {
 
		IConsolePrintF(_icolour_warn, "Current value for 'server_ip': %s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip));
 
		return true;
 
	}
 

	
 
	if (argc != 1) return false;
 

	
 
	_network_server_bind_ip = (strcmp(argv[0], "all") == 0) ? inet_addr("0.0.0.0") : inet_addr(argv[0]);
 
	snprintf(_network_server_bind_ip_host, sizeof(_network_server_bind_ip_host), "%s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip));
 
	IConsolePrintF(_icolour_warn, "'server_ip' changed to:  %s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip));
 
	return true;
 
}
 
#endif /* ENABLE_NETWORK */
 

	
 
DEF_CONSOLE_CMD(ConPatch)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Change patch variables for all players. Usage: 'patch <name> [<value>]'");
 
		IConsoleHelp("Omitting <value> will print out the current value of the patch-setting.");
 
		return true;
 
	}
 

	
 
	if (argc == 1 || argc > 3) return false;
 

	
 
	if (argc == 2) {
 
		IConsoleGetPatchSetting(argv[1]);
 
	} else {
 
		uint32 val;
 

	
 
		if (GetArgumentInteger(&val, argv[2])) {
 
			if (!IConsoleSetPatchSetting(argv[1], val)) {
 
				IConsoleError("This command/variable is only available to a network server.");
 
			}
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConListDumpVariables)
 
{
 
	const IConsoleVar *var;
 
	size_t l = 0;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("List all variables with their value. Usage: 'dump_vars [<pre-filter>]'");
 
		return true;
 
	}
 

	
 
	if (argv[1] != NULL) l = strlen(argv[1]);
 

	
 
	for (var = _iconsole_vars; var != NULL; var = var->next) {
 
		if (argv[1] == NULL || strncmp(var->name, argv[1], l) == 0)
 
			IConsoleVarPrintGetValue(var);
 
	}
 

	
 
	return true;
 
}
 

	
 

	
 
#ifdef _DEBUG
 
/* ****************************************** */
 
/*  debug commands and variables */
 
/* ****************************************** */
 

	
 
static void IConsoleDebugLibRegister(void)
 
{
 
	// debugging variables and functions
 
	extern bool _stdlib_con_developer; /* XXX extern in .c */
 

	
 
	IConsoleVarRegister("con_developer",    &_stdlib_con_developer, ICONSOLE_VAR_BOOLEAN, "Enable/disable console debugging information (internal)");
 
	IConsoleCmdRegister("resettile",        ConResetTile);
 
	IConsoleCmdRegister("stopall",          ConStopAllVehicles);
 
	IConsoleAliasRegister("dbg_echo",       "echo %A; echo %B");
 
	IConsoleAliasRegister("dbg_echo2",      "echo %!");
 
}
 
#endif
 

	
 
/* ****************************************** */
 
/*  console command and variable registration */
 
/* ****************************************** */
 

	
 
void IConsoleStdLibRegister(void)
 
{
 
	// stdlib
 
	extern byte _stdlib_developer; /* XXX extern in .c */
 

	
 
	// default variables and functions
 
	IConsoleCmdRegister("debug_level",  ConDebugLevel);
 
	IConsoleCmdRegister("dump_vars",    ConListDumpVariables);
 
	IConsoleCmdRegister("echo",         ConEcho);
 
	IConsoleCmdRegister("echoc",        ConEchoC);
 
	IConsoleCmdRegister("exec",         ConExec);
 
	IConsoleCmdRegister("exit",         ConExit);
 
	IConsoleCmdRegister("part",         ConPart);
 
	IConsoleCmdRegister("help",         ConHelp);
 
	IConsoleCmdRegister("info_cmd",     ConInfoCmd);
 
	IConsoleCmdRegister("info_var",     ConInfoVar);
 
	IConsoleCmdRegister("list_cmds",    ConListCommands);
 
	IConsoleCmdRegister("list_vars",    ConListVariables);
 
	IConsoleCmdRegister("list_aliases", ConListAliases);
 
	IConsoleCmdRegister("newgame",      ConNewGame);
 
	IConsoleCmdRegister("restart",      ConRestart);
 
	IConsoleCmdRegister("getseed",      ConGetSeed);
 
	IConsoleCmdRegister("quit",         ConExit);
 
	IConsoleCmdRegister("resetengines", ConResetEngines);
 
	IConsoleCmdRegister("return",       ConReturn);
 
	IConsoleCmdRegister("screenshot",   ConScreenShot);
 
	IConsoleCmdRegister("script",       ConScript);
 
	IConsoleCmdRegister("scrollto",     ConScrollToTile);
 
	IConsoleCmdRegister("alias",        ConAlias);
 
	IConsoleCmdRegister("load",         ConLoad);
 
	IConsoleCmdRegister("rm",           ConRemove);
 
	IConsoleCmdRegister("save",         ConSave);
 
	IConsoleCmdRegister("saveconfig",   ConSaveConfig);
 
	IConsoleCmdRegister("ls",           ConListFiles);
 
	IConsoleCmdRegister("cd",           ConChangeDirectory);
 
	IConsoleCmdRegister("pwd",          ConPrintWorkingDirectory);
 
	IConsoleCmdRegister("clear",        ConClearBuffer);
 
	IConsoleCmdRegister("patch",        ConPatch);
 

	
 
	IConsoleAliasRegister("dir",      "ls");
 
	IConsoleAliasRegister("del",      "rm %+");
 
	IConsoleAliasRegister("newmap",   "newgame");
 
	IConsoleAliasRegister("new_map",  "newgame");
 
	IConsoleAliasRegister("new_game", "newgame");
 

	
 

	
 
	IConsoleVarRegister("developer", &_stdlib_developer, ICONSOLE_VAR_BYTE, "Redirect debugging output from the console/command line to the ingame console (value 2). Default value: 1");
 

	
 
	/* networking variables and functions */
 
#ifdef ENABLE_NETWORK
 
	/* Network hooks; only active in network */
 
	IConsoleCmdHookAdd ("resetengines", ICONSOLE_HOOK_ACCESS, ConHookNoNetwork);
 

	
 
	/*** Networking commands ***/
 
	IConsoleCmdRegister("say",             ConSay);
 
	IConsoleCmdHookAdd("say",              ICONSOLE_HOOK_ACCESS, ConHookNeedNetwork);
 
	IConsoleCmdRegister("players",             ConPlayers);
 
	IConsoleCmdHookAdd("players",              ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleCmdRegister("say_player",      ConSayPlayer);
 
	IConsoleCmdHookAdd("say_player",       ICONSOLE_HOOK_ACCESS, ConHookNeedNetwork);
 
	IConsoleCmdRegister("say_client",      ConSayClient);
 
	IConsoleCmdHookAdd("say_client",       ICONSOLE_HOOK_ACCESS, ConHookNeedNetwork);
 

	
 
	IConsoleCmdRegister("connect",         ConNetworkConnect);
 
	IConsoleCmdHookAdd("connect",          ICONSOLE_HOOK_ACCESS, ConHookClientOnly);
 
	IConsoleAliasRegister("join",          "connect %A");
 
	IConsoleCmdRegister("clients",         ConNetworkClients);
 
	IConsoleCmdHookAdd("clients",          ICONSOLE_HOOK_ACCESS, ConHookNeedNetwork);
 
	IConsoleCmdRegister("status",          ConStatus);
 
	IConsoleCmdHookAdd("status",           ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleCmdRegister("server_info",     ConServerInfo);
 
	IConsoleCmdHookAdd("server_info",      ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleAliasRegister("info",          "server_info");
 
	IConsoleCmdRegister("rcon",            ConRcon);
 
	IConsoleCmdHookAdd("rcon",             ICONSOLE_HOOK_ACCESS, ConHookNeedNetwork);
 

	
 
	IConsoleCmdRegister("reset_company",   ConResetCompany);
 
	IConsoleCmdHookAdd("reset_company",    ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleAliasRegister("clean_company", "reset_company %A");
 
	IConsoleCmdRegister("kick",            ConKick);
 
	IConsoleCmdHookAdd("kick",             ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleCmdRegister("ban",             ConBan);
 
	IConsoleCmdHookAdd("ban",              ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleCmdRegister("unban",           ConUnBan);
 
	IConsoleCmdHookAdd("unban",            ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleCmdRegister("banlist",         ConBanList);
 
	IConsoleCmdHookAdd("banlist",          ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 

	
 
	IConsoleCmdRegister("pause",           ConPauseGame);
 
	IConsoleCmdHookAdd("pause",            ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleCmdRegister("unpause",         ConUnPauseGame);
 
	IConsoleCmdHookAdd("unpause",          ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 

	
 
	/*** Networking variables ***/
 
	IConsoleVarRegister("net_frame_freq",        &_network_frame_freq, ICONSOLE_VAR_BYTE, "The amount of frames before a command will be (visibly) executed. Default value: 1");
 
	IConsoleVarHookAdd("net_frame_freq",         ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarRegister("net_sync_freq",         &_network_sync_freq,  ICONSOLE_VAR_UINT16, "The amount of frames to check if the game is still in sync. Default value: 100");
 
	IConsoleVarHookAdd("net_sync_freq",          ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 

	
 
	IConsoleVarStringRegister("server_pw",       &_network_server_password, sizeof(_network_server_password), "Set the server password to protect your server. Use '*' to clear the password");
 
	IConsoleVarHookAdd("server_pw",              ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarHookAdd("server_pw",              ICONSOLE_HOOK_POST_ACTION, ConHookServerPW);
 
	IConsoleAliasRegister("server_password",     "server_pw %+");
 
	IConsoleVarStringRegister("rcon_pw",         &_network_rcon_password, sizeof(_network_rcon_password), "Set the rcon-password to change server behaviour. Use '*' to disable rcon");
 
	IConsoleVarHookAdd("rcon_pw",                ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarHookAdd("rcon_pw",                ICONSOLE_HOOK_POST_ACTION, ConHookRconPW);
 
	IConsoleAliasRegister("rcon_password",       "rcon_pw %+");
 
	IConsoleVarStringRegister("company_pw",      NULL, 0, "Set a password for your company, so no one without the correct password can join. Use '*' to clear the password");
 
	IConsoleVarHookAdd("company_pw",             ICONSOLE_HOOK_ACCESS, ConHookNeedNetwork);
 
	IConsoleVarProcAdd("company_pw",             NetworkChangeCompanyPassword);
 
	IConsoleAliasRegister("company_password",    "company_pw %+");
 

	
 
	IConsoleVarStringRegister("name",            &_network_player_name, sizeof(_network_player_name), "Set your name for multiplayer");
 
	IConsoleVarHookAdd("name",                   ICONSOLE_HOOK_ACCESS, ConHookNeedNetwork);
 
	IConsoleVarHookAdd("name",                   ICONSOLE_HOOK_POST_ACTION, ConProcPlayerName);
 
	IConsoleVarStringRegister("server_name",     &_network_server_name, sizeof(_network_server_name), "Set the name of the server for multiplayer");
 
	IConsoleVarHookAdd("server_name",            ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarHookAdd("server_name",            ICONSOLE_HOOK_POST_ACTION, ConHookServerName);
 

	
 
	IConsoleVarRegister("server_port",           &_network_server_port, ICONSOLE_VAR_UINT32, "Set the server port. Changes take effect the next time you start a server");
 
	IConsoleVarRegister("server_ip",             &_network_server_bind_ip, ICONSOLE_VAR_UINT32, "Set the IP the server binds to. Changes take effect the next time you start a server. Use 'all' to bind to any IP.");
 
	IConsoleVarProcAdd("server_ip",              ConProcServerIP);
 
	IConsoleAliasRegister("server_bind_ip",      "server_ip %+");
 
	IConsoleAliasRegister("server_ip_bind",      "server_ip %+");
 
	IConsoleAliasRegister("server_bind",         "server_ip %+");
 
	IConsoleVarRegister("server_advertise",      &_network_advertise, ICONSOLE_VAR_BOOLEAN, "Set if the server will advertise to the master server and show up there");
 
	IConsoleVarHookAdd("server_advertise",       ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarHookAdd("server_advertise",       ICONSOLE_HOOK_POST_ACTION, ConHookServerAdvertise);
 

	
 
	IConsoleVarRegister("max_clients",           &_network_game_info.clients_max, ICONSOLE_VAR_BYTE, "Control the maximum amount of connected players during runtime. Default value: 10");
 
	IConsoleVarHookAdd("max_clients",            ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarHookAdd("max_clients",            ICONSOLE_HOOK_POST_ACTION, ConHookValidateMaxClientsCount);
 
	IConsoleVarRegister("max_companies",         &_network_game_info.companies_max, ICONSOLE_VAR_BYTE, "Control the maximum amount of active companies during runtime. Default value: 8");
 
	IConsoleVarHookAdd("max_companies",          ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarHookAdd("max_companies",          ICONSOLE_HOOK_POST_ACTION, ConHookValidateMaxCompaniesCount);
 
	IConsoleVarRegister("max_spectators",        &_network_game_info.spectators_max, ICONSOLE_VAR_BYTE, "Control the maximum amount of active spectators during runtime. Default value: 9");
 
	IConsoleVarHookAdd("max_spectators",         ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarHookAdd("max_spectators",         ICONSOLE_HOOK_POST_ACTION, ConHookValidateMaxSpectatorsCount);
 

	
 
	IConsoleVarRegister("max_join_time",         &_network_max_join_time, ICONSOLE_VAR_UINT16, "Set the maximum amount of time (ticks) a client is allowed to join. Default value: 500");
 

	
 
	IConsoleVarRegister("pause_on_join",         &_network_pause_on_join, ICONSOLE_VAR_BOOLEAN, "Set if the server should pause gameplay while a client is joining. This might help slow users");
 
	IConsoleVarHookAdd("pause_on_join",          ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 

	
 
	IConsoleVarRegister("autoclean_companies",   &_network_autoclean_companies, ICONSOLE_VAR_BOOLEAN, "Automatically shut down inactive companies to free them up for other players. Customize with 'autoclean_(un)protected'");
 
	IConsoleVarHookAdd("autoclean_companies",    ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarRegister("autoclean_protected",   &_network_autoclean_protected, ICONSOLE_VAR_BYTE, "Automatically remove the password from an inactive company after the given amount of months");
 
	IConsoleVarHookAdd("autoclean_protected",    ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarRegister("autoclean_unprotected", &_network_autoclean_unprotected, ICONSOLE_VAR_BYTE, "Automatically shut down inactive companies after the given amount of months");
 
	IConsoleVarHookAdd("autoclean_unprotected",  ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarRegister("restart_game_year",     &_network_restart_game_year, ICONSOLE_VAR_UINT16, "Auto-restart the server when Jan 1st of the set year is reached. Use '0' to disable this");
 
	IConsoleVarHookAdd("restart_game_year",      ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 

	
 
	IConsoleVarRegister("min_players",           &_network_min_players, ICONSOLE_VAR_BYTE, "Automatically pause the game when the number of active players passes below the given amount");
 
	IConsoleVarHookAdd("min_players",            ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
 
	IConsoleVarHookAdd("min_players",            ICONSOLE_HOOK_POST_ACTION, ConHookCheckMinPlayers);
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
	// debugging stuff
 
#ifdef _DEBUG
 
	IConsoleDebugLibRegister();
 
#endif
 
}
src/currency.c
Show inline comments
 
deleted file
src/currency.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "currency.h"
 
#include "news.h"
 
#include "variables.h"
 
#include "table/strings.h"
 
#include "date.h"
 

	
 
	//   exchange rate    prefix             symbol_pos
 
	//   |  separator        |     postfix   |
 
	//   |   |    Euro year  |       |       |    name
 
	//   |   |    |          |       |       |    |
 
static const CurrencySpec origin_currency_specs[NUM_CURRENCY] = {
 
	{    1, ',', CF_NOEURO, "£",    "",      0,  STR_CURR_GBP    }, // british pounds
 
	{    2, ',', CF_NOEURO, "$",    "",      0,  STR_CURR_USD    }, // us dollars
 
	{    2, ',', CF_ISEURO, "€",    "",      0,  STR_CURR_EUR    }, // Euro
 
	{  220, ',', CF_NOEURO, "¥",    "",      0,  STR_CURR_YEN    }, // yen
 
	{   20, ',', 2002,      "",     " S.",   1,  STR_CURR_ATS    }, // austrian schilling
 
	{   59, ',', 2002,      "BEF ", "",      0,  STR_CURR_BEF    }, // belgian franc
 
	{    2, ',', CF_NOEURO, "CHF ", "",      0,  STR_CURR_CHF    }, // swiss franc
 
	{   41, ',', CF_NOEURO, "",     " Kč",   1,  STR_CURR_CZK    }, // czech koruna
 
	{    3, '.', 2002,      "DM ",  "",      0,  STR_CURR_DEM    }, // deutsche mark
 
	{   11, '.', CF_NOEURO, "",     " kr",   1,  STR_CURR_DKK    }, // danish krone
 
	{  245, '.', 2002,      "Pts ", "",      0,  STR_CURR_ESP    }, // spanish pesetas
 
	{    9, ',', 2002,      "",     " mk",   1,  STR_CURR_FIM    }, // finnish markka
 
	{   10, '.', 2002,      "FF ",  "",      0,  STR_CURR_FRF    }, // french francs
 
	{  500, ',', 2002,      "",     "Dr.",   1,  STR_CURR_GRD    }, // greek drachma
 
	{  378, ',', 2010,      "",     " Ft",   1,  STR_CURR_HUF    }, // hungarian forint
 
	{  130, '.', CF_NOEURO, "",     " Kr",   1,  STR_CURR_ISK    }, // icelandic krona
 
	{ 2850, ',', 2002,      "",     " L.",   1,  STR_CURR_ITL    }, // italian lira
 
	{    3, ',', 2002,      "NLG ", "",      0,  STR_CURR_NLG    }, // dutch gulden
 
	{   12, '.', CF_NOEURO, "",     " Kr",   1,  STR_CURR_NOK    }, // norwegian krone
 
	{    6, ' ', CF_NOEURO, "",     " zl",   1,  STR_CURR_PLN    }, // polish zloty
 
	{    5, '.', CF_NOEURO, "",     " Lei",  1,  STR_CURR_ROL    }, // romanian Lei
 
	{   50, ' ', CF_NOEURO, "",     " p",    1,  STR_CURR_RUR    }, // russian rouble
 
	{  352, '.', CF_NOEURO, "",     " SIT",  1,  STR_CURR_SIT    }, // slovenian tolar
 
	{   13, '.', CF_NOEURO, "",     " Kr",   1,  STR_CURR_SEK    }, // swedish krona
 
	{    3, '.', CF_NOEURO, "",     " YTL",  1,  STR_CURR_YTL    }, // turkish lira
 
	{   52, ',', CF_NOEURO, "",     " Sk",   1,  STR_CURR_SKK    }, // slovak koruna
 
	{    4, ',', CF_NOEURO, "R$ ",  "",      0,  STR_CURR_BRR    }, // brazil real
 
	{    1, ' ', CF_NOEURO, "",     "",      2,  STR_CURR_CUSTOM }, // custom currency
 
};
 

	
 
/* Array of currencies used by the system */
 
CurrencySpec _currency_specs[NUM_CURRENCY];
 

	
 
/**
 
 * These enums are only declared in order to make sens
 
 * out of the TTDPatch_To_OTTDIndex array that will follow
 
 * Every currency used by Ottd is there, just in case TTDPatch will
 
 * add those missing in its code
 
 **/
 
enum {
 
	CURR_GBP,
 
	CURR_USD,
 
	CURR_EUR,
 
	CURR_YEN,
 
	CURR_ATS,
 
	CURR_BEF,
 
	CURR_CHF,
 
	CURR_CZK,
 
	CURR_DEM,
 
	CURR_DKK,
 
	CURR_ESP,
 
	CURR_FIM,
 
	CURR_FRF,
 
	CURR_GRD,
 
	CURR_HUF,
 
	CURR_ISK,
 
	CURR_ITL,
 
	CURR_NLG,
 
	CURR_NOK,
 
	CURR_PLN,
 
	CURR_ROL,
 
	CURR_RUR,
 
	CURR_SIT,
 
	CURR_SEK,
 
	CURR_YTL,
 
};
 

	
 
/**
 
 * This array represent the position of OpenTTD's currencies,
 
 * compared to TTDPatch's ones.
 
 * When a grf sends currencies, they are based on the order defined by TTDPatch.
 
 * So, we must reindex them to our own order.
 
 **/
 
const byte TTDPatch_To_OTTDIndex[] =
 
{
 
	CURR_GBP,
 
	CURR_USD,
 
	CURR_FRF,
 
	CURR_DEM,
 
	CURR_YEN,
 
	CURR_ESP,
 
	CURR_HUF,
 
	CURR_PLN,
 
	CURR_ATS,
 
	CURR_BEF,
 
	CURR_DKK,
 
	CURR_FIM,
 
	CURR_GRD,
 
	CURR_CHF,
 
	CURR_NLG,
 
	CURR_ITL,
 
	CURR_SEK,
 
	CURR_RUR,
 
	CURR_EUR,
 
};
 

	
 
/**
 
 * Will return the ottd's index correspondance to
 
 * the ttdpatch's id.  If the id is bigger then the array,
 
 * it is  a grf written for ottd, thus returning the same id.
 
 * Only called from newgrf.c
 
 * @param grfcurr_id currency id coming from newgrf
 
 * @return the corrected index
 
 **/
 
byte GetNewgrfCurrencyIdConverted(byte grfcurr_id)
 
{
 
	return (grfcurr_id >= lengthof(TTDPatch_To_OTTDIndex)) ? grfcurr_id : TTDPatch_To_OTTDIndex[grfcurr_id];
 
}
 

	
 
/* get a mask of the allowed currencies depending on the year */
 
uint GetMaskOfAllowedCurrencies(void)
 
{
 
	uint mask = 0;
 
	uint i;
 

	
 
	for (i = 0; i < NUM_CURRENCY; i++) {
 
		Year to_euro = _currency_specs[i].to_euro;
 

	
 
		if (to_euro != CF_NOEURO && to_euro != CF_ISEURO && _cur_year >= to_euro) continue;
 
		if (to_euro == CF_ISEURO && _cur_year < 2000) continue;
 
		mask |= (1 << i);
 
	}
 
	mask |= (1 << CUSTOM_CURRENCY_ID); // always allow custom currency
 
	return mask;
 
}
 

	
 
/**
 
 * Verify if the currency chosen by the user is about to be converted to Euro
 
 **/
 
void CheckSwitchToEuro(void)
 
{
 
	if (_currency_specs[_opt.currency].to_euro != CF_NOEURO &&
 
			_currency_specs[_opt.currency].to_euro != CF_ISEURO &&
 
			_cur_year >= _currency_specs[_opt.currency].to_euro) {
 
		_opt.currency = 2; // this is the index of euro above.
 
		AddNewsItem(STR_EURO_INTRODUCE, NEWS_FLAGS(NM_NORMAL, 0, NT_ECONOMY, 0), 0, 0);
 
	}
 
}
 

	
 
/**
 
 * Called only from newgrf.c.  Will fill _currency_specs array with
 
 * default values from origin_currency_specs
 
 **/
 
void ResetCurrencies(void)
 
{
 
	memcpy(&_currency_specs, &origin_currency_specs, sizeof(origin_currency_specs));
 
}
 

	
 
/**
 
 * Build a list of currency names StringIDs to use in a dropdown list
 
 * @return Pointer to a (static) array of StringIDs
 
 */
 
StringID* BuildCurrencyDropdown(void)
 
{
 
	/* Allow room for all currencies, plus a terminator entry */
 
	static StringID names[CUSTOM_CURRENCY_ID];
 
	uint i;
 

	
 
	/* Add each name */
 
	for (i = 0; i < NUM_CURRENCY; i++) {
 
		names[i] = _currency_specs[i].name;
 
	}
 
	/* Terminate the list */
 
	names[i] = INVALID_STRING_ID;
 

	
 
	return names;
 
}
src/date.c
Show inline comments
 
deleted file
src/date.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "date.h"
 
#include "variables.h"
 
#include "macros.h"
 
#include "vehicle.h"
 
#include "network/network.h"
 
#include "network/network_data.h"
 
#include "network/network_server.h"
 
#include "functions.h"
 
#include "currency.h"
 

	
 
Year      _cur_year;
 
Month     _cur_month;
 
Date      _date;
 
DateFract _date_fract;
 

	
 

	
 
void SetDate(Date date)
 
{
 
	YearMonthDay ymd;
 

	
 
	_date = date;
 
	ConvertDateToYMD(date, &ymd);
 
	_cur_year = ymd.year;
 
	_cur_month = ymd.month;
 
#ifdef ENABLE_NETWORK
 
	_network_last_advertise_frame = 0;
 
	_network_need_advertise = true;
 
#endif /* ENABLE_NETWORK */
 
}
 

	
 
#define M(a, b) ((a << 5) | b)
 
static const uint16 _month_date_from_year_day[] = {
 
	M( 0, 1), M( 0, 2), M( 0, 3), M( 0, 4), M( 0, 5), M( 0, 6), M( 0, 7), M( 0, 8), M( 0, 9), M( 0, 10), M( 0, 11), M( 0, 12), M( 0, 13), M( 0, 14), M( 0, 15), M( 0, 16), M( 0, 17), M( 0, 18), M( 0, 19), M( 0, 20), M( 0, 21), M( 0, 22), M( 0, 23), M( 0, 24), M( 0, 25), M( 0, 26), M( 0, 27), M( 0, 28), M( 0, 29), M( 0, 30), M( 0, 31),
 
	M( 1, 1), M( 1, 2), M( 1, 3), M( 1, 4), M( 1, 5), M( 1, 6), M( 1, 7), M( 1, 8), M( 1, 9), M( 1, 10), M( 1, 11), M( 1, 12), M( 1, 13), M( 1, 14), M( 1, 15), M( 1, 16), M( 1, 17), M( 1, 18), M( 1, 19), M( 1, 20), M( 1, 21), M( 1, 22), M( 1, 23), M( 1, 24), M( 1, 25), M( 1, 26), M( 1, 27), M( 1, 28), M( 1, 29),
 
	M( 2, 1), M( 2, 2), M( 2, 3), M( 2, 4), M( 2, 5), M( 2, 6), M( 2, 7), M( 2, 8), M( 2, 9), M( 2, 10), M( 2, 11), M( 2, 12), M( 2, 13), M( 2, 14), M( 2, 15), M( 2, 16), M( 2, 17), M( 2, 18), M( 2, 19), M( 2, 20), M( 2, 21), M( 2, 22), M( 2, 23), M( 2, 24), M( 2, 25), M( 2, 26), M( 2, 27), M( 2, 28), M( 2, 29), M( 2, 30), M( 2, 31),
 
	M( 3, 1), M( 3, 2), M( 3, 3), M( 3, 4), M( 3, 5), M( 3, 6), M( 3, 7), M( 3, 8), M( 3, 9), M( 3, 10), M( 3, 11), M( 3, 12), M( 3, 13), M( 3, 14), M( 3, 15), M( 3, 16), M( 3, 17), M( 3, 18), M( 3, 19), M( 3, 20), M( 3, 21), M( 3, 22), M( 3, 23), M( 3, 24), M( 3, 25), M( 3, 26), M( 3, 27), M( 3, 28), M( 3, 29), M( 3, 30),
 
	M( 4, 1), M( 4, 2), M( 4, 3), M( 4, 4), M( 4, 5), M( 4, 6), M( 4, 7), M( 4, 8), M( 4, 9), M( 4, 10), M( 4, 11), M( 4, 12), M( 4, 13), M( 4, 14), M( 4, 15), M( 4, 16), M( 4, 17), M( 4, 18), M( 4, 19), M( 4, 20), M( 4, 21), M( 4, 22), M( 4, 23), M( 4, 24), M( 4, 25), M( 4, 26), M( 4, 27), M( 4, 28), M( 4, 29), M( 4, 30), M( 4, 31),
 
	M( 5, 1), M( 5, 2), M( 5, 3), M( 5, 4), M( 5, 5), M( 5, 6), M( 5, 7), M( 5, 8), M( 5, 9), M( 5, 10), M( 5, 11), M( 5, 12), M( 5, 13), M( 5, 14), M( 5, 15), M( 5, 16), M( 5, 17), M( 5, 18), M( 5, 19), M( 5, 20), M( 5, 21), M( 5, 22), M( 5, 23), M( 5, 24), M( 5, 25), M( 5, 26), M( 5, 27), M( 5, 28), M( 5, 29), M( 5, 30),
 
	M( 6, 1), M( 6, 2), M( 6, 3), M( 6, 4), M( 6, 5), M( 6, 6), M( 6, 7), M( 6, 8), M( 6, 9), M( 6, 10), M( 6, 11), M( 6, 12), M( 6, 13), M( 6, 14), M( 6, 15), M( 6, 16), M( 6, 17), M( 6, 18), M( 6, 19), M( 6, 20), M( 6, 21), M( 6, 22), M( 6, 23), M( 6, 24), M( 6, 25), M( 6, 26), M( 6, 27), M( 6, 28), M( 6, 29), M( 6, 30), M( 6, 31),
 
	M( 7, 1), M( 7, 2), M( 7, 3), M( 7, 4), M( 7, 5), M( 7, 6), M( 7, 7), M( 7, 8), M( 7, 9), M( 7, 10), M( 7, 11), M( 7, 12), M( 7, 13), M( 7, 14), M( 7, 15), M( 7, 16), M( 7, 17), M( 7, 18), M( 7, 19), M( 7, 20), M( 7, 21), M( 7, 22), M( 7, 23), M( 7, 24), M( 7, 25), M( 7, 26), M( 7, 27), M( 7, 28), M( 7, 29), M( 7, 30), M( 7, 31),
 
	M( 8, 1), M( 8, 2), M( 8, 3), M( 8, 4), M( 8, 5), M( 8, 6), M( 8, 7), M( 8, 8), M( 8, 9), M( 8, 10), M( 8, 11), M( 8, 12), M( 8, 13), M( 8, 14), M( 8, 15), M( 8, 16), M( 8, 17), M( 8, 18), M( 8, 19), M( 8, 20), M( 8, 21), M( 8, 22), M( 8, 23), M( 8, 24), M( 8, 25), M( 8, 26), M( 8, 27), M( 8, 28), M( 8, 29), M( 8, 30),
 
	M( 9, 1), M( 9, 2), M( 9, 3), M( 9, 4), M( 9, 5), M( 9, 6), M( 9, 7), M( 9, 8), M( 9, 9), M( 9, 10), M( 9, 11), M( 9, 12), M( 9, 13), M( 9, 14), M( 9, 15), M( 9, 16), M( 9, 17), M( 9, 18), M( 9, 19), M( 9, 20), M( 9, 21), M( 9, 22), M( 9, 23), M( 9, 24), M( 9, 25), M( 9, 26), M( 9, 27), M( 9, 28), M( 9, 29), M( 9, 30), M( 9, 31),
 
	M(10, 1), M(10, 2), M(10, 3), M(10, 4), M(10, 5), M(10, 6), M(10, 7), M(10, 8), M(10, 9), M(10, 10), M(10, 11), M(10, 12), M(10, 13), M(10, 14), M(10, 15), M(10, 16), M(10, 17), M(10, 18), M(10, 19), M(10, 20), M(10, 21), M(10, 22), M(10, 23), M(10, 24), M(10, 25), M(10, 26), M(10, 27), M(10, 28), M(10, 29), M(10, 30),
 
	M(11, 1), M(11, 2), M(11, 3), M(11, 4), M(11, 5), M(11, 6), M(11, 7), M(11, 8), M(11, 9), M(11, 10), M(11, 11), M(11, 12), M(11, 13), M(11, 14), M(11, 15), M(11, 16), M(11, 17), M(11, 18), M(11, 19), M(11, 20), M(11, 21), M(11, 22), M(11, 23), M(11, 24), M(11, 25), M(11, 26), M(11, 27), M(11, 28), M(11, 29), M(11, 30), M(11, 31),
 
};
 
#undef M
 

	
 
enum {
 
	ACCUM_JAN = 0,
 
	ACCUM_FEB = ACCUM_JAN + 31,
 
	ACCUM_MAR = ACCUM_FEB + 29,
 
	ACCUM_APR = ACCUM_MAR + 31,
 
	ACCUM_MAY = ACCUM_APR + 30,
 
	ACCUM_JUN = ACCUM_MAY + 31,
 
	ACCUM_JUL = ACCUM_JUN + 30,
 
	ACCUM_AUG = ACCUM_JUL + 31,
 
	ACCUM_SEP = ACCUM_AUG + 31,
 
	ACCUM_OCT = ACCUM_SEP + 30,
 
	ACCUM_NOV = ACCUM_OCT + 31,
 
	ACCUM_DEC = ACCUM_NOV + 30,
 
};
 

	
 
static const uint16 _accum_days_for_month[] = {
 
	ACCUM_JAN, ACCUM_FEB, ACCUM_MAR, ACCUM_APR,
 
	ACCUM_MAY, ACCUM_JUN, ACCUM_JUL, ACCUM_AUG,
 
	ACCUM_SEP, ACCUM_OCT, ACCUM_NOV, ACCUM_DEC,
 
};
 

	
 
static inline bool IsLeapYear(Year yr)
 
{
 
	return yr % 4 == 0 && (yr % 100 != 0 || yr % 400 == 0);
 
}
 

	
 
/**
 
 * Converts a Date to a Year, Month & Day.
 
 * @param date the date to convert from
 
 * @param ymd  the year, month and day to write to
 
 */
 
void ConvertDateToYMD(Date date, YearMonthDay *ymd)
 
{
 
	/*
 
	 * Year determination in multiple steps to account for leap
 
	 * years. First do the large steps, then the smaller ones.
 
	 */
 

	
 
	/* There are 97 leap years in 400 years */
 
	Year yr = 400 * (date / (365 * 400 + 97));
 
	int rem = date % (365 * 400 + 97);
 
	uint16 x;
 

	
 
	if (rem >= 365 * 100 + 25) {
 
		/* There are 25 leap years in the first 100 years after
 
		 * every 400th year, as every 400th year is a leap year */
 
		yr  += 100;
 
		rem -= 365 * 100 + 25;
 

	
 
		/* There are 24 leap years in the next couple of 100 years */
 
		yr += 100 * (rem / (365 * 100 + 24));
 
		rem = (rem % (365 * 100 + 24));
 
	}
 

	
 
	if (!IsLeapYear(yr) && rem >= 365 * 4) {
 
		/* The first 4 year of the century are not always a leap year */
 
		yr  += 4;
 
		rem -= 365 * 4;
 
	}
 

	
 
	/* There is 1 leap year every 4 years */
 
	yr += 4 * (rem / (365 * 4 + 1));
 
	rem = rem % (365 * 4 + 1);
 

	
 
	/* The last (max 3) years to account for; the first one
 
	 * can be, but is not necessarily a leap year */
 
	while (rem >= (IsLeapYear(yr) ? 366 : 365)) {
 
		rem -= IsLeapYear(yr) ? 366 : 365;
 
		yr++;
 
	}
 

	
 
	/* Skip the 29th of February in non-leap years */
 
	if (!IsLeapYear(yr) && rem >= ACCUM_MAR - 1) rem++;
 

	
 
	ymd->year = yr;
 

	
 
	x = _month_date_from_year_day[rem];
 
	ymd->month = x >> 5;
 
	ymd->day = x & 0x1F;
 
}
 

	
 
/**
 
 * Converts a tupe of Year, Month and Day to a Date.
 
 * @param year  is a number between 0..MAX_YEAR
 
 * @param month is a number between 0..11
 
 * @param day   is a number between 1..31
 
 */
 
Date ConvertYMDToDate(Year year, Month month, Day day)
 
{
 
	/*
 
	 * Each passed leap year adds one day to the 'day count'.
 
	 *
 
	 * A special case for the year 0 as no year has been passed,
 
	 * but '(year - 1) / 4' does not yield '-1' to counteract the
 
	 * '+1' at the end of the formula as divisions round to zero.
 
	 */
 
	int nr_of_leap_years = (year == 0) ? 0 : ((year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400 + 1);
 

	
 
	/* Day-offset in a leap year */
 
	int days = _accum_days_for_month[month] + day - 1;
 

	
 
	/* Account for the missing of the 29th of February in non-leap years */
 
	if (!IsLeapYear(year) && days >= ACCUM_MAR) days--;
 

	
 
	return year * 365 + nr_of_leap_years + days;
 
}
 

	
 
/** Functions used by the IncreaseDate function */
 

	
 
extern void OnNewDay_Train(Vehicle *v);
 
extern void OnNewDay_RoadVeh(Vehicle *v);
 
extern void OnNewDay_Aircraft(Vehicle *v);
 
extern void OnNewDay_Ship(Vehicle *v);
 
static void OnNewDay_EffectVehicle(Vehicle *v) { /* empty */ }
 
extern void OnNewDay_DisasterVehicle(Vehicle *v);
 

	
 
typedef void OnNewVehicleDayProc(Vehicle *v);
 

	
 
static OnNewVehicleDayProc * _on_new_vehicle_day_proc[] = {
 
	OnNewDay_Train,
 
	OnNewDay_RoadVeh,
 
	OnNewDay_Ship,
 
	OnNewDay_Aircraft,
 
	OnNewDay_EffectVehicle,
 
	OnNewDay_DisasterVehicle,
 
};
 

	
 
extern void WaypointsDailyLoop(void);
 
extern void TextMessageDailyLoop(void);
 
extern void EnginesDailyLoop(void);
 
extern void DisasterDailyLoop(void);
 

	
 
extern void PlayersMonthlyLoop(void);
 
extern void EnginesMonthlyLoop(void);
 
extern void TownsMonthlyLoop(void);
 
extern void IndustryMonthlyLoop(void);
 
extern void StationMonthlyLoop(void);
 

	
 
extern void PlayersYearlyLoop(void);
 
extern void TrainsYearlyLoop(void);
 
extern void RoadVehiclesYearlyLoop(void);
 
extern void AircraftYearlyLoop(void);
 
extern void ShipsYearlyLoop(void);
 

	
 
extern void ShowEndGameChart(void);
 

	
 

	
 
static const Month _autosave_months[] = {
 
	 0, // never
 
	 1, // every month
 
	 3, // every 3 months
 
	 6, // every 6 months
 
	12, // every 12 months
 
};
 

	
 
/**
 
 * Runs the day_proc for every DAY_TICKS vehicle starting at daytick.
 
 */
 
static void RunVehicleDayProc(uint daytick)
 
{
 
	uint total = GetMaxVehicleIndex() + 1;
 
	uint i;
 

	
 
	for (i = daytick; i < total; i += DAY_TICKS) {
 
		Vehicle *v = GetVehicle(i);
 

	
 
		if (IsValidVehicle(v)) _on_new_vehicle_day_proc[v->type - 0x10](v);
 
	}
 
}
 

	
 
void IncreaseDate(void)
 
{
 
	YearMonthDay ymd;
 

	
 
	if (_game_mode == GM_MENU) {
 
		_tick_counter++;
 
		return;
 
	}
 

	
 
	RunVehicleDayProc(_date_fract);
 

	
 
	/* increase day, and check if a new day is there? */
 
	_tick_counter++;
 

	
 
	_date_fract++;
 
	if (_date_fract < DAY_TICKS) return;
 
	_date_fract = 0;
 

	
 
	/* yeah, increase day counter and call various daily loops */
 
	_date++;
 

	
 
	TextMessageDailyLoop();
 

	
 
	DisasterDailyLoop();
 
	WaypointsDailyLoop();
 

	
 
	if (_game_mode != GM_MENU) {
 
		InvalidateWindowWidget(WC_STATUS_BAR, 0, 0);
 
		EnginesDailyLoop();
 
	}
 

	
 
	/* check if we entered a new month? */
 
	ConvertDateToYMD(_date, &ymd);
 
	if (ymd.month == _cur_month) return;
 
	_cur_month = ymd.month;
 

	
 
	/* yes, call various monthly loops */
 
	if (_game_mode != GM_MENU) {
 
		if (_opt.autosave != 0 && (_cur_month % _autosave_months[_opt.autosave]) == 0) {
 
			_do_autosave = true;
 
			RedrawAutosave();
 
		}
 

	
 
		PlayersMonthlyLoop();
 
		EnginesMonthlyLoop();
 
		TownsMonthlyLoop();
 
		IndustryMonthlyLoop();
 
		StationMonthlyLoop();
 
		if (_network_server) NetworkServerMonthlyLoop();
 
	}
 

	
 
	/* check if we entered a new year? */
 
	if (ymd.year == _cur_year) return;
 
	_cur_year = ymd.year;
 

	
 
	/* yes, call various yearly loops */
 
	PlayersYearlyLoop();
 
	TrainsYearlyLoop();
 
	RoadVehiclesYearlyLoop();
 
	AircraftYearlyLoop();
 
	ShipsYearlyLoop();
 
	if (_network_server) NetworkServerYearlyLoop();
 

	
 
	/* check if we reached end of the game */
 
	if (_cur_year == _patches.ending_year) {
 
			ShowEndGameChart();
 
	/* check if we reached the maximum year, decrement dates by a year */
 
	} else if (_cur_year == MAX_YEAR + 1) {
 
		Vehicle *v;
 
		uint days_this_year;
 

	
 
		_cur_year--;
 
		days_this_year = IsLeapYear(_cur_year) ? 366 : 365;
 
		_date -= days_this_year;
 
		FOR_ALL_VEHICLES(v) v->date_of_last_service -= days_this_year;
 

	
 
		/* Because the _date wraps here, and text-messages expire by game-days, we have to clean out
 
		 *  all of them if the date is set back, else those messages will hang for ever */
 
		InitTextMessage();
 
	}
 

	
 
	if (_patches.auto_euro) CheckSwitchToEuro();
 
}
src/debug.c
Show inline comments
 
deleted file
src/debug.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include <stdio.h>
 
#include <stdarg.h>
 
#include "openttd.h"
 
#include "console.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "string.h"
 

	
 
int _debug_ai_level;
 
int _debug_driver_level;
 
int _debug_grf_level;
 
int _debug_map_level;
 
int _debug_misc_level;
 
int _debug_ms_level;
 
int _debug_net_level;
 
int _debug_sprite_level;
 
int _debug_oldloader_level;
 
int _debug_ntp_level;
 
int _debug_npf_level;
 
int _debug_yapf_level;
 
int _debug_freetype_level;
 
int _debug_sl_level;
 

	
 

	
 
typedef struct DebugLevel {
 
	const char *name;
 
	int *level;
 
} DebugLevel;
 

	
 
#define DEBUG_LEVEL(x) { #x, &_debug_##x##_level }
 
	static const DebugLevel debug_level[] = {
 
	DEBUG_LEVEL(ai),
 
	DEBUG_LEVEL(driver),
 
	DEBUG_LEVEL(grf),
 
	DEBUG_LEVEL(map),
 
	DEBUG_LEVEL(misc),
 
	DEBUG_LEVEL(ms),
 
	DEBUG_LEVEL(net),
 
	DEBUG_LEVEL(sprite),
 
	DEBUG_LEVEL(oldloader),
 
	DEBUG_LEVEL(ntp),
 
	DEBUG_LEVEL(npf),
 
	DEBUG_LEVEL(yapf),
 
	DEBUG_LEVEL(freetype),
 
	DEBUG_LEVEL(sl),
 
	};
 
#undef DEBUG_LEVEL
 

	
 
#if !defined(NO_DEBUG_MESSAGES)
 

	
 
/** Functionized DEBUG macro for compilers that don't support
 
 * variadic macros (__VA_ARGS__) such as...yes MSVC2003 and lower */
 
#if defined(NO_VARARG_MACRO)
 
void CDECL DEBUG(int name, int level, ...)
 
{
 
	va_list va;
 
	const char *dbg;
 
	const DebugLevel *dl = &debug_level[name];
 

	
 
	if (level != 0 && *dl->level < level) return;
 
	dbg = dl->name;
 
	va_start(va, level);
 
#else
 
void CDECL debug(const char *dbg, ...)
 
{
 
	va_list va;
 
	va_start(va, dbg);
 
#endif /* NO_VARARG_MACRO */
 
	{
 
		const char *s;
 
		char buf[1024];
 

	
 
		s = va_arg(va, const char*);
 
		vsnprintf(buf, lengthof(buf), s, va);
 
		va_end(va);
 
		fprintf(stderr, "dbg: [%s] %s\n", dbg, buf);
 
		IConsoleDebug(dbg, buf);
 
	}
 
}
 
#endif /* NO_DEBUG_MESSAGES */
 

	
 
void SetDebugString(const char *s)
 
{
 
	int v;
 
	char *end;
 
	const char *t;
 

	
 
	// global debugging level?
 
	if (*s >= '0' && *s <= '9') {
 
		const DebugLevel *i;
 

	
 
		v = strtoul(s, &end, 0);
 
		s = end;
 

	
 
		for (i = debug_level; i != endof(debug_level); ++i) *i->level = v;
 
	}
 

	
 
	// individual levels
 
	for (;;) {
 
		const DebugLevel *i;
 
		int *p;
 

	
 
		// skip delimiters
 
		while (*s == ' ' || *s == ',' || *s == '\t') s++;
 
		if (*s == '\0') break;
 

	
 
		t = s;
 
		while (*s >= 'a' && *s <= 'z') s++;
 

	
 
		// check debugging levels
 
		p = NULL;
 
		for (i = debug_level; i != endof(debug_level); ++i)
 
			if (s == t + strlen(i->name) && strncmp(t, i->name, s - t) == 0) {
 
				p = i->level;
 
				break;
 
			}
 

	
 
		if (*s == '=') s++;
 
		v = strtoul(s, &end, 0);
 
		s = end;
 
		if (p != NULL) {
 
			*p = v;
 
		} else {
 
			ShowInfoF("Unknown debug level '%.*s'", s - t, t);
 
			return;
 
		}
 
	}
 
}
 

	
 
/** Print out the current debug-level
 
 * Just return a string with the values of all the debug categorites
 
 * @return string with debug-levels
 
 */
 
const char *GetDebugString(void)
 
{
 
	const DebugLevel *i;
 
	static char dbgstr[100];
 
	char dbgval[20];
 

	
 
	memset(dbgstr, 0, sizeof(dbgstr));
 
	i = debug_level;
 
	snprintf(dbgstr, sizeof(dbgstr), "%s=%d", i->name, *i->level);
 

	
 
	for (i++; i != endof(debug_level); i++) {
 
		snprintf(dbgval, sizeof(dbgval), ", %s=%d", i->name, *i->level);
 
		ttd_strlcat(dbgstr, dbgval, sizeof(dbgstr));
 
	}
 

	
 
	return dbgstr;
 
}
src/dedicated.c
Show inline comments
 
deleted file
src/dedicated.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#if defined(UNIX) && !defined(__MORPHOS__)
 

	
 
#include "openttd.h"
 
#include "variables.h"
 

	
 
#include <sys/types.h>
 
#include <unistd.h>
 

	
 
void DedicatedFork(void)
 
{
 
	/* Fork the program */
 
	pid_t pid = fork();
 
	switch (pid) {
 
		case -1:
 
			perror("Unable to fork");
 
			exit(1);
 

	
 
		case 0: { // We're the child
 
			FILE* f;
 

	
 
			/* Open the log-file to log all stuff too */
 
			f = fopen(_log_file, "a");
 
			if (f == NULL) {
 
				perror("Unable to open logfile");
 
				exit(1);
 
			}
 
			/* Redirect stdout and stderr to log-file */
 
			if (dup2(fileno(f), fileno(stdout)) == -1) {
 
				perror("Rerouting stdout");
 
				exit(1);
 
			}
 
			if (dup2(fileno(f), fileno(stderr)) == -1) {
 
				perror("Rerouting stderr");
 
				exit(1);
 
			}
 
			break;
 
		}
 

	
 
		default:
 
			// We're the parent
 
			printf("Loading dedicated server...\n");
 
			printf("  - Forked to background with pid %d\n", pid);
 
			exit(0);
 
	}
 
}
 
#endif
 

	
 
#else
 

	
 
void DedicatedFork(void) {}
 

	
 
#endif /* ENABLE_NETWORK */
src/depot.c
Show inline comments
 
deleted file
src/depot.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "depot.h"
 
#include "functions.h"
 
#include "tile.h"
 
#include "map.h"
 
#include "table/strings.h"
 
#include "saveload.h"
 
#include "order.h"
 

	
 

	
 
/**
 
 * Called if a new block is added to the depot-pool
 
 */
 
static void DepotPoolNewBlock(uint start_item)
 
{
 
	Depot *d;
 

	
 
	/* 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 (d = GetDepot(start_item); d != NULL; d = (d->index + 1U < GetDepotPoolSize()) ? GetDepot(d->index + 1U) : NULL) d->index = start_item++;
 
}
 

	
 
DEFINE_OLD_POOL(Depot, Depot, DepotPoolNewBlock, NULL)
 

	
 

	
 
/**
 
 * Gets a depot from a tile
 
 *
 
 * @return Returns the depot if the tile had a depot, else it returns NULL
 
 */
 
Depot *GetDepotByTile(TileIndex tile)
 
{
 
	Depot *depot;
 

	
 
	FOR_ALL_DEPOTS(depot) {
 
		if (depot->xy == tile) return depot;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 * Allocate a new depot
 
 */
 
Depot *AllocateDepot(void)
 
{
 
	Depot *d;
 

	
 
	/* 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 (d = GetDepot(0); d != NULL; d = (d->index + 1U < GetDepotPoolSize()) ? GetDepot(d->index + 1U) : NULL) {
 
		if (!IsValidDepot(d)) {
 
			DepotID index = d->index;
 

	
 
			memset(d, 0, sizeof(Depot));
 
			d->index = index;
 

	
 
			return d;
 
		}
 
	}
 

	
 
	/* Check if we can add a block to the pool */
 
	if (AddBlockToPool(&_Depot_pool)) return AllocateDepot();
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 * Clean up a depot
 
 */
 
void DestroyDepot(Depot *depot)
 
{
 
	/* Clear the tile */
 
	DoClearSquare(depot->xy);
 

	
 
	/* Clear the depot from all order-lists */
 
	RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, depot->index);
 

	
 
	/* Delete the depot-window */
 
	DeleteWindowById(WC_VEHICLE_DEPOT, depot->xy);
 
}
 

	
 
void InitializeDepots(void)
 
{
 
	CleanPool(&_Depot_pool);
 
	AddBlockToPool(&_Depot_pool);
 
}
 

	
 

	
 
static const SaveLoad _depot_desc[] = {
 
	SLE_CONDVAR(Depot, xy,         SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(Depot, xy,         SLE_UINT32,                 6, SL_MAX_VERSION),
 
	    SLE_VAR(Depot, town_index, SLE_UINT16),
 
	SLE_END()
 
};
 

	
 
static void Save_DEPT(void)
 
{
 
	Depot *depot;
 

	
 
	FOR_ALL_DEPOTS(depot) {
 
		SlSetArrayIndex(depot->index);
 
		SlObject(depot, _depot_desc);
 
	}
 
}
 

	
 
static void Load_DEPT(void)
 
{
 
	int index;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		Depot *depot;
 

	
 
		if (!AddBlockIfNeeded(&_Depot_pool, index))
 
			error("Depots: failed loading savegame: too many depots");
 

	
 
		depot = GetDepot(index);
 
		SlObject(depot, _depot_desc);
 
	}
 
}
 

	
 
const ChunkHandler _depot_chunk_handlers[] = {
 
	{ 'DEPT', Save_DEPT, Load_DEPT, CH_ARRAY | CH_LAST},
 
};
src/depot_gui.c
Show inline comments
 
deleted file
src/depot_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "train.h"
 
#include "roadveh.h"
 
#include "ship.h"
 
#include "aircraft.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "vehicle.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "depot.h"
 
#include "vehicle_gui.h"
 
#include "station_map.h"
 
#include "newgrf_engine.h"
 

	
 
/*
 
 * Since all depot window sizes aren't the same, we need to modify sizes a little.
 
 * It's done with the following arrays of widget indexes. Each of them tells if a widget side should be moved and in what direction.
 
 * How long they should be moved and for what window types are controlled in ShowDepotWindow()
 
 */
 

	
 
/* Names of the widgets. Keep them in the same order as in the widget array */
 
enum DepotWindowWidgets {
 
	DEPOT_WIDGET_CLOSEBOX = 0,
 
	DEPOT_WIDGET_CAPTION,
 
	DEPOT_WIDGET_STICKY,
 
	DEPOT_WIDGET_SELL,
 
	DEPOT_WIDGET_SELL_CHAIN,
 
	DEPOT_WIDGET_SELL_ALL,
 
	DEPOT_WIDGET_AUTOREPLACE,
 
	DEPOT_WIDGET_MATRIX,
 
	DEPOT_WIDGET_V_SCROLL, // Vertical scrollbar
 
	DEPOT_WIDGET_H_SCROLL, // Horizontal scrollbar
 
	DEPOT_WIDGET_BUILD,
 
	DEPOT_WIDGET_CLONE,
 
	DEPOT_WIDGET_LOCATION,
 
	DEPOT_WIDGET_VEHICLE_LIST,
 
	DEPOT_WIDGET_STOP_ALL,
 
	DEPOT_WIDGET_START_ALL,
 
	DEPOT_WIDGET_RESIZE,
 
};
 

	
 
/* Widget array for all depot windows.
 
 * If a widget is needed in some windows only (like train specific), add it for all windows
 
 * and use HideWindowWidget in ShowDepotWindow() to remove it in the windows where it should not be
 
 * Keep the widget numbers in sync with the enum or really bad stuff will happen!!! */
 

	
 
/* When adding widgets, place them as you would place them for the ship depot and define how you want it to move in widget_moves[]
 
 * If you want a widget for one window only, set it to be hidden in ShowDepotWindow() for the windows where you don't want it
 
 * NOTE: the train only widgets are moved/resized in ShowDepotWindow() so they follow certain other widgets if they are moved to ensure that they stick together.
 
 *    Changing the size of those here will not have an effect at all. It should be done in ShowDepotWindow()
 
 */
 
static const Widget _depot_widgets[] = {
 
	{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW},            // DEPOT_WIDGET_CLOSEBOX
 
	{    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   292,     0,    13, 0x0,                 STR_018C_WINDOW_TITLE_DRAG_THIS},  // DEPOT_WIDGET_CAPTION
 
	{  WWT_STICKYBOX,     RESIZE_LR,    14,   293,   304,     0,    13, 0x0,                 STR_STICKY_BUTTON},                // DEPOT_WIDGET_STICKY
 

	
 
	/* Widgets are set up run-time */
 
	{     WWT_IMGBTN,    RESIZE_LRB,    14,   270,   292,    14,    37, 0x0,                 STR_NULL},                         // DEPOT_WIDGET_SELL
 
	{     WWT_IMGBTN,   RESIZE_LRTB,    14,   270,   292,    14,    37, SPR_SELL_CHAIN_TRAIN,STR_DRAG_WHOLE_TRAIN_TO_SELL_TIP}, // DEPOT_WIDGET_SELL_CHAIN, trains only
 
	{ WWT_PUSHIMGBTN,   RESIZE_LRTB,    14,   270,   292,    38,    60, 0x0,                 STR_NULL},                         // DEPOT_WIDGET_SELL_ALL
 
	{ WWT_PUSHIMGBTN,   RESIZE_LRTB,    14,   270,   292,    61,    83, 0x0,                 STR_NULL},                         // DEPOT_WIDGET_AUTOREPLACE
 

	
 
	{     WWT_MATRIX,     RESIZE_RB,    14,     0,   269,    14,    83, 0x0,                 STR_NULL},                         // DEPOT_WIDGET_MATRIX
 
	{  WWT_SCROLLBAR,    RESIZE_LRB,    14,   293,   304,    14,    83, 0x0,                 STR_0190_SCROLL_BAR_SCROLLS_LIST}, // DEPOT_WIDGET_V_SCROLL
 

	
 
	{ WWT_HSCROLLBAR,    RESIZE_RTB,    14,     0,   269,    72,    83, 0x0,                 STR_HSCROLL_BAR_SCROLLS_LIST},     // DEPOT_WIDGET_H_SCROLL, trains only
 

	
 
	/* The buttons in the bottom of the window. left and right is not important as they are later resized to be equal in size
 
	 * This calculation is based on right in DEPOT_WIDGET_LOCATION and it presumes left of DEPOT_WIDGET_BUILD is 0            */
 
	{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,    85,    84,    95, 0x0,                 STR_NULL},                         // DEPOT_WIDGET_BUILD
 
	{    WWT_TEXTBTN,     RESIZE_TB,    14,    86,   170,    84,    95, 0x0,                 STR_NULL},                         // DEPOT_WIDGET_CLONE
 
	{ WWT_PUSHTXTBTN,    RESIZE_RTB,    14,   171,   257,    84,    95, STR_00E4_LOCATION,   STR_NULL},                         // DEPOT_WIDGET_LOCATION
 
	{ WWT_PUSHTXTBTN,   RESIZE_LRTB,    14,   258,   269,    84,    95, 0x0,                 STR_NULL},                         // DEPOT_WIDGET_VEHICLE_LIST
 
	{ WWT_PUSHIMGBTN,   RESIZE_LRTB,    14,   270,   280,    84,    95, SPR_FLAG_VEH_STOPPED,STR_NULL},                         // DEPOT_WIDGET_STOP_ALL
 
	{ WWT_PUSHIMGBTN,   RESIZE_LRTB,    14,   281,   292,    84,    95, SPR_FLAG_VEH_RUNNING,STR_NULL},                         // DEPOT_WIDGET_START_ALL
 
	{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   293,   304,    84,    95, 0x0,                 STR_RESIZE_BUTTON},                // DEPOT_WIDGET_RESIZE
 
	{   WIDGETS_END},
 
};
 

	
 
static void DepotWndProc(Window *w, WindowEvent *e);
 

	
 
static const WindowDesc _train_depot_desc = {
 
	WDP_AUTO, WDP_AUTO, 305, 96,
 
	WC_VEHICLE_DEPOT,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_depot_widgets,
 
	DepotWndProc
 
};
 

	
 
static const WindowDesc _road_depot_desc = {
 
	WDP_AUTO, WDP_AUTO, 305, 96,
 
	WC_VEHICLE_DEPOT,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_depot_widgets,
 
	DepotWndProc
 
};
 

	
 
static const WindowDesc _ship_depot_desc = {
 
	WDP_AUTO, WDP_AUTO, 305, 96,
 
	WC_VEHICLE_DEPOT,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_depot_widgets,
 
	DepotWndProc
 
};
 

	
 
static const WindowDesc _aircraft_depot_desc = {
 
	WDP_AUTO, WDP_AUTO, 305, 96,
 
	WC_VEHICLE_DEPOT,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_depot_widgets,
 
	DepotWndProc
 
};
 

	
 
extern int WagonLengthToPixels(int len);
 

	
 
void CcCloneVehicle(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (!success) return;
 
	switch(GetVehicle(p1)->type) {
 
		case VEH_Train:    CcCloneTrain(   true, tile, p1, p2); break;
 
		case VEH_Road:     CcCloneRoadVeh( true, tile, p1, p2); break;
 
		case VEH_Ship:     CcCloneShip(    true, tile, p1, p2); break;
 
		case VEH_Aircraft: CcCloneAircraft(true, tile, p1, p2); break;
 
	}
 
}
 

	
 
static inline void ShowVehicleViewWindow(const Vehicle *v)
 
{
 
	switch (v->type) {
 
		case VEH_Train:    ShowTrainViewWindow(v);    break;
 
		case VEH_Road:     ShowRoadVehViewWindow(v);  break;
 
		case VEH_Ship:     ShowShipViewWindow(v);     break;
 
		case VEH_Aircraft: ShowAircraftViewWindow(v); break;
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
static void DepotSellAllConfirmationCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) {
 
		TileIndex tile = w->window_number;
 
		byte vehtype = WP(w, depot_d).type;
 
		DoCommandP(tile, vehtype, 0, NULL, CMD_DEPOT_SELL_ALL_VEHICLES);
 
	}
 
}
 

	
 
/** Draw a vehicle in the depot window in the box with the top left corner at x,y
 
 * @param *w Window to draw in
 
 * @param *v Vehicle to draw
 
 * @param x Left side of the box to draw in
 
 * @param y Top of the box to draw in
 
 */
 
static void DrawVehicleInDepot(Window *w, const Vehicle *v, int x, int y)
 
{
 
	byte diff_x = 0, diff_y = 0;
 

	
 
	switch (v->type) {
 
		case VEH_Train:
 
			DrawTrainImage(v, x + 21, y, w->hscroll.cap + 4, w->hscroll.pos, WP(w,depot_d).sel);
 

	
 
			/* Number of wagons relative to a standard length wagon (rounded up) */
 
			SetDParam(0, (v->u.rail.cached_total_length + 7) / 8);
 
			DrawStringRightAligned(w->widget[DEPOT_WIDGET_MATRIX].right - 1, y + 4, STR_TINY_BLACK, 0); // Draw the counter
 
			break;
 

	
 
		case VEH_Road:     DrawRoadVehImage( v, x + 24, y, WP(w, depot_d).sel); break;
 
		case VEH_Ship:     DrawShipImage(    v, x + 19, y, WP(w, depot_d).sel); break;
 
		case VEH_Aircraft: DrawAircraftImage(v, x + 12, y, WP(w, depot_d).sel); break;
 
		default: NOT_REACHED();
 
	}
 

	
 
	if (w->resize.step_height == 14) {
 
		/* VEH_Train and VEH_Road, which are low */
 
		diff_x = 15;
 
	} else {
 
		/* VEH_Ship and VEH_Aircraft, which are tall */
 
		diff_y = 12;
 
	}
 

	
 
	DrawSprite((v->vehstatus & VS_STOPPED) ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, x + diff_x, y + diff_y);
 

	
 
	SetDParam(0, v->unitnumber);
 
	DrawString(x, y + 2, (uint16)(v->max_age-366) >= v->age ? STR_00E2 : STR_00E3, 0);
 
}
 

	
 
static void DrawDepotWindow(Window *w)
 
{
 
	Vehicle **vl = WP(w, depot_d).vehicle_list;
 
	TileIndex tile = w->window_number;
 
	int x, y, i, hnum, max;
 
	uint16 num = WP(w, depot_d).engine_count;
 

	
 
	/* Set the row and number of boxes in each row based on the number of boxes drawn in the matrix */
 
	uint16 rows_in_display   = w->widget[DEPOT_WIDGET_MATRIX].data >> 8;
 
	uint16 boxes_in_each_row = w->widget[DEPOT_WIDGET_MATRIX].data & 0xFF;
 

	
 
	/* setup disabled buttons */
 
	SetWindowWidgetsDisabledState(w, !IsTileOwner(tile, _local_player),
 
		DEPOT_WIDGET_STOP_ALL,
 
		DEPOT_WIDGET_START_ALL,
 
		DEPOT_WIDGET_SELL,
 
		DEPOT_WIDGET_SELL_CHAIN,
 
		DEPOT_WIDGET_SELL_ALL,
 
		DEPOT_WIDGET_BUILD,
 
		DEPOT_WIDGET_CLONE,
 
		DEPOT_WIDGET_AUTOREPLACE,
 
		WIDGET_LIST_END);
 

	
 
	/* determine amount of items for scroller */
 
	if (WP(w, depot_d).type == VEH_Train) {
 
		hnum = 8;
 
		for (num = 0; num < WP(w, depot_d).engine_count; num++) {
 
			const Vehicle *v = vl[num];
 
			hnum = maxu(hnum, v->u.rail.cached_total_length);
 
		}
 
		/* Always have 1 empty row, so people can change the setting of the train */
 
		SetVScrollCount(w, WP(w, depot_d).engine_count + WP(w, depot_d).wagon_count + 1);
 
		SetHScrollCount(w, WagonLengthToPixels(hnum));
 
	} else {
 
		SetVScrollCount(w, (num + w->hscroll.cap - 1) / w->hscroll.cap);
 
	}
 

	
 
	/* locate the depot struct */
 
	if (WP(w, depot_d).type == VEH_Aircraft) {
 
		SetDParam(0, GetStationIndex(tile)); // Airport name
 
	} else {
 
		Depot *depot = GetDepotByTile(tile);
 
		assert(depot != NULL);
 

	
 
		SetDParam(0, depot->town_index);
 
	}
 

	
 
	DrawWindowWidgets(w);
 

	
 
	num = w->vscroll.pos * boxes_in_each_row;
 
	max = min(WP(w, depot_d).engine_count, num + (rows_in_display * boxes_in_each_row));
 

	
 
	for (x = 2, y = 15; num < max; y += w->resize.step_height, x = 2) { // Draw the rows
 
		byte i;
 

	
 
		for (i = 0; i < boxes_in_each_row && num < max; i++, num++, x += w->resize.step_width) {
 
			/* Draw all vehicles in the current row */
 
			const Vehicle *v = vl[num];
 
			DrawVehicleInDepot(w, v, x, y);
 
		}
 
	}
 

	
 
	max = min(WP(w, depot_d).engine_count + WP(w, depot_d).wagon_count, (w->vscroll.pos * boxes_in_each_row) + (rows_in_display * boxes_in_each_row));
 

	
 
	/* draw the train wagons, that do not have an engine in front */
 
	for (; num < max; num++, y += 14) {
 
		const Vehicle *v = WP(w, depot_d).wagon_list[num - WP(w, depot_d).engine_count];
 
		const Vehicle *u;
 

	
 
		DrawTrainImage(v, x + 50, y, w->hscroll.cap - 29, 0, WP(w,depot_d).sel);
 
		DrawString(x, y + 2, STR_8816, 0);
 

	
 
		/*Draw the train counter */
 
		i = 0;
 
		u = v;
 
		do i++; while ( (u=u->next) != NULL); // Determine length of train
 
		SetDParam(0, i);                      // Set the counter
 
		DrawStringRightAligned(w->widget[DEPOT_WIDGET_MATRIX].right - 1, y + 4, STR_TINY_BLACK, 0); // Draw the counter
 
	}
 
}
 

	
 
typedef struct GetDepotVehiclePtData {
 
	Vehicle *head;
 
	Vehicle *wagon;
 
} GetDepotVehiclePtData;
 

	
 
enum {
 
	MODE_ERROR        =  1,
 
	MODE_DRAG_VEHICLE =  0,
 
	MODE_SHOW_VEHICLE = -1,
 
	MODE_START_STOP   = -2,
 
};
 

	
 
static int GetVehicleFromDepotWndPt(const Window *w, int x, int y, Vehicle **veh, GetDepotVehiclePtData *d)
 
{
 
	Vehicle **vl = WP(w, depot_d).vehicle_list;
 
	uint xt, row, xm = 0, ym = 0;
 
	int pos, skip = 0;
 
	uint16 boxes_in_each_row = w->widget[DEPOT_WIDGET_MATRIX].data & 0xFF;
 

	
 
	if (WP(w, depot_d).type == VEH_Train) {
 
		xt = 0;
 
		x -= 23;
 
	} else {
 
		xt = x / w->resize.step_width;
 
		xm = x % w->resize.step_width;
 
		if (xt >= w->hscroll.cap) return MODE_ERROR;
 

	
 
		ym = (y - 14) % w->resize.step_height;
 
	}
 

	
 
	row = (y - 14) / w->resize.step_height;
 
	if (row >= w->vscroll.cap) return MODE_ERROR;
 

	
 
	pos = ((row + w->vscroll.pos) * boxes_in_each_row) + xt;
 

	
 
	if (WP(w, depot_d).engine_count + WP(w, depot_d).wagon_count <= pos) {
 
		if (WP(w, depot_d).type == VEH_Train) {
 
			d->head  = NULL;
 
			d->wagon = NULL;
 
			return MODE_DRAG_VEHICLE;
 
		} else {
 
			return MODE_ERROR; // empty block, so no vehicle is selected
 
		}
 
	}
 

	
 
	if (WP(w, depot_d).engine_count > pos) {
 
		*veh = vl[pos];
 
		skip = w->hscroll.pos;
 
	} else {
 
		vl = WP(w, depot_d).wagon_list;
 
		pos -= WP(w, depot_d).engine_count;
 
		*veh = vl[pos];
 
		/* free wagons don't have an initial loco. */
 
		x -= _traininfo_vehicle_width;
 
	}
 

	
 
	switch (WP(w, depot_d).type) {
 
		case VEH_Train: {
 
			Vehicle *v = *veh;
 
			d->head = d->wagon = v;
 

	
 
			/* either pressed the flag or the number, but only when it's a loco */
 
			if (x < 0 && IsFrontEngine(v)) return (x >= -10) ? MODE_START_STOP : MODE_SHOW_VEHICLE;
 

	
 
			skip = (skip * 8) / _traininfo_vehicle_width;
 
			x = (x * 8) / _traininfo_vehicle_width;
 

	
 
			/* Skip vehicles that are scrolled off the list */
 
			x += skip;
 

	
 
			/* find the vehicle in this row that was clicked */
 
			while (v != NULL && (x -= v->u.rail.cached_veh_length) >= 0) v = v->next;
 

	
 
			/* if an articulated part was selected, find its parent */
 
			while (v != NULL && IsArticulatedPart(v)) v = GetPrevVehicleInChain(v);
 

	
 
			d->wagon = v;
 

	
 
			return MODE_DRAG_VEHICLE;
 
			}
 
			break;
 

	
 
		case VEH_Road:
 
			if (xm >= 24) return MODE_DRAG_VEHICLE;
 
			if (xm <= 16) return MODE_SHOW_VEHICLE;
 
			break;
 

	
 
		case VEH_Ship:
 
			if (xm >= 19) return MODE_DRAG_VEHICLE;
 
			if (ym <= 10) return MODE_SHOW_VEHICLE;
 
			break;
 

	
 
		case VEH_Aircraft:
 
			if (xm >= 12) return MODE_DRAG_VEHICLE;
 
			if (ym <= 12) return MODE_SHOW_VEHICLE;
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 
	return MODE_START_STOP;
 
}
 

	
 
static void TrainDepotMoveVehicle(Vehicle *wagon, VehicleID sel, Vehicle *head)
 
{
 
	Vehicle *v;
 

	
 
	v = GetVehicle(sel);
 

	
 
	if (v == wagon) return;
 

	
 
	if (wagon == NULL) {
 
		if (head != NULL) wagon = GetLastVehicleInChain(head);
 
	} else  {
 
		wagon = GetPrevVehicleInChain(wagon);
 
		if (wagon == NULL) return;
 
	}
 

	
 
	if (wagon == v) return;
 

	
 
	DoCommandP(v->tile, v->index + ((wagon == NULL ? INVALID_VEHICLE : wagon->index) << 16), _ctrl_pressed ? 1 : 0, NULL, CMD_MOVE_RAIL_VEHICLE | CMD_MSG(STR_8837_CAN_T_MOVE_VEHICLE));
 
}
 

	
 
static void DepotClick(Window *w, int x, int y)
 
{
 
	GetDepotVehiclePtData gdvp;
 
	Vehicle *v = NULL;
 
	int mode = GetVehicleFromDepotWndPt(w, x, y, &v, &gdvp);
 

	
 
	/* share / copy orders */
 
	if (_thd.place_mode && mode <= 0) {
 
		_place_clicked_vehicle = (WP(w, depot_d).type == VEH_Train ? gdvp.head : v);
 
		return;
 
	}
 

	
 
	if (WP(w, depot_d).type == VEH_Train) v = gdvp.wagon;
 

	
 
	switch (mode) {
 
		case MODE_ERROR: // invalid
 
			return;
 

	
 
		case MODE_DRAG_VEHICLE: { // start dragging of vehicle
 
			VehicleID sel = WP(w, depot_d).sel;
 

	
 
			if (WP(w, depot_d).type == VEH_Train && sel != INVALID_VEHICLE) {
 
				WP(w,depot_d).sel = INVALID_VEHICLE;
 
				TrainDepotMoveVehicle(v, sel, gdvp.head);
 
			} else if (v != NULL) {
 
				int image;
 

	
 
				switch (WP(w, depot_d).type) {
 
					case VEH_Train:    image = GetTrainImage(v, DIR_W);    break;
 
					case VEH_Road:     image = GetRoadVehImage(v, DIR_W);  break;
 
					case VEH_Ship:     image = GetShipImage(v, DIR_W);     break;
 
					case VEH_Aircraft: image = GetAircraftImage(v, DIR_W); break;
 
					default: NOT_REACHED(); image = 0;
 
				}
 

	
 
				WP(w, depot_d).sel = v->index;
 
				SetWindowDirty(w);
 
				SetObjectToPlaceWnd(GetVehiclePalette(v) | image, 4, w);
 
			}
 
			}
 
			break;
 

	
 
		case MODE_SHOW_VEHICLE: // show info window
 
			ShowVehicleViewWindow(v);
 
			break;
 

	
 
		case MODE_START_STOP: { // click start/stop flag
 
			uint command;
 

	
 
			switch (WP(w, depot_d).type) {
 
				case VEH_Train:    command = CMD_START_STOP_TRAIN | CMD_MSG(STR_883B_CAN_T_STOP_START_TRAIN);          break;
 
				case VEH_Road:     command = CMD_START_STOP_ROADVEH | CMD_MSG(STR_9015_CAN_T_STOP_START_ROAD_VEHICLE); break;
 
				case VEH_Ship:     command = CMD_START_STOP_SHIP | CMD_MSG(STR_9818_CAN_T_STOP_START_SHIP);            break;
 
				case VEH_Aircraft: command = CMD_START_STOP_AIRCRAFT | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT);    break;
 
				default: NOT_REACHED(); command = 0;
 
			}
 
			DoCommandP(v->tile, v->index, 0, NULL, command);
 
			}
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
/**
 
 * Clones a vehicle
 
 * @param *v is the original vehicle to clone
 
 * @param *w is the window of the depot where the clone is build
 
 */
 
static void HandleCloneVehClick(const Vehicle *v, const Window *w)
 
{
 
	uint error_str;
 

	
 
	if (v == NULL) return;
 

	
 
	if (v->type == VEH_Train && !IsFrontEngine(v)) {
 
		v = GetFirstVehicleInChain(v);
 
		/* Do nothing when clicking on a train in depot with no loc attached */
 
		if (!IsFrontEngine(v)) return;
 
	}
 

	
 
	switch (v->type) {
 
		case VEH_Train:    error_str = CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE); break;
 
		case VEH_Road:     error_str = CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE);     break;
 
		case VEH_Ship:     error_str = CMD_MSG(STR_980D_CAN_T_BUILD_SHIP);             break;
 
		case VEH_Aircraft: error_str = CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT);         break;
 
		default: return;
 
	}
 

	
 
	DoCommandP(w->window_number, v->index, _ctrl_pressed ? 1 : 0, CcCloneVehicle, CMD_CLONE_VEHICLE | error_str);
 

	
 
	ResetObjectToPlace();
 
}
 

	
 
static void ClonePlaceObj(const Window *w)
 
{
 
	const Vehicle *v = CheckMouseOverVehicle();
 

	
 
	if (v != NULL) HandleCloneVehClick(v, w);
 
}
 

	
 
static void ResizeDepotButtons(Window *w)
 
{
 
	/* We got the widget moved around. Now we will make some widgets to fill the gap between some widgets in equal sizes */
 

	
 
	/* Make the buttons in the bottom equal in size */
 
	w->widget[DEPOT_WIDGET_BUILD].right    = w->widget[DEPOT_WIDGET_LOCATION].right / 3;
 
	w->widget[DEPOT_WIDGET_LOCATION].left  = w->widget[DEPOT_WIDGET_BUILD].right * 2;
 
	w->widget[DEPOT_WIDGET_CLONE].left     = w->widget[DEPOT_WIDGET_BUILD].right + 1;
 
	w->widget[DEPOT_WIDGET_CLONE].right    = w->widget[DEPOT_WIDGET_LOCATION].left - 1;
 

	
 
	if (WP(w, depot_d).type == VEH_Train) {
 
		/* Divide the size of DEPOT_WIDGET_SELL into two equally big buttons so DEPOT_WIDGET_SELL and DEPOT_WIDGET_SELL_CHAIN will get the same size.
 
		* This way it will stay the same even if DEPOT_WIDGET_SELL_CHAIN is resized for some reason                                                  */
 
		w->widget[DEPOT_WIDGET_SELL_CHAIN].top    = ((w->widget[DEPOT_WIDGET_SELL_CHAIN].bottom - w->widget[DEPOT_WIDGET_SELL].top) / 2) + w->widget[DEPOT_WIDGET_SELL].top;
 
		w->widget[DEPOT_WIDGET_SELL].bottom     = w->widget[DEPOT_WIDGET_SELL_CHAIN].top - 1;
 
	}
 
}
 

	
 
/* Function to set up vehicle specific sprites and strings
 
 * Only use this if it's the same widget, that's used for more than one vehicle type and it needs different text/sprites
 
 * Vehicle specific text/sprites, that's in a widget, that's only shown for one vehicle type (like sell whole train) is set in the widget array
 
 */
 
static void SetupStringsForDepotWindow(Window *w, byte type)
 
{
 
	switch (type) {
 
		case VEH_Train:
 
			w->widget[DEPOT_WIDGET_CAPTION].data      = STR_8800_TRAIN_DEPOT;
 
			w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_TRAIN_TIP;
 
			w->widget[DEPOT_WIDGET_START_ALL].tooltips=	STR_MASS_START_DEPOT_TRAIN_TIP;
 
			w->widget[DEPOT_WIDGET_SELL].tooltips     = STR_8841_DRAG_TRAIN_VEHICLE_TO_HERE;
 
			w->widget[DEPOT_WIDGET_SELL_ALL].tooltips =	STR_DEPOT_SELL_ALL_BUTTON_TRAIN_TIP;
 
			w->widget[DEPOT_WIDGET_MATRIX].tooltips   = STR_883F_TRAINS_CLICK_ON_TRAIN_FOR;
 

	
 
			w->widget[DEPOT_WIDGET_BUILD].data        = STR_8815_NEW_VEHICLES;
 
			w->widget[DEPOT_WIDGET_BUILD].tooltips    = STR_8840_BUILD_NEW_TRAIN_VEHICLE;
 
			w->widget[DEPOT_WIDGET_CLONE].data        = STR_CLONE_TRAIN;
 
			w->widget[DEPOT_WIDGET_CLONE].tooltips    = STR_CLONE_TRAIN_DEPOT_INFO;
 

	
 
			w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_8842_CENTER_MAIN_VIEW_ON_TRAIN;
 
			w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_TRAIN;
 
			w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_TRAIN_TIP;
 
			w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_TRAIN_TIP;
 

	
 
			/* Sprites */
 
			w->widget[DEPOT_WIDGET_SELL].data        = SPR_SELL_TRAIN;
 
			w->widget[DEPOT_WIDGET_SELL_ALL].data    = SPR_SELL_ALL_TRAIN;
 
			w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_TRAIN;
 
			break;
 

	
 
		case VEH_Road:
 
			w->widget[DEPOT_WIDGET_CAPTION].data      = STR_9003_ROAD_VEHICLE_DEPOT;
 
			w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_ROADVEH_TIP;
 
			w->widget[DEPOT_WIDGET_START_ALL].tooltips=	STR_MASS_START_DEPOT_ROADVEH_TIP;
 
			w->widget[DEPOT_WIDGET_SELL].tooltips     = STR_9024_DRAG_ROAD_VEHICLE_TO_HERE;
 
			w->widget[DEPOT_WIDGET_SELL_ALL].tooltips =	STR_DEPOT_SELL_ALL_BUTTON_ROADVEH_TIP;
 
			w->widget[DEPOT_WIDGET_MATRIX].tooltips   = STR_9022_VEHICLES_CLICK_ON_VEHICLE;
 

	
 
			w->widget[DEPOT_WIDGET_BUILD].data        = STR_9004_NEW_VEHICLES;
 
			w->widget[DEPOT_WIDGET_BUILD].tooltips    = STR_9023_BUILD_NEW_ROAD_VEHICLE;
 
			w->widget[DEPOT_WIDGET_CLONE].data        = STR_CLONE_ROAD_VEHICLE;
 
			w->widget[DEPOT_WIDGET_CLONE].tooltips    = STR_CLONE_ROAD_VEHICLE_DEPOT_INFO;
 

	
 
			w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_9025_CENTER_MAIN_VIEW_ON_ROAD;
 
			w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_LORRY;
 
			w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_ROADVEH_TIP;
 
			w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_ROADVEH_TIP;
 

	
 
			/* Sprites */
 
			w->widget[DEPOT_WIDGET_SELL].data        = SPR_SELL_ROADVEH;
 
			w->widget[DEPOT_WIDGET_SELL_ALL].data    = SPR_SELL_ALL_ROADVEH;
 
			w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_ROADVEH;
 
			break;
 

	
 
		case VEH_Ship:
 
			w->widget[DEPOT_WIDGET_CAPTION].data      = STR_9803_SHIP_DEPOT;
 
			w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_DEPOT_SHIP_TIP;
 
			w->widget[DEPOT_WIDGET_START_ALL].tooltips=	STR_MASS_START_DEPOT_SHIP_TIP;
 
			w->widget[DEPOT_WIDGET_SELL].tooltips     = STR_9821_DRAG_SHIP_TO_HERE_TO_SELL;
 
			w->widget[DEPOT_WIDGET_SELL_ALL].tooltips =	STR_DEPOT_SELL_ALL_BUTTON_SHIP_TIP;
 
			w->widget[DEPOT_WIDGET_MATRIX].tooltips   = STR_981F_SHIPS_CLICK_ON_SHIP_FOR;
 

	
 
			w->widget[DEPOT_WIDGET_BUILD].data        = STR_9804_NEW_SHIPS;
 
			w->widget[DEPOT_WIDGET_BUILD].tooltips    = STR_9820_BUILD_NEW_SHIP;
 
			w->widget[DEPOT_WIDGET_CLONE].data        = STR_CLONE_SHIP;
 
			w->widget[DEPOT_WIDGET_CLONE].tooltips    = STR_CLONE_SHIP_DEPOT_INFO;
 

	
 
			w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_9822_CENTER_MAIN_VIEW_ON_SHIP;
 
			w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_SHIP;
 
			w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_SHIP_TIP;
 
			w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_SHIP_TIP;
 

	
 
			/* Sprites */
 
			w->widget[DEPOT_WIDGET_SELL].data        = SPR_SELL_SHIP;
 
			w->widget[DEPOT_WIDGET_SELL_ALL].data    = SPR_SELL_ALL_SHIP;
 
			w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_SHIP;
 
			break;
 

	
 
		case VEH_Aircraft:
 
			w->widget[DEPOT_WIDGET_CAPTION].data      = STR_A002_AIRCRAFT_HANGAR;
 
			w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_HANGAR_TIP;
 
			w->widget[DEPOT_WIDGET_START_ALL].tooltips=	STR_MASS_START_HANGAR_TIP;
 
			w->widget[DEPOT_WIDGET_SELL].tooltips     = STR_A023_DRAG_AIRCRAFT_TO_HERE_TO;
 
			w->widget[DEPOT_WIDGET_SELL_ALL].tooltips =	STR_DEPOT_SELL_ALL_BUTTON_AIRCRAFT_TIP;
 
			w->widget[DEPOT_WIDGET_MATRIX].tooltips   = STR_A021_AIRCRAFT_CLICK_ON_AIRCRAFT;
 

	
 
			w->widget[DEPOT_WIDGET_BUILD].data        = STR_A003_NEW_AIRCRAFT;
 
			w->widget[DEPOT_WIDGET_BUILD].tooltips    = STR_A022_BUILD_NEW_AIRCRAFT;
 
			w->widget[DEPOT_WIDGET_CLONE].data        = STR_CLONE_AIRCRAFT;
 
			w->widget[DEPOT_WIDGET_CLONE].tooltips    = STR_CLONE_AIRCRAFT_INFO_HANGAR_WINDOW;
 

	
 
			w->widget[DEPOT_WIDGET_LOCATION].tooltips = STR_A024_CENTER_MAIN_VIEW_ON_HANGAR;
 
			w->widget[DEPOT_WIDGET_VEHICLE_LIST].data = STR_PLANE;
 
			w->widget[DEPOT_WIDGET_VEHICLE_LIST].tooltips = STR_DEPOT_VEHICLE_ORDER_LIST_AIRCRAFT_TIP;
 
			w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_AIRCRAFT_TIP;
 

	
 
			/* Sprites */
 
			w->widget[DEPOT_WIDGET_SELL].data        = SPR_SELL_AIRCRAFT;
 
			w->widget[DEPOT_WIDGET_SELL_ALL].data    = SPR_SELL_ALL_AIRCRAFT;
 
			w->widget[DEPOT_WIDGET_AUTOREPLACE].data = SPR_REPLACE_AIRCRAFT;
 
			break;
 
	}
 
}
 

	
 
static void CreateDepotListWindow(Window *w, byte type)
 
{
 
	WP(w, depot_d).type = type;
 
	_backup_orders_tile = 0;
 

	
 
	/* Resize the window according to the vehicle type */
 
	switch (type) {
 
		default: NOT_REACHED();
 
		case VEH_Train:
 
			w->vscroll.cap = 6;
 
			w->hscroll.cap = 10 * 29;
 
			w->resize.step_width = 1;
 
			ResizeWindow(w, 56, 26);
 
			break;
 

	
 
		case VEH_Road:
 
			w->vscroll.cap = 5;
 
			w->hscroll.cap = 5;
 
			w->resize.step_width = 56;
 
			ResizeWindow(w, 10, 0);
 
			break;
 

	
 
		case VEH_Ship:
 
			w->vscroll.cap = 3;
 
			w->hscroll.cap = 3;
 
			w->resize.step_width = 90;
 
			ResizeWindow(w, 0, 2);
 
			break;
 

	
 
		case VEH_Aircraft:
 
			w->vscroll.cap = 3;
 
			w->hscroll.cap = 4;
 
			w->resize.step_width = 74;
 
			ResizeWindow(w, 26, 2);
 
			break;
 
	}
 

	
 
	/* Set the minimum window size to the current window size */
 
	w->resize.width = w->width;
 
	w->resize.height = w->height;
 
	w->resize.step_height = GetVehicleListHeight(type);
 

	
 
	SetupStringsForDepotWindow(w, type);
 

	
 
	w->widget[DEPOT_WIDGET_MATRIX].data =
 
		(w->vscroll.cap * 0x100) // number of rows to draw on the background
 
		+ (type == VEH_Train ? 1 : w->hscroll.cap); // number of boxes in each row. Trains always have just one
 

	
 

	
 
	SetWindowWidgetsHiddenState(w, type != VEH_Train,
 
		DEPOT_WIDGET_H_SCROLL,
 
		DEPOT_WIDGET_SELL_CHAIN,
 
		WIDGET_LIST_END);
 

	
 
	/* The train depot has a horizontal scroller, make the matrix that much shorter to fit */
 
	if (type == VEH_Train) w->widget[DEPOT_WIDGET_MATRIX].bottom -= 12;
 
	ResizeDepotButtons(w);
 
}
 

	
 
void DepotSortList(Vehicle **v, uint16 length);
 

	
 
static void DepotWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_CREATE:
 
			WP(w, depot_d).sel = INVALID_VEHICLE;
 
			WP(w, depot_d).vehicle_list  = NULL;
 
			WP(w, depot_d).wagon_list    = NULL;
 
			WP(w, depot_d).engine_count  = 0;
 
			WP(w, depot_d).wagon_count   = 0;
 
			WP(w, depot_d).generate_list = true;
 
			break;
 

	
 
		case WE_INVALIDATE_DATA:
 
			WP(w, depot_d).generate_list = true;
 
			break;
 

	
 
		case WE_PAINT:
 
			if (WP(w, depot_d).generate_list) {
 
				/* Generate the vehicle list
 
				 * It's ok to use the wagon pointers for non-trains as they will be ignored */
 
				BuildDepotVehicleList(WP(w, depot_d).type, w->window_number,
 
					&WP(w, depot_d).vehicle_list, &WP(w, depot_d).engine_list_length, &WP(w, depot_d).engine_count,
 
					&WP(w, depot_d).wagon_list,   &WP(w, depot_d).wagon_list_length,  &WP(w, depot_d).wagon_count);
 
				WP(w, depot_d).generate_list = false;
 
				DepotSortList(WP(w, depot_d).vehicle_list, WP(w, depot_d).engine_count);
 
//#ifndef NDEBUG
 
#if 0
 
/* We disabled this check for now, but will keep it to quickly make this test again later (if we change some code) */
 
			} else {
 
				/* Here we got a piece of code, that only checks if we got a different number of vehicles in the depot list and the number of vehicles actually being in the depot.
 
				 * IF they aren't the same, then WE_INVALIDATE_DATA should have been called somewhere, but it wasn't and we got a bug
 
				 * Since this is a time consuming check and not nice to memory fragmentation, it may not stay for long, but it's a good way to check this
 
				 * We can turn it on/off by switching between #ifndef NDEBUG and #if 0 */
 
				Vehicle **engines = NULL, **wagons = NULL;
 
				uint16 engine_count = 0, engine_length = 0;
 
				uint16 wagon_count  = 0, wagon_length  = 0;
 
				BuildDepotVehicleList(WP(w, depot_d).type, w->window_number, &engines, &engine_length, &engine_count,
 
									  &wagons,  &wagon_length,  &wagon_count);
 

	
 
				assert(engine_count == WP(w, depot_d).engine_count);
 
				assert(wagon_count == WP(w, depot_d).wagon_count);
 
				free((void*)engines);
 
				free((void*)wagons);
 
#endif
 
			}
 
			DrawDepotWindow(w);
 
			break;
 

	
 
		case WE_CLICK:
 
			switch (e->we.click.widget) {
 
				case DEPOT_WIDGET_MATRIX: // List
 
					DepotClick(w, e->we.click.pt.x, e->we.click.pt.y);
 
					break;
 

	
 
				case DEPOT_WIDGET_BUILD: // Build vehicle
 
					ResetObjectToPlace();
 
					switch (WP(w, depot_d).type) {
 
						case VEH_Train:    ShowBuildTrainWindow(w->window_number);    break;
 
						case VEH_Road:     ShowBuildRoadVehWindow(w->window_number);  break;
 
						case VEH_Ship:     ShowBuildShipWindow(w->window_number);     break;
 
						case VEH_Aircraft:
 
							ShowBuildVehicleWindow(w->window_number, WP(w, depot_d).type);
 
							break;
 
					default: NOT_REACHED();
 
					}
 
					break;
 

	
 
				case DEPOT_WIDGET_CLONE: // Clone button
 
					InvalidateWidget(w, DEPOT_WIDGET_CLONE);
 
					ToggleWidgetLoweredState(w, DEPOT_WIDGET_CLONE);
 

	
 
					if (IsWindowWidgetLowered(w, DEPOT_WIDGET_CLONE)) {
 
						static const CursorID clone_icons[] = {
 
							SPR_CURSOR_CLONE_TRAIN, SPR_CURSOR_CLONE_ROADVEH,
 
							SPR_CURSOR_CLONE_SHIP, SPR_CURSOR_CLONE_AIRPLANE
 
						};
 

	
 
						_place_clicked_vehicle = NULL;
 
						SetObjectToPlaceWnd(clone_icons[WP(w, depot_d).type - VEH_Train], VHM_RECT, w);
 
					} else {
 
						ResetObjectToPlace();
 
					}
 
						break;
 

	
 
				case DEPOT_WIDGET_LOCATION: ScrollMainWindowToTile(w->window_number); break;
 

	
 
				case DEPOT_WIDGET_STOP_ALL:
 
				case DEPOT_WIDGET_START_ALL:
 
					DoCommandP(w->window_number, 0, WP(w, depot_d).type | (e->we.click.widget == DEPOT_WIDGET_START_ALL ? (1 << 5) : 0), NULL, CMD_MASS_START_STOP);
 
					break;
 

	
 
				case DEPOT_WIDGET_SELL_ALL:
 
					/* Only open the confimation window if there are anything to sell */
 
					if (WP(w, depot_d).engine_count != 0 || WP(w, depot_d).wagon_count != 0) {
 
						static const StringID confirm_captions[] = {
 
							STR_8800_TRAIN_DEPOT,
 
							STR_9003_ROAD_VEHICLE_DEPOT,
 
							STR_9803_SHIP_DEPOT,
 
							STR_A002_AIRCRAFT_HANGAR
 
						};
 
						TileIndex tile = w->window_number;
 
						byte vehtype = WP(w, depot_d).type;
 

	
 
						SetDParam(0, (vehtype == VEH_Aircraft) ? GetStationIndex(tile) : GetDepotByTile(tile)->town_index);
 
						ShowQuery(
 
							confirm_captions[vehtype - VEH_Train],
 
							STR_DEPOT_SELL_CONFIRMATION_TEXT,
 
							w,
 
							DepotSellAllConfirmationCallback
 
						);
 
					}
 
					break;
 

	
 
				case DEPOT_WIDGET_VEHICLE_LIST:
 
					ShowVehDepotOrders(GetTileOwner(w->window_number), WP(w, depot_d).type, w->window_number);
 
					break;
 

	
 
				case DEPOT_WIDGET_AUTOREPLACE:
 
					DoCommandP(w->window_number, WP(w, depot_d).type, 0, NULL, CMD_DEPOT_MASS_AUTOREPLACE);
 
					break;
 

	
 
			}
 
			break;
 

	
 
		case WE_PLACE_OBJ: {
 
			ClonePlaceObj(w);
 
		} break;
 

	
 
		case WE_ABORT_PLACE_OBJ: {
 
			RaiseWindowWidget(w, DEPOT_WIDGET_CLONE);
 
			InvalidateWidget(w, DEPOT_WIDGET_CLONE);
 
		} break;
 

	
 
			/* check if a vehicle in a depot was clicked.. */
 
		case WE_MOUSELOOP: {
 
			const Vehicle *v = _place_clicked_vehicle;
 

	
 
			/* since OTTD checks all open depot windows, we will make sure that it triggers the one with a clicked clone button */
 
			if (v != NULL && IsWindowWidgetLowered(w, DEPOT_WIDGET_CLONE)) {
 
				_place_clicked_vehicle = NULL;
 
				HandleCloneVehClick(v, w);
 
			}
 
		} break;
 

	
 
		case WE_DESTROY:
 
			DeleteWindowById(WC_BUILD_VEHICLE, w->window_number);
 
			free((void*)WP(w, depot_d).vehicle_list);
 
			free((void*)WP(w, depot_d).wagon_list);
 
			break;
 

	
 
		case WE_DRAGDROP:
 
			switch (e->we.click.widget) {
 
				case DEPOT_WIDGET_MATRIX: {
 
					Vehicle *v;
 
					VehicleID sel = WP(w, depot_d).sel;
 

	
 
					WP(w, depot_d).sel = INVALID_VEHICLE;
 
					SetWindowDirty(w);
 

	
 
					if (WP(w, depot_d).type == VEH_Train) {
 
						GetDepotVehiclePtData gdvp;
 

	
 
						if (GetVehicleFromDepotWndPt(w, e->we.dragdrop.pt.x, e->we.dragdrop.pt.y, &v, &gdvp) == MODE_DRAG_VEHICLE &&
 
							sel != INVALID_VEHICLE) {
 
							if (gdvp.wagon != NULL && gdvp.wagon->index == sel && _ctrl_pressed) {
 
								DoCommandP(GetVehicle(sel)->tile, GetVehicle(sel)->index, true, NULL, CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_9033_CAN_T_MAKE_VEHICLE_TURN));
 
							} else if (gdvp.wagon == NULL || gdvp.wagon->index != sel) {
 
								TrainDepotMoveVehicle(gdvp.wagon, sel, gdvp.head);
 
							} else if (gdvp.head != NULL && IsFrontEngine(gdvp.head)) {
 
								ShowTrainViewWindow(gdvp.head);
 
							}
 
						}
 
					} else if (GetVehicleFromDepotWndPt(w, e->we.dragdrop.pt.x, e->we.dragdrop.pt.y, &v, NULL) == MODE_DRAG_VEHICLE &&
 
						v != NULL &&
 
						sel == v->index) {
 
						ShowVehicleViewWindow(v);
 
					}
 
				} break;
 

	
 
				case DEPOT_WIDGET_SELL: case DEPOT_WIDGET_SELL_CHAIN:
 
					if (!IsWindowWidgetDisabled(w, DEPOT_WIDGET_SELL) &&
 
						WP(w, depot_d).sel != INVALID_VEHICLE) {
 
						Vehicle *v;
 
						uint command;
 
						int sell_cmd;
 
						bool is_engine;
 

	
 
						if (IsWindowWidgetDisabled(w, e->we.click.widget)) return;
 
						if (WP(w, depot_d).sel == INVALID_VEHICLE) return;
 

	
 
						HandleButtonClick(w, e->we.click.widget);
 

	
 
						v = GetVehicle(WP(w, depot_d).sel);
 
						WP(w, depot_d).sel = INVALID_VEHICLE;
 
						SetWindowDirty(w);
 

	
 
						sell_cmd = (v->type == VEH_Train && (e->we.click.widget == DEPOT_WIDGET_SELL_CHAIN || _ctrl_pressed)) ? 1 : 0;
 

	
 
						is_engine = (!(v->type == VEH_Train && !IsFrontEngine(v)));
 

	
 
						if (is_engine) {
 
							_backup_orders_tile = v->tile;
 
							BackupVehicleOrders(v, _backup_orders_data);
 
						}
 

	
 
						switch (v->type) {
 
							case VEH_Train:    command = CMD_SELL_RAIL_WAGON | CMD_MSG(STR_8839_CAN_T_SELL_RAILROAD_VEHICLE); break;
 
							case VEH_Road:     command = CMD_SELL_ROAD_VEH | CMD_MSG(STR_9014_CAN_T_SELL_ROAD_VEHICLE);       break;
 
							case VEH_Ship:     command = CMD_SELL_SHIP | CMD_MSG(STR_980C_CAN_T_SELL_SHIP);                   break;
 
							case VEH_Aircraft: command = CMD_SELL_AIRCRAFT | CMD_MSG(STR_A01C_CAN_T_SELL_AIRCRAFT);           break;
 
							default: NOT_REACHED(); command = 0;
 
						}
 

	
 
						if (!DoCommandP(v->tile, v->index, sell_cmd, NULL, command) && is_engine) _backup_orders_tile = 0;
 
					}
 
					break;
 
				default:
 
					WP(w, depot_d).sel = INVALID_VEHICLE;
 
					SetWindowDirty(w);
 
			}
 
			break;
 

	
 
		case WE_RESIZE:
 
			w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
 
			w->hscroll.cap += e->we.sizing.diff.x / (int)w->resize.step_width;
 
			w->widget[DEPOT_WIDGET_MATRIX].data = (w->vscroll.cap << 8) + (WP(w, depot_d).type == VEH_Train ? 1 : w->hscroll.cap);
 
			ResizeDepotButtons(w);
 
			break;
 
	}
 
}
 

	
 
/** Opens a depot window
 
 * @param tile The tile where the depot/hangar is located
 
 * @param type The type of vehicles in the depot
 
 */
 
void ShowDepotWindow(TileIndex tile, byte type)
 
{
 
	Window *w;
 

	
 
	switch (type) {
 
		default: NOT_REACHED();
 
		case VEH_Train:
 
			w = AllocateWindowDescFront(&_train_depot_desc, tile); break;
 
		case VEH_Road:
 
			w = AllocateWindowDescFront(&_road_depot_desc, tile); break;
 
		case VEH_Ship:
 
			w = AllocateWindowDescFront(&_ship_depot_desc, tile); break;
 
		case VEH_Aircraft:
 
			w = AllocateWindowDescFront(&_aircraft_depot_desc, tile); break;
 
	}
 

	
 
	if (w != NULL) {
 
		w->caption_color = GetTileOwner(tile);
 
		CreateDepotListWindow(w, type);
 
	}
 
}
 

	
 
/** Removes the highlight of a vehicle in a depot window
 
 * @param *v Vehicle to remove all highlights from
 
 */
 
void DeleteDepotHighlightOfVehicle(const Vehicle *v)
 
{
 
	Window *w;
 

	
 
	/* If we haven't got any vehicles on the mouse pointer, we haven't got any highlighted in any depots either
 
	 * If that is the case, we can skip looping though the windows and save time                                */
 
	if (_special_mouse_mode != WSM_DRAGDROP) return;
 

	
 
	w = FindWindowById(WC_VEHICLE_DEPOT, v->tile);
 
	if (w != NULL) {
 
		WP(w, depot_d).sel = INVALID_VEHICLE;
 
		ResetObjectToPlace();
 
	}
 
}
src/disaster_cmd.c
Show inline comments
 
deleted file
src/disaster_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "industry_map.h"
 
#include "station_map.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "vehicle.h"
 
#include "command.h"
 
#include "news.h"
 
#include "station.h"
 
#include "waypoint.h"
 
#include "town.h"
 
#include "industry.h"
 
#include "player.h"
 
#include "airport.h"
 
#include "sound.h"
 
#include "variables.h"
 
#include "table/sprites.h"
 
#include "date.h"
 

	
 
static void DisasterClearSquare(TileIndex tile)
 
{
 
	if (!EnsureNoVehicle(tile)) return;
 

	
 
	switch (GetTileType(tile)) {
 
		case MP_RAILWAY:
 
			if (IsHumanPlayer(GetTileOwner(tile)) && !IsRailWaypoint(tile)) {
 
				PlayerID p = _current_player;
 
				_current_player = OWNER_WATER;
 
				DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				_current_player = p;
 
			}
 
			break;
 

	
 
		case MP_HOUSE: {
 
			PlayerID p = _current_player;
 
			_current_player = OWNER_NONE;
 
			DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
			_current_player = p;
 
			break;
 
		}
 

	
 
		case MP_TREES:
 
		case MP_CLEAR:
 
			DoClearSquare(tile);
 
			break;
 

	
 
		default:
 
			break;
 
	}
 
}
 

	
 
static const SpriteID _disaster_images_1[] = {0xF41, 0xF41, 0xF41, 0xF41, 0xF41, 0xF41, 0xF41, 0xF41};
 
static const SpriteID _disaster_images_2[] = {0xF44, 0xF44, 0xF44, 0xF44, 0xF44, 0xF44, 0xF44, 0xF44};
 
static const SpriteID _disaster_images_3[] = {0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E};
 
static const SpriteID _disaster_images_4[] = {0xF46, 0xF46, 0xF47, 0xF47, 0xF48, 0xF48, 0xF49, 0xF49};
 
static const SpriteID _disaster_images_5[] = {0xF4A, 0xF4A, 0xF4B, 0xF4B, 0xF4C, 0xF4C, 0xF4D, 0xF4D};
 
static const SpriteID _disaster_images_6[] = {0xF50, 0xF50, 0xF50, 0xF50, 0xF50, 0xF50, 0xF50, 0xF50};
 
static const SpriteID _disaster_images_7[] = {0xF51, 0xF51, 0xF51, 0xF51, 0xF51, 0xF51, 0xF51, 0xF51};
 
static const SpriteID _disaster_images_8[] = {0xF52, 0xF52, 0xF52, 0xF52, 0xF52, 0xF52, 0xF52, 0xF52};
 
static const SpriteID _disaster_images_9[] = {0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E};
 

	
 
static const SpriteID * const _disaster_images[] = {
 
	_disaster_images_1, _disaster_images_1,
 
	_disaster_images_2, _disaster_images_2,
 
	_disaster_images_3, _disaster_images_3,
 
	_disaster_images_8, _disaster_images_8, _disaster_images_9,
 
	_disaster_images_6, _disaster_images_6,
 
	_disaster_images_7, _disaster_images_7,
 
	_disaster_images_4, _disaster_images_5,
 
};
 

	
 
static void DisasterVehicleUpdateImage(Vehicle *v)
 
{
 
	int img = v->u.disaster.image_override;
 
	if (img == 0)
 
		img = _disaster_images[v->subtype][v->direction];
 
	v->cur_image = img;
 
}
 

	
 
static void InitializeDisasterVehicle(Vehicle* v, int x, int y, byte z, Direction direction, byte subtype)
 
{
 
	v->type = VEH_Disaster;
 
	v->x_pos = x;
 
	v->y_pos = y;
 
	v->z_pos = z;
 
	v->tile = TileVirtXY(x, y);
 
	v->direction = direction;
 
	v->subtype = subtype;
 
	v->x_offs = -1;
 
	v->y_offs = -1;
 
	v->sprite_width = 2;
 
	v->sprite_height = 2;
 
	v->z_height = 5;
 
	v->owner = OWNER_NONE;
 
	v->vehstatus = VS_UNCLICKABLE;
 
	v->u.disaster.image_override = 0;
 
	v->current_order.type = OT_NOTHING;
 
	v->current_order.flags = 0;
 
	v->current_order.dest = 0;
 

	
 
	DisasterVehicleUpdateImage(v);
 
	VehiclePositionChanged(v);
 
	BeginVehicleMove(v);
 
	EndVehicleMove(v);
 
}
 

	
 
static void DeleteDisasterVeh(Vehicle *v)
 
{
 
	DeleteVehicleChain(v);
 
}
 

	
 
static void SetDisasterVehiclePos(Vehicle *v, int x, int y, byte z)
 
{
 
	Vehicle *u;
 

	
 
	BeginVehicleMove(v);
 
	v->x_pos = x;
 
	v->y_pos = y;
 
	v->z_pos = z;
 
	v->tile = TileVirtXY(x, y);
 

	
 
	DisasterVehicleUpdateImage(v);
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 

	
 
	if ( (u=v->next) != NULL) {
 
		int safe_x = clamp(x, 0, MapMaxX() * TILE_SIZE);
 
		int safe_y = clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
 
		BeginVehicleMove(u);
 

	
 
		u->x_pos = x;
 
		u->y_pos = y - 1 - (max(z - GetSlopeZ(safe_x, safe_y), 0) >> 3);
 
		safe_y = clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
 
		u->z_pos = GetSlopeZ(safe_x, safe_y);
 
		u->direction = v->direction;
 

	
 
		DisasterVehicleUpdateImage(u);
 
		VehiclePositionChanged(u);
 
		EndVehicleMove(u);
 

	
 
		if ( (u=u->next) != NULL) {
 
			BeginVehicleMove(u);
 
			u->x_pos = x;
 
			u->y_pos = y;
 
			u->z_pos = z + 5;
 
			VehiclePositionChanged(u);
 
			EndVehicleMove(u);
 
		}
 
	}
 
}
 

	
 

	
 
static void DisasterTick_Zeppeliner(Vehicle *v)
 
{
 
	GetNewVehiclePosResult gp;
 
	Station *st;
 
	int x,y;
 
	byte z;
 
	TileIndex tile;
 

	
 
	++v->tick_counter;
 

	
 
	if (v->current_order.dest < 2) {
 
		if (v->tick_counter&1)
 
			return;
 

	
 
		GetNewVehiclePos(v, &gp);
 

	
 
		SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 

	
 
		if (v->current_order.dest == 1) {
 
			if (++v->age == 38) {
 
				v->current_order.dest = 2;
 
				v->age = 0;
 
			}
 

	
 
			if ((v->tick_counter&7)==0) {
 
				CreateEffectVehicleRel(v, 0, -17, 2, EV_SMOKE);
 
			}
 
		} else if (v->current_order.dest == 0) {
 
			tile = v->tile; /**/
 

	
 
			if (IsValidTile(tile) &&
 
					IsTileType(tile, MP_STATION) &&
 
					IsAirport(tile) &&
 
					IsHumanPlayer(GetTileOwner(tile))) {
 
				v->current_order.dest = 1;
 
				v->age = 0;
 

	
 
				SetDParam(0, GetStationIndex(tile));
 
				AddNewsItem(STR_B000_ZEPPELIN_DISASTER_AT,
 
					NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
 
					v->index,
 
					0);
 
			}
 
		}
 
		if (v->y_pos >= ((int)MapSizeY() + 9) * TILE_SIZE - 1)
 
			DeleteDisasterVeh(v);
 
		return;
 
	}
 

	
 
	if (v->current_order.dest > 2) {
 
		if (++v->age <= 13320)
 
			return;
 

	
 
		tile = v->tile; /**/
 

	
 
		if (IsValidTile(tile) &&
 
				IsTileType(tile, MP_STATION) &&
 
				IsAirport(tile) &&
 
				IsHumanPlayer(GetTileOwner(tile))) {
 
			st = GetStationByTile(tile);
 
			CLRBITS(st->airport_flags, RUNWAY_IN_block);
 
		}
 

	
 
		SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos);
 
		DeleteDisasterVeh(v);
 
		return;
 
	}
 

	
 
	x = v->x_pos;
 
	y = v->y_pos;
 
	z = GetSlopeZ(x,y);
 
	if (z < v->z_pos)
 
		z = v->z_pos - 1;
 
	SetDisasterVehiclePos(v, x, y, z);
 

	
 
	if (++v->age == 1) {
 
		CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
 
		SndPlayVehicleFx(SND_12_EXPLOSION, v);
 
		v->u.disaster.image_override = SPR_BLIMP_CRASHING;
 
	} else if (v->age == 70) {
 
		v->u.disaster.image_override = SPR_BLIMP_CRASHED;
 
	} else if (v->age <= 300) {
 
		if (!(v->tick_counter&7)) {
 
			uint32 r = Random();
 

	
 
			CreateEffectVehicleRel(v,
 
				GB(r, 0, 4) - 7,
 
				GB(r, 4, 4) - 7,
 
				GB(r, 8, 3) + 5,
 
				EV_EXPLOSION_SMALL);
 
		}
 
	} else if (v->age == 350) {
 
		v->current_order.dest = 3;
 
		v->age = 0;
 
	}
 

	
 
	tile = v->tile;/**/
 
	if (IsValidTile(tile) &&
 
			IsTileType(tile, MP_STATION) &&
 
			IsAirport(tile) &&
 
			IsHumanPlayer(GetTileOwner(tile))) {
 
		st = GetStationByTile(tile);
 
		SETBITS(st->airport_flags, RUNWAY_IN_block);
 
	}
 
}
 

	
 
// UFO starts in the middle, and flies around a bit until it locates
 
// a road vehicle which it targets.
 
static void DisasterTick_UFO(Vehicle *v)
 
{
 
	GetNewVehiclePosResult gp;
 
	Vehicle *u;
 
	uint dist;
 
	byte z;
 

	
 
	v->u.disaster.image_override = (++v->tick_counter & 8) ? SPR_UFO_SMALL_SCOUT_DARKER : SPR_UFO_SMALL_SCOUT;
 

	
 
	if (v->current_order.dest == 0) {
 
// fly around randomly
 
		int x = TileX(v->dest_tile) * TILE_SIZE;
 
		int y = TileY(v->dest_tile) * TILE_SIZE;
 
		if (abs(x - v->x_pos) + abs(y - v->y_pos) >= TILE_SIZE) {
 
			v->direction = GetDirectionTowards(v, x, y);
 
			GetNewVehiclePos(v, &gp);
 
			SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 
			return;
 
		}
 
		if (++v->age < 6) {
 
			v->dest_tile = RandomTile();
 
			return;
 
		}
 
		v->current_order.dest = 1;
 

	
 
		FOR_ALL_VEHICLES(u) {
 
			if (u->type == VEH_Road && IsHumanPlayer(u->owner)) {
 
				v->dest_tile = u->index;
 
				v->age = 0;
 
				return;
 
			}
 
		}
 

	
 
		DeleteDisasterVeh(v);
 
	} else {
 
// target a vehicle
 
		u = GetVehicle(v->dest_tile);
 
		if (u->type != VEH_Road) {
 
			DeleteDisasterVeh(v);
 
			return;
 
		}
 

	
 
		dist = abs(v->x_pos - u->x_pos) + abs(v->y_pos - u->y_pos);
 

	
 
		if (dist < TILE_SIZE && !(u->vehstatus&VS_HIDDEN) && u->breakdown_ctr==0) {
 
			u->breakdown_ctr = 3;
 
			u->breakdown_delay = 140;
 
		}
 

	
 
		v->direction = GetDirectionTowards(v, u->x_pos, u->y_pos);
 
		GetNewVehiclePos(v, &gp);
 

	
 
		z = v->z_pos;
 
		if (dist <= TILE_SIZE && z > u->z_pos) z--;
 
		SetDisasterVehiclePos(v, gp.x, gp.y, z);
 

	
 
		if (z <= u->z_pos && (u->vehstatus&VS_HIDDEN)==0) {
 
			v->age++;
 
			if (u->u.road.crashed_ctr == 0) {
 
				u->u.road.crashed_ctr++;
 
				u->vehstatus |= VS_CRASHED;
 

	
 
				AddNewsItem(STR_B001_ROAD_VEHICLE_DESTROYED,
 
					NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
 
					u->index,
 
					0);
 
			}
 
		}
 

	
 
// destroy?
 
		if (v->age > 50) {
 
			CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
 
			SndPlayVehicleFx(SND_12_EXPLOSION, v);
 
			DeleteDisasterVeh(v);
 
		}
 
	}
 
}
 

	
 
static void DestructIndustry(Industry *i)
 
{
 
	TileIndex tile;
 

	
 
	for (tile = 0; tile != MapSize(); tile++) {
 
		if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == i->index) {
 
			ResetIndustryConstructionStage(tile);
 
			MarkTileDirtyByTile(tile);
 
		}
 
	}
 
}
 

	
 
// Airplane which destroys an oil refinery
 
static void DisasterTick_2(Vehicle *v)
 
{
 
	GetNewVehiclePosResult gp;
 

	
 
	v->tick_counter++;
 
	v->u.disaster.image_override =
 
		(v->current_order.dest == 1 && v->tick_counter & 4) ? SPR_F_15_FIRING : 0;
 

	
 
	GetNewVehiclePos(v, &gp);
 
	SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 

	
 
	if (gp.x < -160) {
 
		DeleteDisasterVeh(v);
 
		return;
 
	}
 

	
 
	if (v->current_order.dest == 2) {
 
		if (!(v->tick_counter&3)) {
 
			Industry *i = GetIndustry(v->dest_tile);
 
			int x = TileX(i->xy) * TILE_SIZE;
 
			int y = TileY(i->xy) * TILE_SIZE;
 
			uint32 r = Random();
 

	
 
			CreateEffectVehicleAbove(
 
				GB(r,  0, 6) + x,
 
				GB(r,  6, 6) + y,
 
				GB(r, 12, 4),
 
				EV_EXPLOSION_SMALL);
 

	
 
			if (++v->age >= 55)
 
				v->current_order.dest = 3;
 
		}
 
	} else if (v->current_order.dest == 1) {
 
		if (++v->age == 112) {
 
			Industry *i;
 

	
 
			v->current_order.dest = 2;
 
			v->age = 0;
 

	
 
			i = GetIndustry(v->dest_tile);
 
			DestructIndustry(i);
 

	
 
			SetDParam(0, i->town->index);
 
			AddNewsItem(STR_B002_OIL_REFINERY_EXPLOSION, NEWS_FLAGS(NM_THIN,NF_VIEWPORT|NF_TILE,NT_ACCIDENT,0), i->xy, 0);
 
			SndPlayTileFx(SND_12_EXPLOSION, i->xy);
 
		}
 
	} else if (v->current_order.dest == 0) {
 
		int x,y;
 
		TileIndex tile;
 
		uint ind;
 

	
 
		x = v->x_pos - 15 * TILE_SIZE;
 
		y = v->y_pos;
 

	
 
		if ( (uint)x > MapMaxX() * TILE_SIZE - 1)
 
			return;
 

	
 
		tile = TileVirtXY(x, y);
 
		if (!IsTileType(tile, MP_INDUSTRY))
 
			return;
 

	
 
		ind = GetIndustryIndex(tile);
 
		v->dest_tile = ind;
 

	
 
		if (GetIndustry(ind)->type == IT_OIL_REFINERY) {
 
			v->current_order.dest = 1;
 
			v->age = 0;
 
		}
 
	}
 
}
 

	
 
// Helicopter which destroys a factory
 
static void DisasterTick_3(Vehicle *v)
 
{
 
	GetNewVehiclePosResult gp;
 

	
 
	v->tick_counter++;
 
	v->u.disaster.image_override =
 
		(v->current_order.dest == 1 && v->tick_counter & 4) ? SPR_AH_64A_FIRING : 0;
 

	
 
	GetNewVehiclePos(v, &gp);
 
	SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 

	
 
	if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
 
		DeleteDisasterVeh(v);
 
		return;
 
	}
 

	
 
	if (v->current_order.dest == 2) {
 
		if (!(v->tick_counter&3)) {
 
			Industry *i = GetIndustry(v->dest_tile);
 
			int x = TileX(i->xy) * TILE_SIZE;
 
			int y = TileY(i->xy) * TILE_SIZE;
 
			uint32 r = Random();
 

	
 
			CreateEffectVehicleAbove(
 
				GB(r,  0, 6) + x,
 
				GB(r,  6, 6) + y,
 
				GB(r, 12, 4),
 
				EV_EXPLOSION_SMALL);
 

	
 
			if (++v->age >= 55)
 
				v->current_order.dest = 3;
 
		}
 
	} else if (v->current_order.dest == 1) {
 
		if (++v->age == 112) {
 
			Industry *i;
 

	
 
			v->current_order.dest = 2;
 
			v->age = 0;
 

	
 
			i = GetIndustry(v->dest_tile);
 
			DestructIndustry(i);
 

	
 
			SetDParam(0, i->town->index);
 
			AddNewsItem(STR_B003_FACTORY_DESTROYED_IN_SUSPICIOUS, NEWS_FLAGS(NM_THIN,NF_VIEWPORT|NF_TILE,NT_ACCIDENT,0), i->xy, 0);
 
			SndPlayTileFx(SND_12_EXPLOSION, i->xy);
 
		}
 
	} else if (v->current_order.dest == 0) {
 
		int x,y;
 
		TileIndex tile;
 
		uint ind;
 

	
 
		x = v->x_pos - 15 * TILE_SIZE;
 
		y = v->y_pos;
 

	
 
		if ( (uint)x > MapMaxX() * TILE_SIZE - 1)
 
			return;
 

	
 
		tile = TileVirtXY(x, y);
 
		if (!IsTileType(tile, MP_INDUSTRY))
 
			return;
 

	
 
		ind = GetIndustryIndex(tile);
 
		v->dest_tile = ind;
 

	
 
		if (GetIndustry(ind)->type == IT_FACTORY) {
 
			v->current_order.dest = 1;
 
			v->age = 0;
 
		}
 
	}
 
}
 

	
 
// Helicopter rotor blades
 
static void DisasterTick_3b(Vehicle *v)
 
{
 
	if (++v->tick_counter & 1)
 
		return;
 

	
 
	if (++v->cur_image > SPR_ROTOR_MOVING_3) v->cur_image = SPR_ROTOR_MOVING_1;
 

	
 
	VehiclePositionChanged(v);
 
	BeginVehicleMove(v);
 
	EndVehicleMove(v);
 
}
 

	
 
// Big UFO which lands on a piece of rail.
 
// Will be shot down by a plane
 
static void DisasterTick_4(Vehicle *v)
 
{
 
	GetNewVehiclePosResult gp;
 
	byte z;
 
	Vehicle *u,*w;
 
	Town *t;
 
	TileIndex tile;
 
	TileIndex tile_org;
 

	
 
	v->tick_counter++;
 

	
 
	if (v->current_order.dest == 1) {
 
		int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
 
		int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
 
		if (abs(v->x_pos - x) + abs(v->y_pos - y) >= 8) {
 
			v->direction = GetDirectionTowards(v, x, y);
 

	
 
			GetNewVehiclePos(v, &gp);
 
			SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 
			return;
 
		}
 

	
 
		z = GetSlopeZ(v->x_pos, v->y_pos);
 
		if (z < v->z_pos) {
 
			SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos - 1);
 
			return;
 
		}
 

	
 
		v->current_order.dest = 2;
 

	
 
		FOR_ALL_VEHICLES(u) {
 
			if (u->type == VEH_Train || u->type == VEH_Road) {
 
				if (abs(u->x_pos - v->x_pos) + abs(u->y_pos - v->y_pos) <= 12 * TILE_SIZE) {
 
					u->breakdown_ctr = 5;
 
					u->breakdown_delay = 0xF0;
 
				}
 
			}
 
		}
 

	
 
		t = ClosestTownFromTile(v->dest_tile, (uint)-1);
 
		SetDParam(0, t->index);
 
		AddNewsItem(STR_B004_UFO_LANDS_NEAR,
 
			NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_TILE, NT_ACCIDENT, 0),
 
			v->tile,
 
			0);
 

	
 
		u = ForceAllocateSpecialVehicle();
 
		if (u == NULL) {
 
			DeleteDisasterVeh(v);
 
			return;
 
		}
 

	
 
		InitializeDisasterVehicle(u, -6 * TILE_SIZE, v->y_pos, 135, DIR_SW, 11);
 
		u->u.disaster.unk2 = v->index;
 

	
 
		w = ForceAllocateSpecialVehicle();
 
		if (w == NULL)
 
			return;
 

	
 
		u->next = w;
 
		InitializeDisasterVehicle(w, -6 * TILE_SIZE, v->y_pos, 0, DIR_SW, 12);
 
		w->vehstatus |= VS_SHADOW;
 
	} else if (v->current_order.dest < 1) {
 

	
 
		int x = TileX(v->dest_tile) * TILE_SIZE;
 
		int y = TileY(v->dest_tile) * TILE_SIZE;
 
		if (abs(x - v->x_pos) + abs(y - v->y_pos) >= TILE_SIZE) {
 
			v->direction = GetDirectionTowards(v, x, y);
 
			GetNewVehiclePos(v, &gp);
 
			SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 
			return;
 
		}
 

	
 
		if (++v->age < 6) {
 
			v->dest_tile = RandomTile();
 
			return;
 
		}
 
		v->current_order.dest = 1;
 

	
 
		tile_org = tile = RandomTile();
 
		do {
 
			if (IsTileType(tile, MP_RAILWAY) &&
 
					IsPlainRailTile(tile) &&
 
					IsHumanPlayer(GetTileOwner(tile))) {
 
				break;
 
			}
 
			tile = TILE_MASK(tile+1);
 
		} while (tile != tile_org);
 
		v->dest_tile = tile;
 
		v->age = 0;
 
	} else {
 
		return;
 
	}
 
}
 

	
 
// The plane which will shoot down the UFO
 
static void DisasterTick_4b(Vehicle *v)
 
{
 
	GetNewVehiclePosResult gp;
 
	Vehicle *u;
 
	int i;
 

	
 
	v->tick_counter++;
 

	
 
	GetNewVehiclePos(v, &gp);
 
	SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 

	
 
	if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
 
		DeleteDisasterVeh(v);
 
		return;
 
	}
 

	
 
	if (v->current_order.dest == 0) {
 
		u = GetVehicle(v->u.disaster.unk2);
 
		if (abs(v->x_pos - u->x_pos) > TILE_SIZE)
 
			return;
 
		v->current_order.dest = 1;
 

	
 
		CreateEffectVehicleRel(u, 0, 7, 8, EV_EXPLOSION_LARGE);
 
		SndPlayVehicleFx(SND_12_EXPLOSION, u);
 

	
 
		DeleteDisasterVeh(u);
 

	
 
		for (i = 0; i != 80; i++) {
 
			uint32 r = Random();
 
			CreateEffectVehicleAbove(
 
				GB(r, 0, 6) + v->x_pos - 32,
 
				GB(r, 5, 6) + v->y_pos - 32,
 
				0,
 
				EV_EXPLOSION_SMALL);
 
		}
 

	
 
		BEGIN_TILE_LOOP(tile, 6, 6, v->tile - TileDiffXY(3, 3))
 
			tile = TILE_MASK(tile);
 
			DisasterClearSquare(tile);
 
		END_TILE_LOOP(tile, 6, 6, v->tile - TileDiffXY(3, 3))
 
	}
 
}
 

	
 
// Submarine handler
 
static void DisasterTick_5_and_6(Vehicle *v)
 
{
 
	uint32 r;
 
	GetNewVehiclePosResult gp;
 
	TileIndex tile;
 

	
 
	v->tick_counter++;
 

	
 
	if (++v->age > 8880) {
 
		VehiclePositionChanged(v);
 
		BeginVehicleMove(v);
 
		EndVehicleMove(v);
 
		DeleteVehicle(v);
 
		return;
 
	}
 

	
 
	if (!(v->tick_counter & 1)) return;
 

	
 
	tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
 
	if (IsValidTile(tile) &&
 
			(r=GetTileTrackStatus(tile,TRANSPORT_WATER),(byte)(r+(r >> 8)) == 0x3F) &&
 
			!CHANCE16(1,90)) {
 
		GetNewVehiclePos(v, &gp);
 
		SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 
		return;
 
	}
 

	
 
	v->direction = ChangeDir(v->direction, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT : DIRDIFF_90LEFT);
 
}
 

	
 

	
 
static void DisasterTick_NULL(Vehicle *v) {}
 
typedef void DisasterVehicleTickProc(Vehicle *v);
 

	
 
static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
 
	DisasterTick_Zeppeliner,DisasterTick_NULL,
 
	DisasterTick_UFO,DisasterTick_NULL,
 
	DisasterTick_2,DisasterTick_NULL,
 
	DisasterTick_3,DisasterTick_NULL,DisasterTick_3b,
 
	DisasterTick_4,DisasterTick_NULL,
 
	DisasterTick_4b,DisasterTick_NULL,
 
	DisasterTick_5_and_6,
 
	DisasterTick_5_and_6,
 
};
 

	
 

	
 
void DisasterVehicle_Tick(Vehicle *v)
 
{
 
	_disastervehicle_tick_procs[v->subtype](v);
 
}
 

	
 

	
 
void OnNewDay_DisasterVehicle(Vehicle *v)
 
{
 
	// not used
 
}
 

	
 
typedef void DisasterInitProc(void);
 

	
 
// Zeppeliner which crashes on a small airport
 
static void Disaster0_Init(void)
 
{
 
	Vehicle *v = ForceAllocateSpecialVehicle(), *u;
 
	Station *st;
 
	int x;
 

	
 
	if (v == NULL) return;
 

	
 
	/* Pick a random place, unless we find a small airport */
 
	x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		if (st->airport_tile != 0 &&
 
				st->airport_type <= 1 &&
 
				IsHumanPlayer(st->owner)) {
 
			x = (TileX(st->xy) + 2) * TILE_SIZE;
 
			break;
 
		}
 
	}
 

	
 
	InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, 0);
 

	
 
	// Allocate shadow too?
 
	u = ForceAllocateSpecialVehicle();
 
	if (u != NULL) {
 
		v->next = u;
 
		InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, 1);
 
		u->vehstatus |= VS_SHADOW;
 
	}
 
}
 

	
 
static void Disaster1_Init(void)
 
{
 
	Vehicle *v = ForceAllocateSpecialVehicle(), *u;
 
	int x;
 

	
 
	if (v == NULL) return;
 

	
 
	x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
 

	
 
	InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, 2);
 
	v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
 
	v->age = 0;
 

	
 
	// Allocate shadow too?
 
	u = ForceAllocateSpecialVehicle();
 
	if (u != NULL) {
 
		v->next = u;
 
		InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, 3);
 
		u->vehstatus |= VS_SHADOW;
 
	}
 
}
 

	
 
static void Disaster2_Init(void)
 
{
 
	Industry *i, *found;
 
	Vehicle *v,*u;
 
	int x,y;
 

	
 
	found = NULL;
 

	
 
	FOR_ALL_INDUSTRIES(i) {
 
		if (i->type == IT_OIL_REFINERY &&
 
				(found == NULL || CHANCE16(1, 2))) {
 
			found = i;
 
		}
 
	}
 

	
 
	if (found == NULL) return;
 

	
 
	v = ForceAllocateSpecialVehicle();
 
	if (v == NULL) return;
 

	
 
	x = (MapSizeX() + 9) * TILE_SIZE - 1;
 
	y = TileY(found->xy) * TILE_SIZE + 37;
 

	
 
	InitializeDisasterVehicle(v, x, y, 135, DIR_NE, 4);
 

	
 
	u = ForceAllocateSpecialVehicle();
 
	if (u != NULL) {
 
		v->next = u;
 
		InitializeDisasterVehicle(u, x, y, 0, DIR_SE, 5);
 
		u->vehstatus |= VS_SHADOW;
 
	}
 
}
 

	
 
static void Disaster3_Init(void)
 
{
 
	Industry *i, *found;
 
	Vehicle *v,*u,*w;
 
	int x,y;
 

	
 
	found = NULL;
 

	
 
	FOR_ALL_INDUSTRIES(i) {
 
		if (i->type == IT_FACTORY &&
 
				(found==NULL || CHANCE16(1,2))) {
 
			found = i;
 
		}
 
	}
 

	
 
	if (found == NULL) return;
 

	
 
	v = ForceAllocateSpecialVehicle();
 
	if (v == NULL) return;
 

	
 
	x = -16 * TILE_SIZE;
 
	y = TileY(found->xy) * TILE_SIZE + 37;
 

	
 
	InitializeDisasterVehicle(v, x, y, 135, DIR_SW, 6);
 

	
 
	u = ForceAllocateSpecialVehicle();
 
	if (u != NULL) {
 
		v->next = u;
 
		InitializeDisasterVehicle(u, x, y, 0, DIR_SW, 7);
 
		u->vehstatus |= VS_SHADOW;
 

	
 
		w = ForceAllocateSpecialVehicle();
 
		if (w != NULL) {
 
			u->next = w;
 
			InitializeDisasterVehicle(w, x, y, 140, DIR_SW, 8);
 
		}
 
	}
 
}
 

	
 
static void Disaster4_Init(void)
 
{
 
	Vehicle *v = ForceAllocateSpecialVehicle(), *u;
 
	int x,y;
 

	
 
	if (v == NULL) return;
 

	
 
	x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
 

	
 
	y = MapMaxX() * TILE_SIZE - 1;
 
	InitializeDisasterVehicle(v, x, y, 135, DIR_NW, 9);
 
	v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
 
	v->age = 0;
 

	
 
	// Allocate shadow too?
 
	u = ForceAllocateSpecialVehicle();
 
	if (u != NULL) {
 
		v->next = u;
 
		InitializeDisasterVehicle(u, x, y, 0, DIR_NW, 10);
 
		u->vehstatus |= VS_SHADOW;
 
	}
 
}
 

	
 
// Submarine type 1
 
static void Disaster5_Init(void)
 
{
 
	Vehicle *v = ForceAllocateSpecialVehicle();
 
	int x,y;
 
	Direction dir;
 
	uint32 r;
 

	
 
	if (v == NULL) return;
 

	
 
	r = Random();
 
	x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
 

	
 
	if (r & 0x80000000) {
 
		y = MapMaxX() * TILE_SIZE - TILE_SIZE / 2 - 1;
 
		dir = DIR_NW;
 
	} else {
 
		y = TILE_SIZE / 2;
 
		dir = DIR_SE;
 
	}
 
	InitializeDisasterVehicle(v, x, y, 0, dir, 13);
 
	v->age = 0;
 
}
 

	
 
// Submarine type 2
 
static void Disaster6_Init(void)
 
{
 
	Vehicle *v = ForceAllocateSpecialVehicle();
 
	int x,y;
 
	Direction dir;
 
	uint32 r;
 

	
 
	if (v == NULL) return;
 

	
 
	r = Random();
 
	x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
 

	
 
	if (r & 0x80000000) {
 
		y = MapMaxX() * TILE_SIZE - TILE_SIZE / 2 - 1;
 
		dir = DIR_NW;
 
	} else {
 
		y = TILE_SIZE / 2;
 
		dir = DIR_SE;
 
	}
 
	InitializeDisasterVehicle(v, x, y, 0, dir, 14);
 
	v->age = 0;
 
}
 

	
 
static void Disaster7_Init(void)
 
{
 
	int index = GB(Random(), 0, 4);
 
	uint m;
 

	
 
	for (m = 0; m < 15; m++) {
 
		const Industry* i;
 

	
 
		FOR_ALL_INDUSTRIES(i) {
 
			if (i->type == IT_COAL_MINE && --index < 0) {
 
				SetDParam(0, i->town->index);
 
				AddNewsItem(STR_B005_COAL_MINE_SUBSIDENCE_LEAVES,
 
					NEWS_FLAGS(NM_THIN,NF_VIEWPORT|NF_TILE,NT_ACCIDENT,0), i->xy + TileDiffXY(1, 1), 0);
 

	
 
				{
 
					TileIndex tile = i->xy;
 
					TileIndexDiff step = TileOffsByDiagDir(GB(Random(), 0, 2));
 
					uint n;
 

	
 
					for (n = 0; n < 30; n++) {
 
						DisasterClearSquare(tile);
 
						tile = TILE_MASK(tile + step);
 
					}
 
				}
 
				return;
 
			}
 
		}
 
	}
 
}
 

	
 
static DisasterInitProc * const _disaster_initprocs[] = {
 
	Disaster0_Init,
 
	Disaster1_Init,
 
	Disaster2_Init,
 
	Disaster3_Init,
 
	Disaster4_Init,
 
	Disaster5_Init,
 
	Disaster6_Init,
 
	Disaster7_Init,
 
};
 

	
 
static const struct {
 
	Year min;
 
	Year max;
 
} _dis_years[] = {
 
	{ 1930, 1955 },
 
	{ 1940, 1970 },
 
	{ 1960, 1990 },
 
	{ 1970, 2000 },
 
	{ 2000, 2100 },
 
	{ 1940, 1965 },
 
	{ 1975, 2010 },
 
	{ 1950, 1985 }
 
};
 

	
 

	
 
static void DoDisaster(void)
 
{
 
	byte buf[lengthof(_dis_years)];
 
	uint i;
 
	uint j;
 

	
 
	j = 0;
 
	for (i = 0; i != lengthof(_dis_years); i++) {
 
		if (_cur_year >= _dis_years[i].min && _cur_year < _dis_years[i].max) buf[j++] = i;
 
	}
 

	
 
	if (j == 0) return;
 

	
 
	_disaster_initprocs[buf[RandomRange(j)]]();
 
}
 

	
 

	
 
static void ResetDisasterDelay(void)
 
{
 
	_disaster_delay = GB(Random(), 0, 9) + 730;
 
}
 

	
 
void DisasterDailyLoop(void)
 
{
 
	if (--_disaster_delay != 0) return;
 

	
 
	ResetDisasterDelay();
 

	
 
	if (_opt.diff.disasters != 0) DoDisaster();
 
}
 

	
 
void StartupDisasters(void)
 
{
 
	ResetDisasterDelay();
 
}
src/dock_gui.c
Show inline comments
 
deleted file
src/dock_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "window.h"
 
#include "station.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "sound.h"
 
#include "command.h"
 
#include "variables.h"
 

	
 
static void ShowBuildDockStationPicker(void);
 
static void ShowBuildDocksDepotPicker(void);
 

	
 
static Axis _ship_depot_direction;
 

	
 
void CcBuildDocks(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_02_SPLAT, tile);
 
		ResetObjectToPlace();
 
	}
 
}
 

	
 
void CcBuildCanal(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) SndPlayTileFx(SND_02_SPLAT, tile);
 
}
 

	
 

	
 
static void PlaceDocks_Dock(TileIndex tile)
 
{
 
	DoCommandP(tile, 0, 0, CcBuildDocks, CMD_BUILD_DOCK | CMD_AUTO | CMD_MSG(STR_9802_CAN_T_BUILD_DOCK_HERE));
 
}
 

	
 
static void PlaceDocks_Depot(TileIndex tile)
 
{
 
	DoCommandP(tile, _ship_depot_direction, 0, CcBuildDocks, CMD_BUILD_SHIP_DEPOT | CMD_AUTO | CMD_MSG(STR_3802_CAN_T_BUILD_SHIP_DEPOT));
 
}
 

	
 
static void PlaceDocks_Buoy(TileIndex tile)
 
{
 
	DoCommandP(tile, 0, 0, CcBuildDocks, CMD_BUILD_BUOY | CMD_AUTO | CMD_MSG(STR_9835_CAN_T_POSITION_BUOY_HERE));
 
}
 

	
 
static void PlaceDocks_DemolishArea(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_X_AND_Y | GUI_PlaceProc_DemolishArea);
 
}
 

	
 
static void PlaceDocks_BuildCanal(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_X_OR_Y);
 
}
 

	
 
static void PlaceDocks_BuildLock(TileIndex tile)
 
{
 
	DoCommandP(tile, 0, 0, CcBuildDocks, CMD_BUILD_LOCK | CMD_AUTO | CMD_MSG(STR_CANT_BUILD_LOCKS));
 
}
 

	
 

	
 
enum {
 
	DTW_CANAL    = 3,
 
	DTW_LOCK     = 4,
 
	DTW_DEMOLISH = 6,
 
	DTW_DEPOT    = 7,
 
	DTW_STATION  = 8,
 
	DTW_BUOY     = 9
 
};
 

	
 

	
 
static void BuildDocksClick_Canal(Window *w)
 
{
 
	HandlePlacePushButton(w, DTW_CANAL, SPR_CURSOR_CANAL, 1, PlaceDocks_BuildCanal);
 
}
 

	
 
static void BuildDocksClick_Lock(Window *w)
 
{
 
	HandlePlacePushButton(w, DTW_LOCK, SPR_CURSOR_LOCK, 1, PlaceDocks_BuildLock);
 
}
 

	
 
static void BuildDocksClick_Demolish(Window *w)
 
{
 
	HandlePlacePushButton(w, DTW_DEMOLISH, ANIMCURSOR_DEMOLISH, 1, PlaceDocks_DemolishArea);
 
}
 

	
 
static void BuildDocksClick_Depot(Window *w)
 
{
 
	if (HandlePlacePushButton(w, DTW_DEPOT, SPR_CURSOR_SHIP_DEPOT, 1, PlaceDocks_Depot)) ShowBuildDocksDepotPicker();
 
}
 

	
 
static void BuildDocksClick_Dock(Window *w)
 
{
 
	if (HandlePlacePushButton(w, DTW_STATION, SPR_CURSOR_DOCK, 3, PlaceDocks_Dock)) ShowBuildDockStationPicker();
 
}
 

	
 
static void BuildDocksClick_Buoy(Window *w)
 
{
 
	HandlePlacePushButton(w, DTW_BUOY, SPR_CURSOR_BOUY, 1, PlaceDocks_Buoy);
 
}
 

	
 
static void BuildDocksClick_Landscaping(Window *w)
 
{
 
	ShowTerraformToolbar();
 
}
 

	
 
typedef void OnButtonClick(Window *w);
 
static OnButtonClick * const _build_docks_button_proc[] = {
 
	BuildDocksClick_Canal,
 
	BuildDocksClick_Lock,
 
	NULL,
 
	BuildDocksClick_Demolish,
 
	BuildDocksClick_Depot,
 
	BuildDocksClick_Dock,
 
	BuildDocksClick_Buoy,
 
	BuildDocksClick_Landscaping,
 
};
 

	
 
static void BuildDocksToolbWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		if (e->we.click.widget - 3 >= 0 && e->we.click.widget != 5) _build_docks_button_proc[e->we.click.widget - 3](w);
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		switch (e->we.keypress.keycode) {
 
			case '1': BuildDocksClick_Canal(w); break;
 
			case '2': BuildDocksClick_Lock(w); break;
 
			case '3': BuildDocksClick_Demolish(w); break;
 
			case '4': BuildDocksClick_Depot(w); break;
 
			case '5': BuildDocksClick_Dock(w); break;
 
			case '6': BuildDocksClick_Buoy(w); break;
 
			case 'l': BuildDocksClick_Landscaping(w); break;
 
			default:  return;
 
		}
 
		break;
 

	
 
	case WE_PLACE_OBJ:
 
		_place_proc(e->we.place.tile);
 
		break;
 

	
 
	case WE_PLACE_DRAG: {
 
		VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata);
 
		return;
 
	}
 

	
 
	case WE_PLACE_MOUSEUP:
 
		if (e->we.place.pt.x != -1) {
 
			if ((e->we.place.userdata & 0xF) == VPM_X_AND_Y) { // dragged actions
 
				GUIPlaceProcDragXY(e);
 
			} else if (e->we.place.userdata == VPM_X_OR_Y) {
 
				DoCommandP(e->we.place.tile, e->we.place.starttile, _ctrl_pressed, CcBuildCanal, CMD_BUILD_CANAL | CMD_AUTO | CMD_MSG(STR_CANT_BUILD_CANALS));
 
			}
 
		}
 
		break;
 

	
 
	case WE_ABORT_PLACE_OBJ:
 
		RaiseWindowButtons(w);
 

	
 
		w = FindWindowById(WC_BUILD_STATION, 0);
 
		if (w != NULL) WP(w,def_d).close = true;
 

	
 
		w = FindWindowById(WC_BUILD_DEPOT, 0);
 
		if (w != NULL) WP(w,def_d).close = true;
 
		break;
 

	
 
	case WE_PLACE_PRESIZE: {
 
		TileIndex tile_from;
 
		TileIndex tile_to;
 

	
 
		tile_from = tile_to = e->we.place.tile;
 
		switch (GetTileSlope(tile_from, NULL)) {
 
			case SLOPE_SW: tile_to += TileDiffXY(-1,  0); break;
 
			case SLOPE_SE: tile_to += TileDiffXY( 0, -1); break;
 
			case SLOPE_NW: tile_to += TileDiffXY( 0,  1); break;
 
			case SLOPE_NE: tile_to += TileDiffXY( 1,  0); break;
 
			default: break;
 
		}
 
		VpSetPresizeRange(tile_from, tile_to);
 
	} break;
 

	
 
	case WE_DESTROY:
 
		if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0);
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_docks_toolb_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                   STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   145,     0,    13, STR_9801_DOCK_CONSTRUCTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,     7,   146,   157,     0,    13, 0x0,                        STR_STICKY_BUTTON},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,     0,    21,    14,    35, SPR_IMG_BUILD_CANAL,        STR_BUILD_CANALS_TIP},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    22,    43,    14,    35, SPR_IMG_BUILD_LOCK,         STR_BUILD_LOCKS_TIP},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,     7,    44,    47,    14,    35, 0x0,                        STR_NULL},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    48,    69,    14,    35, SPR_IMG_DYNAMITE,           STR_018D_DEMOLISH_BUILDINGS_ETC},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    70,    91,    14,    35, SPR_IMG_SHIP_DEPOT,         STR_981E_BUILD_SHIP_DEPOT_FOR_BUILDING},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    92,   113,    14,    35, SPR_IMG_SHIP_DOCK,          STR_981D_BUILD_SHIP_DOCK},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   114,   135,    14,    35, SPR_IMG_BOUY,               STR_9834_POSITION_BUOY_WHICH_CAN},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   136,   157,    14,    35, SPR_IMG_LANDSCAPING,        STR_LANDSCAPING_TOOLBAR_TIP},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_docks_toolbar_desc = {
 
	WDP_ALIGN_TBR, 22, 158, 36,
 
	WC_BUILD_TOOLBAR, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
 
	_build_docks_toolb_widgets,
 
	BuildDocksToolbWndProc
 
};
 

	
 
void ShowBuildDocksToolbar(void)
 
{
 
	if (!IsValidPlayer(_current_player)) return;
 

	
 
	DeleteWindowById(WC_BUILD_TOOLBAR, 0);
 
	AllocateWindowDesc(&_build_docks_toolbar_desc);
 
	if (_patches.link_terraform_toolbar) ShowTerraformToolbar();
 
}
 

	
 
static void BuildDockStationWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: LowerWindowWidget(w, _station_show_coverage + 3); break;
 

	
 
	case WE_PAINT: {
 
		int rad;
 

	
 
		if (WP(w,def_d).close) return;
 
		DrawWindowWidgets(w);
 

	
 
		rad = (_patches.modified_catchment) ? CA_DOCK : 4;
 

	
 
		if (_station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
 

	
 
		DrawStationCoverageAreaText(4, 50, (uint)-1, rad);
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
			case 3:
 
			case 4:
 
				RaiseWindowWidget(w, _station_show_coverage + 3);
 
				_station_show_coverage = e->we.click.widget - 3;
 
				LowerWindowWidget(w, _station_show_coverage + 3);
 
				SndPlayFx(SND_15_BEEP);
 
				SetWindowDirty(w);
 
				break;
 
		}
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		if (WP(w,def_d).close) {
 
			DeleteWindow(w);
 
			return;
 
		}
 

	
 
		CheckRedrawStationCoverage(w);
 
		break;
 

	
 
	case WE_DESTROY:
 
		if (!WP(w,def_d).close) ResetObjectToPlace();
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_dock_station_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   147,     0,    13, STR_3068_DOCK,                    STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   147,    14,    74, 0x0,                              STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    14,    73,    30,    40, STR_02DB_OFF,                     STR_3065_DON_T_HIGHLIGHT_COVERAGE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    74,   133,    30,    40, STR_02DA_ON,                      STR_3064_HIGHLIGHT_COVERAGE_AREA},
 
{      WWT_LABEL,   RESIZE_NONE,     7,     0,   147,    17,    30, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_dock_station_desc = {
 
	WDP_AUTO, WDP_AUTO, 148, 75,
 
	WC_BUILD_STATION, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_dock_station_widgets,
 
	BuildDockStationWndProc
 
};
 

	
 
static void ShowBuildDockStationPicker(void)
 
{
 
	AllocateWindowDesc(&_build_dock_station_desc);
 
}
 

	
 
static void UpdateDocksDirection(void)
 
{
 
	if (_ship_depot_direction != AXIS_X) {
 
		SetTileSelectSize(1, 2);
 
	} else {
 
		SetTileSelectSize(2, 1);
 
	}
 
}
 

	
 
static void BuildDocksDepotWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: LowerWindowWidget(w, _ship_depot_direction + 3); break;
 

	
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 

	
 
		DrawShipDepotSprite(67, 35, 0);
 
		DrawShipDepotSprite(35, 51, 1);
 
		DrawShipDepotSprite(135, 35, 2);
 
		DrawShipDepotSprite(167, 51, 3);
 
		return;
 

	
 
	case WE_CLICK: {
 
		switch (e->we.click.widget) {
 
		case 3:
 
		case 4:
 
			RaiseWindowWidget(w, _ship_depot_direction + 3);
 
			_ship_depot_direction = e->we.click.widget - 3;
 
			LowerWindowWidget(w, _ship_depot_direction + 3);
 
			SndPlayFx(SND_15_BEEP);
 
			UpdateDocksDirection();
 
			SetWindowDirty(w);
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_MOUSELOOP:
 
		if (WP(w,def_d).close) DeleteWindow(w);
 
		break;
 

	
 
	case WE_DESTROY:
 
		if (!WP(w,def_d).close) ResetObjectToPlace();
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_docks_depot_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   203,     0,    13, STR_3800_SHIP_DEPOT_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   203,    14,    85, 0x0,                             STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     3,   100,    17,    82, 0x0,                             STR_3803_SELECT_SHIP_DEPOT_ORIENTATION},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   103,   200,    17,    82, 0x0,                             STR_3803_SELECT_SHIP_DEPOT_ORIENTATION},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_docks_depot_desc = {
 
	WDP_AUTO, WDP_AUTO, 204, 86,
 
	WC_BUILD_DEPOT, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_docks_depot_widgets,
 
	BuildDocksDepotWndProc
 
};
 

	
 

	
 
static void ShowBuildDocksDepotPicker(void)
 
{
 
	AllocateWindowDesc(&_build_docks_depot_desc);
 
	UpdateDocksDirection();
 
}
 

	
 

	
 
void InitializeDockGui(void)
 
{
 
	_ship_depot_direction = AXIS_X;
 
}
src/driver.c
Show inline comments
 
deleted file
src/driver.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "driver.h"
 
#include "functions.h"
 
#include "hal.h"
 
#include "string.h"
 

	
 
#include "music/bemidi.h"
 
#include "music/dmusic.h"
 
#include "music/extmidi.h"
 
#include "music/null_m.h"
 
#include "music/os2_m.h"
 
#include "music/win32_m.h"
 
#include "music/qtmidi.h"
 

	
 
#include "sound/null_s.h"
 
#include "sound/sdl_s.h"
 
#include "sound/cocoa_s.h"
 
#include "sound/win32_s.h"
 

	
 
#include "video/dedicated_v.h"
 
#include "video/null_v.h"
 
#include "video/sdl_v.h"
 
#include "video/cocoa_v.h"
 
#include "video/win32_v.h"
 

	
 
typedef struct DriverDesc {
 
	const char* name;
 
	const char* longname;
 
	const HalCommonDriver* drv;
 
} DriverDesc;
 

	
 
typedef struct DriverClass {
 
	const DriverDesc *descs;
 
	const char *name;
 
	const HalCommonDriver** drv;
 
} DriverClass;
 

	
 

	
 
#define M(x, y, z) { x, y, (const HalCommonDriver *)(void *)z }
 
static const DriverDesc _music_driver_descs[] = {
 
#ifdef __BEOS__
 
	M("bemidi",  "BeOS MIDI Driver",        &_bemidi_music_driver),
 
#endif
 
#if defined(__OS2__) && !defined(__INNOTEK_LIBC__)
 
	M("os2",     "OS/2 Music Driver",       &_os2_music_driver),
 
#endif
 
#ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT
 
	M("dmusic",  "DirectMusic MIDI Driver", &_dmusic_midi_driver),
 
#endif
 
#ifdef WIN32
 
	M("win32",   "Win32 MIDI Driver",       &_win32_music_driver),
 
#endif
 
#if defined(__APPLE__) && !defined(DEDICATED)
 
	M("qt",      "QuickTime MIDI Driver",   &_qtime_music_driver),
 
#endif
 
#ifdef UNIX
 
#if !defined(__MORPHOS__) && !defined(__AMIGA__)
 
	M("extmidi", "External MIDI Driver",    &_extmidi_music_driver),
 
#endif
 
#endif
 
	M("null",    "Null Music Driver",       &_null_music_driver),
 
	M(NULL, NULL, NULL)
 
};
 

	
 
static const DriverDesc _sound_driver_descs[] = {
 
#ifdef WIN32
 
	M("win32", "Win32 WaveOut Driver", &_win32_sound_driver),
 
#endif
 
#ifdef WITH_SDL
 
	M("sdl",   "SDL Sound Driver",     &_sdl_sound_driver),
 
#endif
 
#ifdef WITH_COCOA
 
	M("cocoa", "Cocoa Sound Driver",   &_cocoa_sound_driver),
 
#endif
 
	M("null",  "Null Sound Driver",    &_null_sound_driver),
 
	M(NULL, NULL, NULL)
 
};
 

	
 
static const DriverDesc _video_driver_descs[] = {
 
#ifdef WIN32
 
	M("win32",      "Win32 GDI Video Driver", &_win32_video_driver),
 
#endif
 
#ifdef WITH_SDL
 
	M("sdl",        "SDL Video Driver",       &_sdl_video_driver),
 
#endif
 
#ifdef WITH_COCOA
 
	M("cocoa",      "Cocoa Video Driver",       &_cocoa_video_driver),
 
#endif
 
	M("null",       "Null Video Driver",      &_null_video_driver),
 
#ifdef ENABLE_NETWORK
 
	M("dedicated",  "Dedicated Video Driver", &_dedicated_video_driver),
 
#endif
 
	M(NULL, NULL, NULL)
 
};
 
#undef M
 

	
 

	
 
#define M(x, y, z) { x, y, (const HalCommonDriver **)(void *)z }
 
static const DriverClass _driver_classes[] = {
 
	M(_video_driver_descs, "video", &_video_driver),
 
	M(_sound_driver_descs, "sound", &_sound_driver),
 
	M(_music_driver_descs, "music", &_music_driver)
 
};
 
#undef M
 

	
 
static const DriverDesc* GetDriverByName(const DriverDesc* dd, const char* name)
 
{
 
	for (; dd->name != NULL; dd++) {
 
		if (strcmp(dd->name, name) == 0) return dd;
 
	}
 
	return NULL;
 
}
 

	
 
void LoadDriver(int driver, const char *name)
 
{
 
	const DriverClass *dc = &_driver_classes[driver];
 
	const DriverDesc *dd;
 
	const char *err;
 

	
 
	if (*name == '\0') {
 
		for (dd = dc->descs; dd->name != NULL; dd++) {
 
			err = dd->drv->start(NULL);
 
			if (err == NULL) break;
 
			DEBUG(driver, 1, "Probing %s driver '%s' failed with error: %s",
 
				dc->name, dd->name, err
 
			);
 
		}
 
		if (dd->name == NULL) error("Couldn't find any suitable %s driver", dc->name);
 

	
 
		DEBUG(driver, 1, "Successfully probed %s driver '%s'", dc->name, dd->name);
 

	
 
		*dc->drv = dd->drv;
 
	} else {
 
		char* parm;
 
		char buffer[256];
 
		const char* parms[32];
 

	
 
		// Extract the driver name and put parameter list in parm
 
		ttd_strlcpy(buffer, name, sizeof(buffer));
 
		parm = strchr(buffer, ':');
 
		parms[0] = NULL;
 
		if (parm != NULL) {
 
			uint np = 0;
 
			// Tokenize the parm.
 
			do {
 
				*parm++ = '\0';
 
				if (np < lengthof(parms) - 1)
 
					parms[np++] = parm;
 
				while (*parm != '\0' && *parm != ',')
 
					parm++;
 
			} while (*parm == ',');
 
			parms[np] = NULL;
 
		}
 
		dd = GetDriverByName(dc->descs, buffer);
 
		if (dd == NULL)
 
			error("No such %s driver: %s\n", dc->name, buffer);
 

	
 
		if (*dc->drv != NULL) (*dc->drv)->stop();
 
		*dc->drv = NULL;
 

	
 
		err = dd->drv->start(parms);
 
		if (err != NULL) {
 
			error("Unable to load driver %s(%s). The error was: %s\n",
 
				dd->name, dd->longname, err
 
			);
 
		}
 
		*dc->drv = dd->drv;
 
	}
 
}
 

	
 

	
 
static const char* GetDriverParam(const char* const* parm, const char* name)
 
{
 
	size_t len;
 

	
 
	if (parm == NULL) return NULL;
 

	
 
	len = strlen(name);
 
	for (; *parm != NULL; parm++) {
 
		const char* p = *parm;
 

	
 
		if (strncmp(p, name, len) == 0) {
 
			if (p[len] == '=')  return p + len + 1;
 
			if (p[len] == '\0') return p + len;
 
		}
 
	}
 
	return NULL;
 
}
 

	
 
bool GetDriverParamBool(const char* const* parm, const char* name)
 
{
 
	return GetDriverParam(parm, name) != NULL;
 
}
 

	
 
int GetDriverParamInt(const char* const* parm, const char* name, int def)
 
{
 
	const char* p = GetDriverParam(parm, name);
 
	return p != NULL ? atoi(p) : def;
 
}
 

	
 

	
 
char *GetDriverList(char* p, const char *last)
 
{
 
	const DriverClass* dc;
 

	
 
	for (dc = _driver_classes; dc != endof(_driver_classes); dc++) {
 
		const DriverDesc* dd;
 

	
 
		p += snprintf(p, last - p, "List of %s drivers:\n", dc->name);
 
		for (dd = dc->descs; dd->name != NULL; dd++) {
 
			p += snprintf(p, last - p, "%10s: %s\n", dd->name, dd->longname);
 
		}
 
		p = strecpy(p, "\n", last);
 
	}
 

	
 
	return p;
 
}
src/dummy_land.c
Show inline comments
 
deleted file
src/dummy_land.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "table/sprites.h"
 

	
 
static void DrawTile_Dummy(TileInfo *ti)
 
{
 
	DrawGroundSpriteAt(SPR_SHADOW_CELL, ti->x, ti->y, ti->z);
 
}
 

	
 

	
 
static uint GetSlopeZ_Dummy(TileIndex tile, uint x, uint y)
 
{
 
	return 0;
 
}
 

	
 
static Slope GetSlopeTileh_Dummy(TileIndex tile, Slope tileh)
 
{
 
	return SLOPE_FLAT;
 
}
 

	
 
static int32 ClearTile_Dummy(TileIndex tile, byte flags)
 
{
 
	return_cmd_error(STR_0001_OFF_EDGE_OF_MAP);
 
}
 

	
 

	
 
static void GetAcceptedCargo_Dummy(TileIndex tile, AcceptedCargo ac)
 
{
 
	/* not used */
 
}
 

	
 
static void GetTileDesc_Dummy(TileIndex tile, TileDesc *td)
 
{
 
	td->str = STR_EMPTY;
 
	td->owner = OWNER_NONE;
 
}
 

	
 
static void AnimateTile_Dummy(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static void TileLoop_Dummy(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static void ClickTile_Dummy(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static void ChangeTileOwner_Dummy(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	/* not used */
 
}
 

	
 
static uint32 GetTileTrackStatus_Dummy(TileIndex tile, TransportType mode)
 
{
 
	return 0;
 
}
 

	
 
const TileTypeProcs _tile_type_dummy_procs = {
 
	DrawTile_Dummy,           /* draw_tile_proc */
 
	GetSlopeZ_Dummy,          /* get_slope_z_proc */
 
	ClearTile_Dummy,          /* clear_tile_proc */
 
	GetAcceptedCargo_Dummy,   /* get_accepted_cargo_proc */
 
	GetTileDesc_Dummy,        /* get_tile_desc_proc */
 
	GetTileTrackStatus_Dummy, /* get_tile_track_status_proc */
 
	ClickTile_Dummy,          /* click_tile_proc */
 
	AnimateTile_Dummy,        /* animate_tile_proc */
 
	TileLoop_Dummy,           /* tile_loop_clear */
 
	ChangeTileOwner_Dummy,    /* change_tile_owner_clear */
 
	NULL,                     /* get_produced_cargo_proc */
 
	NULL,                     /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_Dummy,      /* get_slope_tileh_proc */
 
};
src/economy.c
Show inline comments
 
deleted file
src/economy.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "currency.h"
 
#include "functions.h"
 
#include "strings.h" // XXX InjectDParam()
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "map.h"
 
#include "news.h"
 
#include "player.h"
 
#include "station.h"
 
#include "vehicle.h"
 
#include "window.h"
 
#include "gfx.h"
 
#include "command.h"
 
#include "saveload.h"
 
#include "economy.h"
 
#include "industry.h"
 
#include "town.h"
 
#include "network/network.h"
 
#include "sound.h"
 
#include "engine.h"
 
#include "network/network_data.h"
 
#include "variables.h"
 
#include "vehicle_gui.h"
 
#include "ai/ai.h"
 
#include "train.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_sound.h"
 
#include "newgrf_callbacks.h"
 
#include "unmovable.h"
 
#include "date.h"
 

	
 
// Score info
 
const ScoreInfo _score_info[] = {
 
	{ SCORE_VEHICLES,        120, 100 },
 
	{ SCORE_STATIONS,         80, 100 },
 
	{ SCORE_MIN_PROFIT,    10000, 100 },
 
	{ SCORE_MIN_INCOME,    50000,  50 },
 
	{ SCORE_MAX_INCOME,   100000, 100 },
 
	{ SCORE_DELIVERED,     40000, 400 },
 
	{ SCORE_CARGO,             8,  50 },
 
	{ SCORE_MONEY,      10000000,  50 },
 
	{ SCORE_LOAN,         250000,  50 },
 
	{ SCORE_TOTAL,             0,   0 }
 
};
 

	
 
int _score_part[MAX_PLAYERS][NUM_SCORE];
 

	
 
int64 CalculateCompanyValue(const Player* p)
 
{
 
	PlayerID owner = p->index;
 
	int64 value;
 

	
 
	{
 
		Station *st;
 
		uint num = 0;
 

	
 
		FOR_ALL_STATIONS(st) {
 
			if (st->owner == owner) {
 
				uint facil = st->facilities;
 
				do num += (facil&1); while (facil >>= 1);
 
			}
 
		}
 

	
 
		value = num * _price.station_value * 25;
 
	}
 

	
 
	{
 
		Vehicle *v;
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->owner != owner) continue;
 

	
 
			if (v->type == VEH_Train ||
 
					v->type == VEH_Road ||
 
					(v->type == VEH_Aircraft && v->subtype<=2) ||
 
					v->type == VEH_Ship) {
 
				value += v->value * 3 >> 1;
 
			}
 
		}
 
	}
 

	
 
	value += p->money64 - p->current_loan; // add real money value
 

	
 
	return max64(value, 1);
 
}
 

	
 
// if update is set to true, the economy is updated with this score
 
//  (also the house is updated, should only be true in the on-tick event)
 
int UpdateCompanyRatingAndValue(Player *p, bool update)
 
{
 
	byte owner = p->index;
 
	int score = 0;
 

	
 
	memset(_score_part[owner], 0, sizeof(_score_part[owner]));
 

	
 
/* Count vehicles */
 
	{
 
		Vehicle *v;
 
		int32 min_profit = 0;
 
		bool min_profit_first = true;
 
		uint num = 0;
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->owner != owner) continue;
 
			if ((v->type == VEH_Train && IsFrontEngine(v)) ||
 
					 v->type == VEH_Road ||
 
					(v->type == VEH_Aircraft && v->subtype <= 2) ||
 
					 v->type == VEH_Ship) {
 
				num++;
 
				if (v->age > 730) {
 
					/* Find the vehicle with the lowest amount of profit */
 
					if (min_profit_first == true) {
 
						min_profit = v->profit_last_year;
 
						min_profit_first = false;
 
					} else if (min_profit > v->profit_last_year) {
 
						min_profit = v->profit_last_year;
 
					}
 
				}
 
			}
 
		}
 

	
 
		_score_part[owner][SCORE_VEHICLES] = num;
 
		/* Don't allow negative min_profit to show */
 
		if (min_profit > 0)
 
			_score_part[owner][SCORE_MIN_PROFIT] = min_profit;
 
	}
 

	
 
/* Count stations */
 
	{
 
		uint num = 0;
 
		const Station* st;
 

	
 
		FOR_ALL_STATIONS(st) {
 
			if (st->owner == owner) {
 
				int facil = st->facilities;
 
				do num += facil&1; while (facil>>=1);
 
			}
 
		}
 
		_score_part[owner][SCORE_STATIONS] = num;
 
	}
 

	
 
/* Generate statistics depending on recent income statistics */
 
	{
 
		const PlayerEconomyEntry* pee;
 
		int numec;
 
		int32 min_income;
 
		int32 max_income;
 

	
 
		numec = min(p->num_valid_stat_ent, 12);
 
		if (numec != 0) {
 
			min_income = 0x7FFFFFFF;
 
			max_income = 0;
 
			pee = p->old_economy;
 
			do {
 
				min_income = min(min_income, pee->income + pee->expenses);
 
				max_income = max(max_income, pee->income + pee->expenses);
 
			} while (++pee,--numec);
 

	
 
			if (min_income > 0)
 
				_score_part[owner][SCORE_MIN_INCOME] = min_income;
 

	
 
			_score_part[owner][SCORE_MAX_INCOME] = max_income;
 
		}
 
	}
 

	
 
/* Generate score depending on amount of transported cargo */
 
	{
 
		const PlayerEconomyEntry* pee;
 
		int numec;
 
		uint32 total_delivered;
 

	
 
		numec = min(p->num_valid_stat_ent, 4);
 
		if (numec != 0) {
 
			pee = p->old_economy;
 
			total_delivered = 0;
 
			do {
 
				total_delivered += pee->delivered_cargo;
 
			} while (++pee,--numec);
 

	
 
			_score_part[owner][SCORE_DELIVERED] = total_delivered;
 
		}
 
	}
 

	
 
/* Generate score for variety of cargo */
 
	{
 
		uint cargo = p->cargo_types;
 
		uint num = 0;
 
		do num += cargo&1; while (cargo>>=1);
 
		_score_part[owner][SCORE_CARGO] = num;
 
		if (update) p->cargo_types = 0;
 
	}
 

	
 
/* Generate score for player money */
 
	{
 
		int32 money = p->player_money;
 
		if (money > 0) {
 
			_score_part[owner][SCORE_MONEY] = money;
 
		}
 
	}
 

	
 
/* Generate score for loan */
 
	{
 
		_score_part[owner][SCORE_LOAN] = _score_info[SCORE_LOAN].needed - p->current_loan;
 
	}
 

	
 
	// Now we calculate the score for each item..
 
	{
 
		int i;
 
		int total_score = 0;
 
		int s;
 
		score = 0;
 
		for (i = 0; i < NUM_SCORE; i++) {
 
			// Skip the total
 
			if (i == SCORE_TOTAL) continue;
 
			// Check the score
 
			s = (_score_part[owner][i] >= _score_info[i].needed) ?
 
				_score_info[i].score :
 
				_score_part[owner][i] * _score_info[i].score / _score_info[i].needed;
 
			if (s < 0) s = 0;
 
			score += s;
 
			total_score += _score_info[i].score;
 
		}
 

	
 
		_score_part[owner][SCORE_TOTAL] = score;
 

	
 
		// We always want the score scaled to SCORE_MAX (1000)
 
		if (total_score != SCORE_MAX) score = score * SCORE_MAX / total_score;
 
	}
 

	
 
	if (update) {
 
		p->old_economy[0].performance_history = score;
 
		UpdateCompanyHQ(p, score);
 
		p->old_economy[0].company_value = CalculateCompanyValue(p);
 
	}
 

	
 
	InvalidateWindow(WC_PERFORMANCE_DETAIL, 0);
 
	return score;
 
}
 

	
 
// use PLAYER_SPECTATOR as new_player to delete the player.
 
void ChangeOwnershipOfPlayerItems(PlayerID old_player, PlayerID new_player)
 
{
 
	Town *t;
 
	PlayerID old = _current_player;
 
	_current_player = old_player;
 

	
 
	/* Temporarily increase the player's money, to be sure that
 
	 * removing his/her property doesn't fail because of lack of money.
 
	 * Not too drastically though, because it could overflow */
 
	if (new_player == PLAYER_SPECTATOR) {
 
		GetPlayer(old_player)->money64 = MAX_UVALUE(uint64) >>2; // jackpot ;p
 
		UpdatePlayerMoney32(GetPlayer(old_player));
 
	}
 

	
 
	if (new_player == PLAYER_SPECTATOR) {
 
		Subsidy *s;
 

	
 
		for (s = _subsidies; s != endof(_subsidies); s++) {
 
			if (s->cargo_type != CT_INVALID && s->age >= 12) {
 
				if (GetStation(s->to)->owner == old_player) s->cargo_type = CT_INVALID;
 
			}
 
		}
 
	}
 

	
 
	/* Take care of rating in towns */
 
	FOR_ALL_TOWNS(t) {
 
		/* If a player takes over, give the ratings to that player. */
 
		if (new_player != PLAYER_SPECTATOR) {
 
			if (HASBIT(t->have_ratings, old_player)) {
 
				if (HASBIT(t->have_ratings, new_player)) {
 
					// use max of the two ratings.
 
					t->ratings[new_player] = max(t->ratings[new_player], t->ratings[old_player]);
 
				} else {
 
					SETBIT(t->have_ratings, new_player);
 
					t->ratings[new_player] = t->ratings[old_player];
 
				}
 
			}
 
		}
 

	
 
		/* Reset the ratings for the old player */
 
		t->ratings[old_player] = 500;
 
		CLRBIT(t->have_ratings, old_player);
 
	}
 

	
 
	{
 
		int num_train = 0;
 
		int num_road = 0;
 
		int num_ship = 0;
 
		int num_aircraft = 0;
 
		Vehicle *v;
 

	
 
		// Determine Ids for the new vehicles
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->owner == new_player) {
 
				switch (v->type) {
 
					case VEH_Train:    if (IsFrontEngine(v)) num_train++; break;
 
					case VEH_Road:     num_road++; break;
 
					case VEH_Ship:     num_ship++; break;
 
					case VEH_Aircraft: if (v->subtype <= 2) num_aircraft++; break;
 
					default: break;
 
				}
 
			}
 
		}
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->owner == old_player && IS_BYTE_INSIDE(v->type, VEH_Train, VEH_Aircraft + 1)) {
 
				if (new_player == PLAYER_SPECTATOR) {
 
					DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 
					DeleteWindowById(WC_VEHICLE_DETAILS, v->index);
 
					DeleteWindowById(WC_VEHICLE_ORDERS, v->index);
 
					DeleteVehicle(v);
 
				} else {
 
					v->owner = new_player;
 
					if (IsEngineCountable(v)) GetPlayer(new_player)->num_engines[v->engine_type]++;
 
					switch (v->type) {
 
						case VEH_Train:    if (IsFrontEngine(v)) v->unitnumber = ++num_train; break;
 
						case VEH_Road:     v->unitnumber = ++num_road; break;
 
						case VEH_Ship:     v->unitnumber = ++num_ship; break;
 
						case VEH_Aircraft: if (v->subtype <= 2) v->unitnumber = ++num_aircraft; break;
 
					}
 
				}
 
			}
 
		}
 
	}
 

	
 
	// Change ownership of tiles
 
	{
 
		TileIndex tile = 0;
 
		do {
 
			ChangeTileOwner(tile, old_player, new_player);
 
		} while (++tile != MapSize());
 
	}
 

	
 
	/* Change color of existing windows */
 
	if (new_player != PLAYER_SPECTATOR) ChangeWindowOwner(old_player, new_player);
 

	
 
	{
 
		Player *p;
 
		uint i;
 

	
 
		/* Check for shares */
 
		FOR_ALL_PLAYERS(p) {
 
			for (i = 0; i < 4; i++) {
 
				/* 'Sell' the share if this player has any */
 
				if (p->share_owners[i] == _current_player) {
 
					p->share_owners[i] = PLAYER_SPECTATOR;
 
				}
 
			}
 
		}
 
		p = GetPlayer(_current_player);
 
		/* Sell all the shares that people have on this company */
 
		for (i = 0; i < 4; i++)
 
			p->share_owners[i] = PLAYER_SPECTATOR;
 
	}
 

	
 
	_current_player = old;
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
static void PlayersCheckBankrupt(Player *p)
 
{
 
	PlayerID owner;
 
	int64 val;
 

	
 
	// If the player has money again, it does not go bankrupt
 
	if (p->player_money >= 0) {
 
		p->quarters_of_bankrupcy = 0;
 
		return;
 
	}
 

	
 
	p->quarters_of_bankrupcy++;
 

	
 
	owner = p->index;
 

	
 
	switch (p->quarters_of_bankrupcy) {
 
		case 2:
 
			AddNewsItem( (StringID)(owner | NB_BTROUBLE),
 
				NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 
			break;
 
		case 3: {
 
			/* XXX - In multiplayer, should we ask other players if it wants to take
 
		          over when it is a human company? -- TrueLight */
 
			if (IsHumanPlayer(owner)) {
 
				AddNewsItem( (StringID)(owner | NB_BTROUBLE),
 
					NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 
				break;
 
			}
 

	
 
			// Check if the company has any value.. if not, declare it bankrupt
 
			//  right now
 
			val = CalculateCompanyValue(p);
 
			if (val > 0) {
 
				p->bankrupt_value = val;
 
				p->bankrupt_asked = 1 << owner; // Don't ask the owner
 
				p->bankrupt_timeout = 0;
 
				break;
 
			}
 
			// Else, falltrue to case 4...
 
		}
 
		case 4: {
 
			// Close everything the owner has open
 
			DeletePlayerWindows(owner);
 

	
 
//		Show bankrupt news
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			AddNewsItem( (StringID)(owner | NB_BBANKRUPT), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 

	
 
			if (IsHumanPlayer(owner)) {
 
				/* XXX - If we are in offline mode, leave the player playing. Eg. there
 
				 * is no THE-END, otherwise mark the player as spectator to make sure
 
				 * he/she is no long in control of this company */
 
				if (!_networking) {
 
					p->bankrupt_asked = 0xFF;
 
					p->bankrupt_timeout = 0x456;
 
					break;
 
				} else if (owner == _local_player) {
 
					_network_playas = PLAYER_SPECTATOR;
 
					SetLocalPlayer(PLAYER_SPECTATOR);
 
				}
 

	
 
#ifdef ENABLE_NETWORK
 
				/* The server has to handle all administrative issues, for example
 
				 * updating and notifying all clients of what has happened */
 
				if (_network_server) {
 
					const NetworkClientState *cs;
 
					NetworkClientInfo *ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 

	
 
					/* The server has just gone belly-up, mark it as spectator */
 
					if (owner == ci->client_playas) {
 
						ci->client_playas = PLAYER_SPECTATOR;
 
						NetworkUpdateClientInfo(NETWORK_SERVER_INDEX);
 
					}
 

	
 
					/* Find all clients that were in control of this company,
 
					 * and mark them as spectator; broadcast this message to everyone */
 
					FOR_ALL_CLIENTS(cs) {
 
						ci = DEREF_CLIENT_INFO(cs);
 
						if (ci->client_playas == owner) {
 
							ci->client_playas = PLAYER_SPECTATOR;
 
							NetworkUpdateClientInfo(ci->client_index);
 
						}
 
					}
 
				}
 
#endif /* ENABLE_NETWORK */
 
			}
 

	
 
			/* Remove the player */
 
			ChangeOwnershipOfPlayerItems(owner, PLAYER_SPECTATOR);
 
			/* Register the player as not-active */
 
			p->is_active = false;
 

	
 
			if (!IsHumanPlayer(owner) && (!_networking || _network_server) && _ai.enabled)
 
				AI_PlayerDied(owner);
 
		}
 
	}
 
}
 

	
 
void DrawNewsBankrupcy(Window *w)
 
{
 
	Player *p;
 

	
 
	DrawNewsBorder(w);
 

	
 
	p = GetPlayer(GB(WP(w,news_d).ni->string_id, 0, 4));
 
	DrawPlayerFace(p->face, p->player_color, 2, 23);
 
	GfxFillRect(3, 23, 3+91, 23+118, 0x323 | USE_COLORTABLE);
 

	
 
	SetDParam(0, p->president_name_1);
 
	SetDParam(1, p->president_name_2);
 

	
 
	DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94);
 

	
 
	switch (WP(w,news_d).ni->string_id & 0xF0) {
 
	case NB_BTROUBLE:
 
		DrawStringCentered(w->width>>1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, 0);
 

	
 
		SetDParam(0, p->name_1);
 
		SetDParam(1, p->name_2);
 

	
 
		DrawStringMultiCenter(
 
			((w->width - 101) >> 1) + 98,
 
			90,
 
			STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED,
 
			w->width - 101);
 
		break;
 

	
 
	case NB_BMERGER: {
 
		int32 price;
 

	
 
		DrawStringCentered(w->width>>1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, 0);
 
		COPY_IN_DPARAM(0,WP(w,news_d).ni->params, 2);
 
		SetDParam(2, p->name_1);
 
		SetDParam(3, p->name_2);
 
		price = WP(w,news_d).ni->params[2];
 
		SetDParam(4, price);
 
		DrawStringMultiCenter(
 
			((w->width - 101) >> 1) + 98,
 
			90,
 
			price==0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR,
 
			w->width - 101);
 
		break;
 
	}
 

	
 
	case NB_BBANKRUPT:
 
		DrawStringCentered(w->width>>1, 1, STR_705C_BANKRUPT, 0);
 
		COPY_IN_DPARAM(0,WP(w,news_d).ni->params, 2);
 
		DrawStringMultiCenter(
 
			((w->width - 101) >> 1) + 98,
 
			90,
 
			STR_705D_HAS_BEEN_CLOSED_DOWN_BY,
 
			w->width - 101);
 
		break;
 

	
 
	case NB_BNEWCOMPANY:
 
		DrawStringCentered(w->width>>1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, 0);
 
		SetDParam(0, p->name_1);
 
		SetDParam(1, p->name_2);
 
		COPY_IN_DPARAM(2,WP(w,news_d).ni->params, 2);
 
		DrawStringMultiCenter(
 
			((w->width - 101) >> 1) + 98,
 
			90,
 
			STR_705F_STARTS_CONSTRUCTION_NEAR,
 
			w->width - 101);
 
		break;
 

	
 
	default:
 
		NOT_REACHED();
 
	}
 
}
 

	
 
StringID GetNewsStringBankrupcy(const NewsItem *ni)
 
{
 
	const Player *p = GetPlayer(GB(ni->string_id, 0, 4));
 

	
 
	switch (ni->string_id & 0xF0) {
 
	case NB_BTROUBLE:
 
		SetDParam(0, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE);
 
		SetDParam(1, STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED);
 
		SetDParam(2, p->name_1);
 
		SetDParam(3, p->name_2);
 
		return STR_02B6;
 
	case NB_BMERGER:
 
		SetDParam(0, STR_7059_TRANSPORT_COMPANY_MERGER);
 
		SetDParam(1, STR_705A_HAS_BEEN_SOLD_TO_FOR);
 
		COPY_IN_DPARAM(2,ni->params, 2);
 
		SetDParam(4, p->name_1);
 
		SetDParam(5, p->name_2);
 
		COPY_IN_DPARAM(6,ni->params + 2, 1);
 
		return STR_02B6;
 
	case NB_BBANKRUPT:
 
		SetDParam(0, STR_705C_BANKRUPT);
 
		SetDParam(1, STR_705D_HAS_BEEN_CLOSED_DOWN_BY);
 
		COPY_IN_DPARAM(2,ni->params, 2);
 
		return STR_02B6;
 
	case NB_BNEWCOMPANY:
 
		SetDParam(0, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED);
 
		SetDParam(1, STR_705F_STARTS_CONSTRUCTION_NEAR);
 
		SetDParam(2, p->name_1);
 
		SetDParam(3, p->name_2);
 
		COPY_IN_DPARAM(4,ni->params, 2);
 
		return STR_02B6;
 
	default:
 
		NOT_REACHED();
 
	}
 

	
 
	/* useless, but avoids compiler warning this way */
 
	return 0;
 
}
 

	
 
static void PlayersGenStatistics(void)
 
{
 
	Station *st;
 
	Player *p;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		_current_player = st->owner;
 
		SET_EXPENSES_TYPE(EXPENSES_PROPERTY);
 
		SubtractMoneyFromPlayer(_price.station_value >> 1);
 
	}
 

	
 
	if (!HASBIT(1<<0|1<<3|1<<6|1<<9, _cur_month))
 
		return;
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active) {
 
			memmove(&p->old_economy[1], &p->old_economy[0], sizeof(p->old_economy) - sizeof(p->old_economy[0]));
 
			p->old_economy[0] = p->cur_economy;
 
			memset(&p->cur_economy, 0, sizeof(p->cur_economy));
 

	
 
			if (p->num_valid_stat_ent != 24) p->num_valid_stat_ent++;
 

	
 
			UpdateCompanyRatingAndValue(p, true);
 
			PlayersCheckBankrupt(p);
 

	
 
			if (p->block_preview != 0) p->block_preview--;
 
		}
 
	}
 

	
 
	InvalidateWindow(WC_INCOME_GRAPH, 0);
 
	InvalidateWindow(WC_OPERATING_PROFIT, 0);
 
	InvalidateWindow(WC_DELIVERED_CARGO, 0);
 
	InvalidateWindow(WC_PERFORMANCE_HISTORY, 0);
 
	InvalidateWindow(WC_COMPANY_VALUE, 0);
 
	InvalidateWindow(WC_COMPANY_LEAGUE, 0);
 
}
 

	
 
static void AddSingleInflation(int32 *value, uint16 *frac, int32 amt)
 
{
 
	int64 tmp;
 
	int32 low;
 
	tmp = BIGMULS(*value, amt);
 
	*frac = (uint16)(low = (uint16)tmp + *frac);
 
	*value += (int32)(tmp >> 16) + (low >> 16);
 
}
 

	
 
static void AddInflation(void)
 
{
 
	int i;
 
	int32 inf = _economy.infl_amount * 54;
 

	
 
	for (i = 0; i != NUM_PRICES; i++) {
 
		AddSingleInflation((int32*)&_price + i, _price_frac + i, inf);
 
	}
 

	
 
	_economy.max_loan_unround += BIGMULUS(_economy.max_loan_unround, inf, 16);
 

	
 
	if (_economy.max_loan + 50000 <= _economy.max_loan_unround)
 
		_economy.max_loan += 50000;
 

	
 
	inf = _economy.infl_amount_pr * 54;
 
	for (i = 0; i != NUM_CARGO; i++) {
 
		AddSingleInflation(
 
			(int32*)_cargo_payment_rates + i,
 
			_cargo_payment_rates_frac + i,
 
			inf
 
		);
 
	}
 

	
 
	InvalidateWindowClasses(WC_BUILD_VEHICLE);
 
	InvalidateWindowClasses(WC_REPLACE_VEHICLE);
 
	InvalidateWindowClasses(WC_VEHICLE_DETAILS);
 
	InvalidateWindow(WC_PAYMENT_RATES, 0);
 
}
 

	
 
static void PlayersPayInterest(void)
 
{
 
	const Player* p;
 
	int interest = _economy.interest_rate * 54;
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		if (!p->is_active) continue;
 

	
 
		_current_player = p->index;
 
		SET_EXPENSES_TYPE(EXPENSES_LOAN_INT);
 

	
 
		SubtractMoneyFromPlayer(BIGMULUS(p->current_loan, interest, 16));
 

	
 
		SET_EXPENSES_TYPE(EXPENSES_OTHER);
 
		SubtractMoneyFromPlayer(_price.station_value >> 2);
 
	}
 
}
 

	
 
static void HandleEconomyFluctuations(void)
 
{
 
	if (_opt.diff.economy == 0) return;
 

	
 
	if (--_economy.fluct == 0) {
 
		_economy.fluct = -(int)GB(Random(), 0, 2);
 
		AddNewsItem(STR_7073_WORLD_RECESSION_FINANCIAL, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0);
 
	} else if (_economy.fluct == -12) {
 
		_economy.fluct = GB(Random(), 0, 8) + 312;
 
		AddNewsItem(STR_7074_RECESSION_OVER_UPTURN_IN, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0);
 
	}
 
}
 

	
 
static byte _price_category[NUM_PRICES] = {
 
	0, 2, 2, 2, 2, 2, 2, 2,
 
	2, 2, 2, 2, 2, 2, 2, 2,
 
	2, 2, 2, 2, 2, 2, 2, 2,
 
	2, 2, 2, 2, 2, 2, 2, 2,
 
	2, 2, 2, 2, 2, 2, 2, 2,
 
	2, 2, 1, 1, 1, 1, 1, 1,
 
	2,
 
};
 

	
 
static const int32 _price_base[NUM_PRICES] = {
 
	    100, // station_value
 
	    100, // build_rail
 
	     95, // build_road
 
	     65, // build_signals
 
	    275, // build_bridge
 
	    600, // build_train_depot
 
	    500, // build_road_depot
 
	    700, // build_ship_depot
 
	    450, // build_tunnel
 
	    200, // train_station_track
 
	    180, // train_station_length
 
	    600, // build_airport
 
	    200, // build_bus_station
 
	    200, // build_truck_station
 
	    350, // build_dock
 
	 400000, // build_railvehicle
 
	   2000, // build_railwagon
 
	 700000, // aircraft_base
 
	  14000, // roadveh_base
 
	  65000, // ship_base
 
	     20, // build_trees
 
	    250, // terraform
 
	     20, // clear_1
 
	     40, // purchase_land
 
	    200, // clear_2
 
	    500, // clear_3
 
	     20, // remove_trees
 
	    -70, // remove_rail
 
	     10, // remove_signals
 
	     50, // clear_bridge
 
	     80, // remove_train_depot
 
	     80, // remove_road_depot
 
	     90, // remove_ship_depot
 
	     30, // clear_tunnel
 
	  10000, // clear_water
 
	     50, // remove_rail_station
 
	     30, // remove_airport
 
	     50, // remove_bus_station
 
	     50, // remove_truck_station
 
	     55, // remove_dock
 
	   1600, // remove_house
 
	     40, // remove_road
 
	   5600, // running_rail[0] railroad
 
	   5200, // running_rail[1] monorail
 
	   4800, // running_rail[2] maglev
 
	   9600, // aircraft_running
 
	   1600, // roadveh_running
 
	   5600, // ship_running
 
	1000000, // build_industry
 
};
 

	
 
static byte price_base_multiplier[NUM_PRICES];
 

	
 
/**
 
 * Reset changes to the price base multipliers.
 
 */
 
void ResetPriceBaseMultipliers(void)
 
{
 
	uint i;
 

	
 
	// 8 means no multiplier.
 
	for (i = 0; i < NUM_PRICES; i++)
 
		price_base_multiplier[i] = 8;
 
}
 

	
 
/**
 
 * Change a price base by the given factor.
 
 * The price base is altered by factors of two, with an offset of 8.
 
 * NewBaseCost = OldBaseCost * 2^(n-8)
 
 * @param price Index of price base to change.
 
 * @param factor Amount to change by.
 
 */
 
void SetPriceBaseMultiplier(uint price, byte factor)
 
{
 
	assert(price < NUM_PRICES);
 
	price_base_multiplier[price] = factor;
 
}
 

	
 
void StartupEconomy(void)
 
{
 
	int i;
 

	
 
	assert(sizeof(_price) == NUM_PRICES * sizeof(int32));
 

	
 
	for (i = 0; i != NUM_PRICES; i++) {
 
		int32 price = _price_base[i];
 
		if (_price_category[i] != 0) {
 
			uint mod = _price_category[i] == 1 ? _opt.diff.vehicle_costs : _opt.diff.construction_cost;
 
			if (mod < 1) {
 
				price = price * 3 >> 2;
 
			} else if (mod > 1) {
 
				price = price * 9 >> 3;
 
			}
 
		}
 
		if (price_base_multiplier[i] > 8) {
 
			price <<= price_base_multiplier[i] - 8;
 
		} else {
 
			price >>= 8 - price_base_multiplier[i];
 
		}
 
		((int32*)&_price)[i] = price;
 
		_price_frac[i] = 0;
 
	}
 

	
 
	_economy.interest_rate = _opt.diff.initial_interest;
 
	_economy.infl_amount = _opt.diff.initial_interest;
 
	_economy.infl_amount_pr = max(0, _opt.diff.initial_interest - 1);
 
	_economy.max_loan_unround = _economy.max_loan = _opt.diff.max_loan * 1000;
 
	_economy.fluct = GB(Random(), 0, 8) + 168;
 
}
 

	
 
Pair SetupSubsidyDecodeParam(const Subsidy* s, bool mode)
 
{
 
	TileIndex tile;
 
	TileIndex tile2;
 
	Pair tp;
 

	
 
	/* if mode is false, use the singular form */
 
	SetDParam(0, _cargoc.names_s[s->cargo_type] + (mode ? 0 : 32));
 

	
 
	if (s->age < 12) {
 
		if (s->cargo_type != CT_PASSENGERS && s->cargo_type != CT_MAIL) {
 
			SetDParam(1, STR_INDUSTRY);
 
			SetDParam(2, s->from);
 
			tile = GetIndustry(s->from)->xy;
 

	
 
			if (s->cargo_type != CT_GOODS && s->cargo_type != CT_FOOD) {
 
				SetDParam(4, STR_INDUSTRY);
 
				SetDParam(5, s->to);
 
				tile2 = GetIndustry(s->to)->xy;
 
			} else {
 
				SetDParam(4, STR_TOWN);
 
				SetDParam(5, s->to);
 
				tile2 = GetTown(s->to)->xy;
 
			}
 
		} else {
 
			SetDParam(1, STR_TOWN);
 
			SetDParam(2, s->from);
 
			tile = GetTown(s->from)->xy;
 

	
 
			SetDParam(4, STR_TOWN);
 
			SetDParam(5, s->to);
 
			tile2 = GetTown(s->to)->xy;
 
		}
 
	} else {
 
		SetDParam(1, s->from);
 
		tile = GetStation(s->from)->xy;
 

	
 
		SetDParam(2, s->to);
 
		tile2 = GetStation(s->to)->xy;
 
	}
 

	
 
	tp.a = tile;
 
	tp.b = tile2;
 

	
 
	return tp;
 
}
 

	
 
void DeleteSubsidyWithTown(TownID index)
 
{
 
	Subsidy *s;
 

	
 
	for (s = _subsidies; s != endof(_subsidies); s++) {
 
		if (s->cargo_type != CT_INVALID && s->age < 12 &&
 
				(((s->cargo_type == CT_PASSENGERS || s->cargo_type == CT_MAIL) && (index == s->from || index == s->to)) ||
 
				((s->cargo_type == CT_GOODS || s->cargo_type == CT_FOOD) && index == s->to))) {
 
			s->cargo_type = CT_INVALID;
 
		}
 
	}
 
}
 

	
 
void DeleteSubsidyWithIndustry(IndustryID index)
 
{
 
	Subsidy *s;
 

	
 
	for (s = _subsidies; s != endof(_subsidies); s++) {
 
		if (s->cargo_type != CT_INVALID && s->age < 12 &&
 
				s->cargo_type != CT_PASSENGERS && s->cargo_type != CT_MAIL &&
 
				(index == s->from || (s->cargo_type != CT_GOODS && s->cargo_type != CT_FOOD && index == s->to))) {
 
			s->cargo_type = CT_INVALID;
 
		}
 
	}
 
}
 

	
 
void DeleteSubsidyWithStation(StationID index)
 
{
 
	Subsidy *s;
 
	bool dirty = false;
 

	
 
	for (s = _subsidies; s != endof(_subsidies); s++) {
 
		if (s->cargo_type != CT_INVALID && s->age >= 12 &&
 
				(s->from == index || s->to == index)) {
 
			s->cargo_type = CT_INVALID;
 
			dirty = true;
 
		}
 
	}
 

	
 
	if (dirty)
 
		InvalidateWindow(WC_SUBSIDIES_LIST, 0);
 
}
 

	
 
typedef struct FoundRoute {
 
	uint distance;
 
	CargoID cargo;
 
	void *from;
 
	void *to;
 
} FoundRoute;
 

	
 
static void FindSubsidyPassengerRoute(FoundRoute *fr)
 
{
 
	Town *from,*to;
 

	
 
	fr->distance = (uint)-1;
 

	
 
	fr->from = from = GetRandomTown();
 
	if (from == NULL || from->population < 400) return;
 

	
 
	fr->to = to = GetRandomTown();
 
	if (from == to || to == NULL || to->population < 400 || to->pct_pass_transported > 42)
 
		return;
 

	
 
	fr->distance = DistanceManhattan(from->xy, to->xy);
 
}
 

	
 
static void FindSubsidyCargoRoute(FoundRoute *fr)
 
{
 
	Industry *i;
 
	int trans, total;
 
	CargoID cargo;
 

	
 
	fr->distance = (uint)-1;
 

	
 
	fr->from = i = GetRandomIndustry();
 
	if (i == NULL) return;
 

	
 
	// Randomize cargo type
 
	if (Random()&1 && i->produced_cargo[1] != CT_INVALID) {
 
		cargo = i->produced_cargo[1];
 
		trans = i->pct_transported[1];
 
		total = i->total_production[1];
 
	} else {
 
		cargo = i->produced_cargo[0];
 
		trans = i->pct_transported[0];
 
		total = i->total_production[0];
 
	}
 

	
 
	// Quit if no production in this industry
 
	//  or if the cargo type is passengers
 
	//  or if the pct transported is already large enough
 
	if (total == 0 || trans > 42 || cargo == CT_INVALID || cargo == CT_PASSENGERS)
 
		return;
 

	
 
	fr->cargo = cargo;
 

	
 
	if (cargo == CT_GOODS || cargo == CT_FOOD) {
 
		// The destination is a town
 
		Town *t = GetRandomTown();
 

	
 
		// Only want big towns
 
		if (t == NULL || t->population < 900) return;
 

	
 
		fr->distance = DistanceManhattan(i->xy, t->xy);
 
		fr->to = t;
 
	} else {
 
		// The destination is an industry
 
		Industry *i2 = GetRandomIndustry();
 

	
 
		// The industry must accept the cargo
 
		if (i == i2 || i == NULL ||
 
				(cargo != i2->accepts_cargo[0] &&
 
				cargo != i2->accepts_cargo[1] &&
 
				cargo != i2->accepts_cargo[2]))
 
			return;
 
		fr->distance = DistanceManhattan(i->xy, i2->xy);
 
		fr->to = i2;
 
	}
 
}
 

	
 
static bool CheckSubsidyDuplicate(Subsidy *s)
 
{
 
	const Subsidy* ss;
 

	
 
	for (ss = _subsidies; ss != endof(_subsidies); ss++) {
 
		if (s != ss &&
 
				ss->from == s->from &&
 
				ss->to == s->to &&
 
				ss->cargo_type == s->cargo_type) {
 
			s->cargo_type = CT_INVALID;
 
			return true;
 
		}
 
	}
 
	return false;
 
}
 

	
 

	
 
static void SubsidyMonthlyHandler(void)
 
{
 
	Subsidy *s;
 
	Pair pair;
 
	Station *st;
 
	uint n;
 
	FoundRoute fr;
 
	bool modified = false;
 

	
 
	for (s = _subsidies; s != endof(_subsidies); s++) {
 
		if (s->cargo_type == CT_INVALID) continue;
 

	
 
		if (s->age == 12-1) {
 
			pair = SetupSubsidyDecodeParam(s, 1);
 
			AddNewsItem(STR_202E_OFFER_OF_SUBSIDY_EXPIRED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);
 
			s->cargo_type = CT_INVALID;
 
			modified = true;
 
		} else if (s->age == 2*12-1) {
 
			st = GetStation(s->to);
 
			if (st->owner == _local_player) {
 
				pair = SetupSubsidyDecodeParam(s, 1);
 
				AddNewsItem(STR_202F_SUBSIDY_WITHDRAWN_SERVICE, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);
 
			}
 
			s->cargo_type = CT_INVALID;
 
			modified = true;
 
		} else {
 
			s->age++;
 
		}
 
	}
 

	
 
	// 25% chance to go on
 
	if (CHANCE16(1,4)) {
 
		// Find a free slot
 
		s = _subsidies;
 
		while (s->cargo_type != CT_INVALID) {
 
			if (++s == endof(_subsidies))
 
				goto no_add;
 
		}
 

	
 
		n = 1000;
 
		do {
 
			FindSubsidyPassengerRoute(&fr);
 
			if (fr.distance <= 70) {
 
				s->cargo_type = CT_PASSENGERS;
 
				s->from = ((Town*)fr.from)->index;
 
				s->to = ((Town*)fr.to)->index;
 
				goto add_subsidy;
 
			}
 
			FindSubsidyCargoRoute(&fr);
 
			if (fr.distance <= 70) {
 
				s->cargo_type = fr.cargo;
 
				s->from = ((Industry*)fr.from)->index;
 
				s->to = (fr.cargo == CT_GOODS || fr.cargo == CT_FOOD) ? ((Town*)fr.to)->index : ((Industry*)fr.to)->index;
 
	add_subsidy:
 
				if (!CheckSubsidyDuplicate(s)) {
 
					s->age = 0;
 
					pair = SetupSubsidyDecodeParam(s, 0);
 
					AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);
 
					modified = true;
 
					break;
 
				}
 
			}
 
		} while (n--);
 
	}
 
no_add:;
 
	if (modified)
 
		InvalidateWindow(WC_SUBSIDIES_LIST, 0);
 
}
 

	
 
static const SaveLoad _subsidies_desc[] = {
 
	    SLE_VAR(Subsidy, cargo_type, SLE_UINT8),
 
	    SLE_VAR(Subsidy, age,        SLE_UINT8),
 
	SLE_CONDVAR(Subsidy, from,       SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
 
	SLE_CONDVAR(Subsidy, from,       SLE_UINT16,                5, SL_MAX_VERSION),
 
	SLE_CONDVAR(Subsidy, to,         SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
 
	SLE_CONDVAR(Subsidy, to,         SLE_UINT16,                5, SL_MAX_VERSION),
 
	SLE_END()
 
};
 

	
 
static void Save_SUBS(void)
 
{
 
	int i;
 
	Subsidy *s;
 

	
 
	for (i = 0; i != lengthof(_subsidies); i++) {
 
		s = &_subsidies[i];
 
		if (s->cargo_type != CT_INVALID) {
 
			SlSetArrayIndex(i);
 
			SlObject(s, _subsidies_desc);
 
		}
 
	}
 
}
 

	
 
static void Load_SUBS(void)
 
{
 
	int index;
 
	while ((index = SlIterateArray()) != -1)
 
		SlObject(&_subsidies[index], _subsidies_desc);
 
}
 

	
 
int32 GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type)
 
{
 
	CargoID cargo = cargo_type;
 
	byte f;
 

	
 
	/* zero the distance if it's the bank and very short transport. */
 
	if (_opt.landscape == LT_NORMAL && cargo == CT_VALUABLES && dist < 10)
 
		dist = 0;
 

	
 
	f = 255;
 
	if (transit_days > _cargoc.transit_days_1[cargo]) {
 
		transit_days -= _cargoc.transit_days_1[cargo];
 
		f -= transit_days;
 

	
 
		if (transit_days > _cargoc.transit_days_2[cargo]) {
 
			transit_days -= _cargoc.transit_days_2[cargo];
 

	
 
			if (f < transit_days) {
 
				f = 0;
 
			} else {
 
				f -= transit_days;
 
			}
 
		}
 
	}
 
	if (f < 31) f = 31;
 

	
 
	return BIGMULSS(dist * f * num_pieces, _cargo_payment_rates[cargo], 21);
 
}
 

	
 
static void DeliverGoodsToIndustry(TileIndex xy, CargoID cargo_type, int num_pieces)
 
{
 
	Industry* best = NULL;
 
	Industry* ind;
 
	uint u;
 

	
 
	// Check if there's an industry close to the station that accepts the cargo
 
	// XXX - Think of something better to
 
	//       1) Only deliver to industries which are withing the catchment radius
 
	//       2) Distribute between industries if more then one is present
 
	u = (_patches.station_spread + 8) * 2;
 
	FOR_ALL_INDUSTRIES(ind) {
 
		uint t;
 

	
 
		if (( cargo_type == ind->accepts_cargo[0] ||
 
					cargo_type == ind->accepts_cargo[1] ||
 
					cargo_type == ind->accepts_cargo[2]
 
				) &&
 
				ind->produced_cargo[0] != CT_INVALID &&
 
				ind->produced_cargo[0] != cargo_type &&
 
				(t = DistanceManhattan(ind->xy, xy)) < u) {
 
			u = t;
 
			best = ind;
 
		}
 
	}
 

	
 
	/* Found one? */
 
	if (best != NULL) {
 
		best->was_cargo_delivered = true;
 
		best->cargo_waiting[0] = min(best->cargo_waiting[0] + num_pieces, 0xFFFF);
 
	}
 
}
 

	
 
static bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type)
 
{
 
	Subsidy *s;
 
	TileIndex xy;
 
	Pair pair;
 
	Player *p;
 

	
 
	// check if there is an already existing subsidy that applies to us
 
	for (s = _subsidies; s != endof(_subsidies); s++) {
 
		if (s->cargo_type == cargo_type &&
 
				s->age >= 12 &&
 
				s->from == from->index &&
 
				s->to == to->index) {
 
			return true;
 
		}
 
	}
 

	
 
	/* check if there's a new subsidy that applies.. */
 
	for (s = _subsidies; s != endof(_subsidies); s++) {
 
		if (s->cargo_type == cargo_type && s->age < 12) {
 
			/* Check distance from source */
 
			if (cargo_type == CT_PASSENGERS || cargo_type == CT_MAIL) {
 
				xy = GetTown(s->from)->xy;
 
			} else {
 
				xy = (GetIndustry(s->from))->xy;
 
			}
 
			if (DistanceMax(xy, from->xy) > 9) continue;
 

	
 
			/* Check distance from dest */
 
			switch (cargo_type) {
 
				case CT_PASSENGERS:
 
				case CT_MAIL:
 
				case CT_GOODS:
 
				case CT_FOOD:
 
					xy = GetTown(s->to)->xy;
 
					break;
 

	
 
				default:
 
					xy = GetIndustry(s->to)->xy;
 
					break;
 
			}
 
			if (DistanceMax(xy, to->xy) > 9) continue;
 

	
 
			/* Found a subsidy, change the values to indicate that it's in use */
 
			s->age = 12;
 
			s->from = from->index;
 
			s->to = to->index;
 

	
 
			/* Add a news item */
 
			pair = SetupSubsidyDecodeParam(s, 0);
 
			InjectDParam(2);
 

	
 
			p = GetPlayer(_current_player);
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			AddNewsItem(
 
				STR_2031_SERVICE_SUBSIDY_AWARDED + _opt.diff.subsidy_multiplier,
 
				NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0),
 
				pair.a, pair.b
 
			);
 

	
 
			InvalidateWindow(WC_SUBSIDIES_LIST, 0);
 
			return true;
 
		}
 
	}
 
	return false;
 
}
 

	
 
static int32 DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, StationID dest, byte days_in_transit)
 
{
 
	bool subsidised;
 
	Station *s_from, *s_to;
 
	int32 profit;
 

	
 
	assert(num_pieces > 0);
 

	
 
	// Update player statistics
 
	{
 
		Player *p = GetPlayer(_current_player);
 
		p->cur_economy.delivered_cargo += num_pieces;
 
		SETBIT(p->cargo_types, cargo_type);
 
	}
 

	
 
	// Get station pointers.
 
	s_from = GetStation(source);
 
	s_to = GetStation(dest);
 

	
 
	// Check if a subsidy applies.
 
	subsidised = CheckSubsidised(s_from, s_to, cargo_type);
 

	
 
	// Increase town's counter for some special goods types
 
	if (cargo_type == CT_FOOD) s_to->town->new_act_food += num_pieces;
 
	if (cargo_type == CT_WATER)  s_to->town->new_act_water += num_pieces;
 

	
 
	// Give the goods to the industry.
 
	DeliverGoodsToIndustry(s_to->xy, cargo_type, num_pieces);
 

	
 
	// Determine profit
 
	profit = GetTransportedGoodsIncome(num_pieces, DistanceManhattan(s_from->xy, s_to->xy), days_in_transit, cargo_type);
 

	
 
	// Modify profit if a subsidy is in effect
 
	if (subsidised) {
 
		switch (_opt.diff.subsidy_multiplier) {
 
			case 0:  profit += profit >> 1; break;
 
			case 1:  profit *= 2; break;
 
			case 2:  profit *= 3; break;
 
			default: profit *= 4; break;
 
		}
 
	}
 

	
 
	return profit;
 
}
 

	
 
/*
 
 * Returns true if Vehicle v should wait loading because other vehicle is
 
 * already loading the same cargo type
 
 * v = vehicle to load, u = GetFirstInChain(v)
 
 */
 
static bool LoadWait(const Vehicle* v, const Vehicle* u)
 
{
 
	const Vehicle *w;
 
	const Vehicle *x;
 
	bool has_any_cargo = false;
 

	
 
	if (!(u->current_order.flags & OF_FULL_LOAD)) return false;
 

	
 
	for (w = u; w != NULL; w = w->next) {
 
		if (w->cargo_count != 0) {
 
			if (v->cargo_type == w->cargo_type &&
 
					u->last_station_visited == w->cargo_source) {
 
				return false;
 
			}
 
			has_any_cargo = true;
 
		}
 
	}
 

	
 
	FOR_ALL_VEHICLES(x) {
 
		if ((x->type != VEH_Train || IsFrontEngine(x)) && // for all locs
 
				u->last_station_visited == x->last_station_visited && // at the same station
 
				!(x->vehstatus & (VS_STOPPED | VS_CRASHED)) && // not stopped or crashed
 
				x->current_order.type == OT_LOADING && // loading
 
				u != x) { // not itself
 
			bool other_has_any_cargo = false;
 
			bool has_space_for_same_type = false;
 
			bool other_has_same_type = false;
 

	
 
			for (w = x; w != NULL; w = w->next) {
 
				if (w->cargo_count < w->cargo_cap && v->cargo_type == w->cargo_type) {
 
					has_space_for_same_type = true;
 
				}
 

	
 
				if (w->cargo_count != 0) {
 
					if (v->cargo_type == w->cargo_type &&
 
							u->last_station_visited == w->cargo_source) {
 
						other_has_same_type = true;
 
					}
 
					other_has_any_cargo = true;
 
				}
 
			}
 

	
 
			if (has_space_for_same_type) {
 
				if (other_has_same_type) return true;
 
				if (other_has_any_cargo && !has_any_cargo) return true;
 
			}
 
		}
 
	}
 

	
 
	return false;
 
}
 

	
 
int LoadUnloadVehicle(Vehicle *v, bool just_arrived)
 
{
 
	int profit = 0;
 
	int v_profit = 0; //virtual profit for feeder systems
 
	int v_profit_total = 0;
 
	int unloading_time = 20;
 
	Vehicle *u = v;
 
	int result = 0;
 
	StationID last_visited;
 
	Station *st;
 
	int t;
 
	uint count, cap;
 
	PlayerID old_player;
 
	bool completely_empty = true;
 
	byte load_amount;
 
	bool anything_loaded = false;
 

	
 
	assert(v->current_order.type == OT_LOADING);
 

	
 
	v->cur_speed = 0;
 

	
 
	/* Loading can only have finished when all the cargo has been unloaded, and
 
	 * there is nothing left to load. It's easier to clear this if the
 
	 * conditions haven't been met than attempting to check them all before
 
	 * enabling though. */
 
	SETBIT(v->load_status, LS_LOADING_FINISHED);
 

	
 
	old_player = _current_player;
 
	_current_player = v->owner;
 

	
 
	last_visited = v->last_station_visited;
 
	st = GetStation(last_visited);
 

	
 
	for (; v != NULL; v = v->next) {
 
		GoodsEntry* ge;
 
		load_amount = EngInfo(v->engine_type)->load_amount;
 
		if (_patches.gradual_loading) {
 
			uint16 cb_load_amount = GetVehicleCallback(CBID_VEHICLE_LOAD_AMOUNT, 0, 0, v->engine_type, v);
 
			if (cb_load_amount != CALLBACK_FAILED) load_amount = cb_load_amount & 0xFF;
 
		}
 

	
 
		if (v->cargo_cap == 0) continue;
 

	
 
		/* If the vehicle has just arrived, set it to unload. */
 
		if (just_arrived) SETBIT(v->load_status, LS_CARGO_UNLOADING);
 

	
 
		ge = &st->goods[v->cargo_type];
 
		count = GB(ge->waiting_acceptance, 0, 12);
 

	
 
		/* unload? */
 
		if (v->cargo_count != 0 && HASBIT(v->load_status, LS_CARGO_UNLOADING)) {
 
			uint16 amount_unloaded = _patches.gradual_loading ? min(v->cargo_count, load_amount) : v->cargo_count;
 

	
 
			CLRBIT(u->load_status, LS_LOADING_FINISHED);
 

	
 
			if (v->cargo_source != last_visited && ge->waiting_acceptance & 0x8000 && !(u->current_order.flags & OF_TRANSFER)) {
 
				// deliver goods to the station
 
				st->time_since_unload = 0;
 

	
 
				unloading_time += v->cargo_count; /* TTDBUG: bug in original TTD */
 
				if (just_arrived && !HASBIT(v->load_status, LS_CARGO_PAID_FOR)) {
 
					profit += DeliverGoods(v->cargo_count, v->cargo_type, v->cargo_source, last_visited, v->cargo_days);
 
					SETBIT(v->load_status, LS_CARGO_PAID_FOR);
 
				}
 
				result |= 1;
 
				v->cargo_count -= amount_unloaded;
 
				if (_patches.gradual_loading) continue;
 
			} else if (u->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) {
 
				/* unload goods and let it wait at the station */
 
				st->time_since_unload = 0;
 
				if (just_arrived && (u->current_order.flags & OF_TRANSFER) && !HASBIT(v->load_status, LS_CARGO_PAID_FOR)) {
 
					v_profit = GetTransportedGoodsIncome(
 
						v->cargo_count,
 
						DistanceManhattan(GetStation(v->cargo_source)->xy, GetStation(last_visited)->xy),
 
						v->cargo_days,
 
						v->cargo_type) * 3 / 2;
 

	
 
					v_profit_total += v_profit;
 
					SETBIT(v->load_status, LS_CARGO_PAID_FOR);
 
				}
 

	
 
				unloading_time += v->cargo_count;
 
				t = GB(ge->waiting_acceptance, 0, 12);
 
				if (t == 0) {
 
					// No goods waiting at station
 
					ge->enroute_time = v->cargo_days;
 
					ge->enroute_from = v->cargo_source;
 
				} else {
 
					// Goods already waiting at station. Set counters to the worst value.
 
					if (v->cargo_days >= ge->enroute_time)
 
						ge->enroute_time = v->cargo_days;
 
					if (last_visited != ge->enroute_from)
 
						ge->enroute_from = v->cargo_source;
 
				}
 
				// Update amount of waiting cargo
 
				SB(ge->waiting_acceptance, 0, 12, min(amount_unloaded + t, 0xFFF));
 

	
 
				if (u->current_order.flags & OF_TRANSFER) {
 
					ge->feeder_profit += v_profit;
 
					u->profit_this_year += v_profit;
 
				}
 
				result |= 2;
 
				v->cargo_count -= amount_unloaded;
 
				if (_patches.gradual_loading) continue;
 
			}
 

	
 
			if (v->cargo_count != 0) completely_empty = false;
 
		}
 

	
 
		/* The vehicle must have been unloaded because it is either empty, or
 
		 * the UNLOADING bit is already clear in v->load_status. */
 
		CLRBIT(v->load_status, LS_CARGO_UNLOADING);
 
		CLRBIT(v->load_status, LS_CARGO_PAID_FOR);
 

	
 
		/* don't pick up goods that we unloaded */
 
		if (u->current_order.flags & OF_UNLOAD) continue;
 

	
 
		/* update stats */
 
		ge->days_since_pickup = 0;
 
		switch (u->type) {
 
			case VEH_Train: t = u->u.rail.cached_max_speed; break;
 
			case VEH_Road:  t = u->max_speed / 2;           break;
 
			default:        t = u->max_speed;               break;
 
		}
 

	
 
		// if last speed is 0, we treat that as if no vehicle has ever visited the station.
 
		ge->last_speed = min(t, 255);
 
		ge->last_age = _cur_year - v->build_year;
 

	
 
		// If there's goods waiting at the station, and the vehicle
 
		//  has capacity for it, load it on the vehicle.
 
		if (count != 0 &&
 
				(cap = v->cargo_cap - v->cargo_count) != 0) {
 
			int cargoshare;
 
			int feeder_profit_share;
 

	
 
			if (v->cargo_count == 0)
 
				TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
 

	
 
			/* Skip loading this vehicle if another train/vehicle is already handling
 
			 * the same cargo type at this station */
 
			if (_patches.improved_load && (u->current_order.flags & OF_FULL_LOAD) && LoadWait(v,u)) continue;
 

	
 
			/* TODO: Regarding this, when we do gradual loading, we
 
			 * should first unload all vehicles and then start
 
			 * loading them. Since this will cause
 
			 * VEHICLE_TRIGGER_EMPTY to be called at the time when
 
			 * the whole vehicle chain is really totally empty, the
 
			 * @completely_empty assignment can then be safely
 
			 * removed; that's how TTDPatch behaves too. --pasky */
 
			completely_empty = false;
 
			anything_loaded = true;
 

	
 
			if (cap > count) cap = count;
 
			if (_patches.gradual_loading) cap = min(cap, load_amount);
 
			if (cap < count) CLRBIT(u->load_status, LS_LOADING_FINISHED);
 
			cargoshare = cap * 10000 / ge->waiting_acceptance;
 
			feeder_profit_share = ge->feeder_profit * cargoshare / 10000;
 
			v->cargo_count += cap;
 
			ge->waiting_acceptance -= cap;
 
			u->profit_this_year -= feeder_profit_share;
 
			ge->feeder_profit -= feeder_profit_share;
 
			unloading_time += cap;
 
			st->time_since_load = 0;
 

	
 
			// And record the source of the cargo, and the days in travel.
 
			v->cargo_source = ge->enroute_from;
 
			v->cargo_days = ge->enroute_time;
 
			result |= 2;
 
			st->last_vehicle_type = v->type;
 
		}
 
	}
 

	
 
	v = u;
 

	
 
	if (_patches.gradual_loading) {
 
		/* The time it takes to load one 'slice' of cargo or passengers depends
 
		 * on the vehicle type - the values here are those found in TTDPatch */
 
		uint gradual_loading_wait_time[] = { 40, 20, 10, 20 };
 

	
 
		unloading_time = gradual_loading_wait_time[v->type - VEH_Train];
 
		if (HASBIT(v->load_status, LS_LOADING_FINISHED)) {
 
			if (anything_loaded) {
 
				unloading_time += 20;
 
			} else {
 
				unloading_time = 20;
 
			}
 
		}
 
	}
 

	
 
	if (v_profit_total > 0) {
 
		ShowFeederIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, v_profit_total);
 
	}
 

	
 
	if (v->type == VEH_Train) {
 
		// Each platform tile is worth 2 rail vehicles.
 
		int overhang = v->u.rail.cached_total_length - GetStationPlatforms(st, v->tile) * TILE_SIZE;
 
		if (overhang > 0) {
 
			unloading_time <<= 1;
 
			unloading_time += (overhang * unloading_time) / 8;
 
		}
 
	}
 

	
 
	v->load_unload_time_rem = unloading_time;
 

	
 
	if (completely_empty) {
 
		TriggerVehicle(v, VEHICLE_TRIGGER_EMPTY);
 
	}
 

	
 
	if (result != 0) {
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		MarkStationTilesDirty(st);
 

	
 
		if (result & 2) InvalidateWindow(WC_STATION_VIEW, last_visited);
 

	
 
		if (profit != 0) {
 
			v->profit_this_year += profit;
 
			SubtractMoneyFromPlayer(-profit);
 

	
 
			if (IsLocalPlayer() && !PlayVehicleSound(v, VSE_LOAD_UNLOAD)) {
 
				SndPlayVehicleFx(SND_14_CASHTILL, v);
 
			}
 

	
 
			ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, -profit);
 
		}
 
	}
 

	
 
	_current_player = old_player;
 
	return result;
 
}
 

	
 
void PlayersMonthlyLoop(void)
 
{
 
	PlayersGenStatistics();
 
	if (_patches.inflation && _cur_year < MAX_YEAR)
 
		AddInflation();
 
	PlayersPayInterest();
 
	// Reset the _current_player flag
 
	_current_player = OWNER_NONE;
 
	HandleEconomyFluctuations();
 
	SubsidyMonthlyHandler();
 
}
 

	
 
static void DoAcquireCompany(Player *p)
 
{
 
	Player *owner;
 
	int i,pi;
 
	int64 value;
 

	
 
	SetDParam(0, p->name_1);
 
	SetDParam(1, p->name_2);
 
	SetDParam(2, p->bankrupt_value);
 
	AddNewsItem( (StringID)(_current_player | NB_BMERGER), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 

	
 
	// original code does this a little bit differently
 
	pi = p->index;
 
	ChangeOwnershipOfPlayerItems(pi, _current_player);
 

	
 
	if (p->bankrupt_value == 0) {
 
		owner = GetPlayer(_current_player);
 
		owner->current_loan += p->current_loan;
 
	}
 

	
 
	value = CalculateCompanyValue(p) >> 2;
 
	for (i = 0; i != 4; i++) {
 
		if (p->share_owners[i] != PLAYER_SPECTATOR) {
 
			owner = GetPlayer(p->share_owners[i]);
 
			owner->money64 += value;
 
			owner->yearly_expenses[0][EXPENSES_OTHER] += value;
 
			UpdatePlayerMoney32(owner);
 
		}
 
	}
 

	
 
	p->is_active = false;
 

	
 
	DeletePlayerWindows(pi);
 
	RebuildVehicleLists(); //Updates the open windows to add the newly acquired vehicles to the lists
 
}
 

	
 
extern int GetAmountOwnedBy(Player *p, byte owner);
 

	
 
/** Acquire shares in an opposing company.
 
 * @param tile unused
 
 * @param p1 player to buy the shares from
 
 * @param p2 unused
 
 */
 
int32 CmdBuyShareInCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Player *p;
 
	int64 cost;
 

	
 
	/* Check if buying shares is allowed (protection against modified clients */
 
	if (!IsValidPlayer((PlayerID)p1) || !_patches.allow_shares) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_OTHER);
 
	p = GetPlayer(p1);
 

	
 
	/* Protect new companies from hostile takeovers */
 
	if (_cur_year - p->inaugurated_year < 6) return_cmd_error(STR_7080_PROTECTED);
 

	
 
	/* Those lines are here for network-protection (clients can be slow) */
 
	if (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 0) return 0;
 

	
 
	/* We can not buy out a real player (temporarily). TODO: well, enable it obviously */
 
	if (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 1 && !p->is_ai) return 0;
 

	
 
	cost = CalculateCompanyValue(p) >> 2;
 
	if (flags & DC_EXEC) {
 
		PlayerID* b = p->share_owners;
 
		int i;
 

	
 
		while (*b != PLAYER_SPECTATOR) b++; /* share owners is guaranteed to contain at least one PLAYER_SPECTATOR */
 
		*b = _current_player;
 

	
 
		for (i = 0; p->share_owners[i] == _current_player;) {
 
			if (++i == 4) {
 
				p->bankrupt_value = 0;
 
				DoAcquireCompany(p);
 
				break;
 
			}
 
		}
 
		InvalidateWindow(WC_COMPANY, p1);
 
	}
 
	return cost;
 
}
 

	
 
/** Sell shares in an opposing company.
 
 * @param tile unused
 
 * @param p1 player to sell the shares from
 
 * @param p2 unused
 
 */
 
int32 CmdSellShareInCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Player *p;
 
	int64 cost;
 

	
 
	/* Check if buying shares is allowed (protection against modified clients */
 
	if (!IsValidPlayer((PlayerID)p1) || !_patches.allow_shares) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_OTHER);
 
	p = GetPlayer(p1);
 

	
 
	/* Those lines are here for network-protection (clients can be slow) */
 
	if (GetAmountOwnedBy(p, _current_player) == 0) return 0;
 

	
 
	/* adjust it a little to make it less profitable to sell and buy */
 
	cost = CalculateCompanyValue(p) >> 2;
 
	cost = -(cost - (cost >> 7));
 

	
 
	if (flags & DC_EXEC) {
 
		PlayerID* b = p->share_owners;
 
		while (*b != _current_player) b++; /* share owners is guaranteed to contain player */
 
		*b = PLAYER_SPECTATOR;
 
		InvalidateWindow(WC_COMPANY, p1);
 
	}
 
	return cost;
 
}
 

	
 
/** Buy up another company.
 
 * When a competing company is gone bankrupt you get the chance to purchase
 
 * that company.
 
 * @todo currently this only works for AI players
 
 * @param tile unused
 
 * @param p1 player/company to buy up
 
 * @param p2 unused
 
 */
 
int32 CmdBuyCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Player *p;
 

	
 
	/* Disable takeovers in multiplayer games */
 
	if (!IsValidPlayer((PlayerID)p1) || _networking) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_OTHER);
 
	p = GetPlayer(p1);
 

	
 
	if (!p->is_ai) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		DoAcquireCompany(p);
 
	}
 
	return p->bankrupt_value;
 
}
 

	
 
// Prices
 
static void SaveLoad_PRIC(void)
 
{
 
	SlArray(&_price,      NUM_PRICES, SLE_INT32);
 
	SlArray(&_price_frac, NUM_PRICES, SLE_UINT16);
 
}
 

	
 
// Cargo payment rates
 
static void SaveLoad_CAPR(void)
 
{
 
	SlArray(&_cargo_payment_rates,      NUM_CARGO, SLE_INT32);
 
	SlArray(&_cargo_payment_rates_frac, NUM_CARGO, SLE_UINT16);
 
}
 

	
 
static const SaveLoad _economy_desc[] = {
 
	SLE_VAR(Economy, max_loan,         SLE_INT32),
 
	SLE_VAR(Economy, max_loan_unround, SLE_INT32),
 
	SLE_VAR(Economy, fluct,            SLE_FILE_I16 | SLE_VAR_I32),
 
	SLE_VAR(Economy, interest_rate,    SLE_UINT8),
 
	SLE_VAR(Economy, infl_amount,      SLE_UINT8),
 
	SLE_VAR(Economy, infl_amount_pr,   SLE_UINT8),
 
	SLE_END()
 
};
 

	
 
// Economy variables
 
static void SaveLoad_ECMY(void)
 
{
 
	SlObject(&_economy, _economy_desc);
 
}
 

	
 
const ChunkHandler _economy_chunk_handlers[] = {
 
	{ 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, CH_RIFF | CH_AUTO_LENGTH},
 
	{ 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, CH_RIFF | CH_AUTO_LENGTH},
 
	{ 'SUBS', Save_SUBS,     Load_SUBS,     CH_ARRAY},
 
	{ 'ECMY', SaveLoad_ECMY, SaveLoad_ECMY, CH_RIFF | CH_LAST},
 
};
src/elrail.c
Show inline comments
 
deleted file
src/elrail.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 
/** @file elrail.c
 
 * This file deals with displaying wires and pylons for electric railways.
 
 * <h2>Basics</h2>
 
 *
 
 * <h3>Tile Types</h3>
 
 *
 
 * We have two different types of tiles in the drawing code:
 
 * Normal Railway Tiles (NRTs) which can have more than one track on it, and
 
 * Special Railways tiles (SRTs) which have only one track (like crossings, depots
 
 * stations, etc).
 
 *
 
 * <h3>Location Categories</h3>
 
 *
 
 * All tiles are categorized into three location groups (TLG):
 
 * Group 0: Tiles with both an even X coordinate and an even Y coordinate
 
 * Group 1: Tiles with an even X and an odd Y coordinate
 
 * Group 2: Tiles with an odd X and an even Y coordinate
 
 * Group 3: Tiles with both an odd X and Y coordnate.
 
 *
 
 * <h3>Pylon Points</h3>
 
 * <h4>Control Points</h4>
 
 * A Pylon Control Point (PCP) is a position where a wire (or rather two)
 
 * is mounted onto a pylon.
 
 * Each NRT does contain 4 PCPs which are bitmapped to a byte
 
 * variable and are represented by the DiagDirection enum
 
 *
 
 * Each track ends on two PCPs and thus requires one pylon on each end. However,
 
 * there is one exception: Straight-and-level tracks only have one pylon every
 
 * other tile.
 
 *
 
 * Now on each edge there are two PCPs: One from each adjacent tile. Both PCPs
 
 * are merged using an OR operation (i. e. if one tile needs a PCP at the postion
 
 * in question, both tiles get it).
 
 *
 
 * <h4>Position Points</h4>
 
 * A Pylon Position Point (PPP) is a position where a pylon is located on the
 
 * ground.  Each PCP owns 8 in (45 degree steps) PPPs that are located around
 
 * it. PPPs are represented using the Direction enum. Each track bit has PPPs
 
 * that are impossible (because the pylon would be situated on the track) and
 
 * some that are preferred (because the pylon would be rectangular to the track).
 
 *
 
 * <img src="../../elrail_tile.png">
 
 * <img src="../../elrail_track.png">
 
 *
 
 */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "station_map.h"
 
#include "tile.h"
 
#include "viewport.h"
 
#include "functions.h" /* We should REALLY get rid of this goddamn file, as it is butt-ugly */
 
#include "variables.h" /* ... same here */
 
#include "rail.h"
 
#include "debug.h"
 
#include "tunnel_map.h"
 
#include "road_map.h"
 
#include "bridge_map.h"
 
#include "bridge.h"
 
#include "rail_map.h"
 
#include "table/sprites.h"
 
#include "table/elrail_data.h"
 
#include "vehicle.h"
 
#include "train.h"
 
#include "gui.h"
 

	
 
static inline TLG GetTLG(TileIndex t)
 
{
 
	return (HASBIT(TileX(t), 0) << 1) + HASBIT(TileY(t), 0);
 
}
 

	
 
/** Finds which Rail Bits are present on a given tile. For bridge tiles,
 
 * returns track bits under the bridge
 
 */
 
static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
 
{
 
	switch (GetTileType(t)) {
 
		case MP_RAILWAY:
 
			if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
 
			switch (GetRailTileType(t)) {
 
				case RAIL_TILE_NORMAL: case RAIL_TILE_SIGNALS:
 
					return GetTrackBits(t);
 
				case RAIL_TILE_DEPOT_WAYPOINT:
 
					if (GetRailTileSubtype(t) == RAIL_SUBTYPE_WAYPOINT) return GetRailWaypointBits(t);
 
				default:
 
					return 0;
 
			}
 
			break;
 

	
 
		case MP_TUNNELBRIDGE:
 
			if (IsTunnel(t)) {
 
				if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
 
				if (override != NULL) *override = 1 << GetTunnelDirection(t);
 
				return AxisToTrackBits(DiagDirToAxis(GetTunnelDirection(t)));
 
			} else {
 
				if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
 
				if (override != NULL && DistanceMax(t, GetOtherBridgeEnd(t)) > 1) {
 
					*override = 1 << GetBridgeRampDirection(t);
 
				}
 
				return AxisToTrackBits(DiagDirToAxis(GetBridgeRampDirection(t)));
 
			}
 

	
 
		case MP_STREET:
 
			if (GetRoadTileType(t) != ROAD_TILE_CROSSING) return 0;
 
			if (GetRailTypeCrossing(t) != RAILTYPE_ELECTRIC) return 0;
 
			return GetCrossingRailBits(t);
 

	
 
		case MP_STATION:
 
			if (!IsRailwayStation(t)) return 0;
 
			if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
 
			if (!IsStationTileElectrifiable(t)) return 0;
 
			return TrackToTrackBits(GetRailStationTrack(t));
 

	
 
		default:
 
			return 0;
 
	}
 
}
 

	
 
/** Corrects the tileh for certain tile types. Returns an effective tileh for the track on the tile.
 
 * @param tile The tile to analyse
 
 * @param *tileh the tileh
 
 */
 
static void AdjustTileh(TileIndex tile, Slope *tileh)
 
{
 
	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
		if (IsTunnel(tile)) {
 
			*tileh = SLOPE_FLAT;
 
		} else {
 
			if (*tileh != SLOPE_FLAT) {
 
				*tileh = SLOPE_FLAT;
 
			} else {
 
				switch (GetBridgeRampDirection(tile)) {
 
					case DIAGDIR_NE: *tileh = SLOPE_NE; break;
 
					case DIAGDIR_SE: *tileh = SLOPE_SE; break;
 
					case DIAGDIR_SW: *tileh = SLOPE_SW; break;
 
					case DIAGDIR_NW: *tileh = SLOPE_NW; break;
 
					default: NOT_REACHED();
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 
/** Draws wires and, if required, pylons on a given tile
 
 * @param ti The Tileinfo to draw the tile for
 
 */
 
static void DrawCatenaryRailway(const TileInfo *ti)
 
{
 
	/* Pylons are placed on a tile edge, so we need to take into account
 
	 * the track configuration of 2 adjacent tiles. trackconfig[0] stores the
 
	 * current tile (home tile) while [1] holds the neighbour */
 
	TrackBits trackconfig[TS_END];
 
	bool isflat[TS_END];
 
	/* Note that ti->tileh has already been adjusted for Foundations */
 
	Slope tileh[TS_END] = { ti->tileh, SLOPE_FLAT };
 

	
 
	TLG tlg = GetTLG(ti->tile);
 
	byte PCPstatus = 0;
 
	byte OverridePCP = 0;
 
	byte PPPpreferred[DIAGDIR_END];
 
	byte PPPallowed[DIAGDIR_END];
 
	DiagDirection i;
 
	Track t;
 

	
 
	/* Find which rail bits are present, and select the override points.
 
	 * We don't draw a pylon:
 
	 * 1) INSIDE a tunnel (we wouldn't see it anyway)
 
	 * 2) on the "far" end of a bridge head (the one that connects to bridge middle),
 
	 *    because that one is drawn on the bridge. Exception is for length 0 bridges
 
	 *    which have no middle tiles */
 
	trackconfig[TS_HOME] = GetRailTrackBitsUniversal(ti->tile, &OverridePCP);
 
	/* If a track bit is present that is not in the main direction, the track is level */
 
	isflat[TS_HOME] = trackconfig[TS_HOME] & (TRACK_BIT_HORZ | TRACK_BIT_VERT);
 

	
 
	AdjustTileh(ti->tile, &tileh[TS_HOME]);
 

	
 
	for (i = DIAGDIR_NE; i < DIAGDIR_END; i++) {
 
		TileIndex neighbour = ti->tile + TileOffsByDiagDir(i);
 
		uint foundation = 0;
 
		int k;
 

	
 
		/* Here's one of the main headaches. GetTileSlope does not correct for possibly
 
		 * existing foundataions, so we do have to do that manually later on.*/
 
		tileh[TS_NEIGHBOUR] = GetTileSlope(neighbour, NULL);
 
		trackconfig[TS_NEIGHBOUR] = GetRailTrackBitsUniversal(neighbour, NULL);
 
		if (IsTunnelTile(neighbour) && i != GetTunnelDirection(neighbour)) trackconfig[TS_NEIGHBOUR] = 0;
 
		isflat[TS_NEIGHBOUR] = trackconfig[TS_NEIGHBOUR] & (TRACK_BIT_HORZ | TRACK_BIT_VERT);
 

	
 
		PPPpreferred[i] = 0xFF; /* We start with preferring everything (end-of-line in any direction) */
 
		PPPallowed[i] = AllowedPPPonPCP[i];
 

	
 
		/* We cycle through all the existing tracks at a PCP and see what
 
		 * PPPs we want to have, or may not have at all */
 
		for (k = 0; k < NUM_TRACKS_AT_PCP; k++) {
 
			/* Next to us, we have a bridge head, don't worry about that one, if it shows away from us */
 
			if (TrackSourceTile[i][k] == TS_NEIGHBOUR &&
 
			    IsBridgeTile(neighbour) &&
 
			    GetBridgeRampDirection(neighbour) == ReverseDiagDir(i)) {
 
				continue;
 
			}
 

	
 
			/* We check whether the track in question (k) is present in the tile
 
			 * (TrackSourceTile) */
 
			if (HASBIT(trackconfig[TrackSourceTile[i][k]], TracksAtPCP[i][k])) {
 
				/* track found, if track is in the neighbour tile, adjust the number
 
				 * of the PCP for preferred/allowed determination*/
 
				DiagDirection PCPpos = (TrackSourceTile[i][k] == TS_HOME) ? i : ReverseDiagDir(i);
 
				SETBIT(PCPstatus, i); /* This PCP is in use */
 

	
 
				PPPpreferred[i] &= PreferredPPPofTrackAtPCP[TracksAtPCP[i][k]][PCPpos];
 
				PPPallowed[i] &= ~DisallowedPPPofTrackAtPCP[TracksAtPCP[i][k]][PCPpos];
 
			}
 
		}
 

	
 
		/* Deactivate all PPPs if PCP is not used */
 
		PPPpreferred[i] *= HASBIT(PCPstatus, i);
 
		PPPallowed[i] *= HASBIT(PCPstatus, i);
 

	
 
		/* A station is always "flat", so adjust the tileh accordingly */
 
		if (IsTileType(neighbour, MP_STATION)) tileh[TS_NEIGHBOUR] = SLOPE_FLAT;
 

	
 
		/* Read the foundataions if they are present, and adjust the tileh */
 
		if (IsTileType(neighbour, MP_RAILWAY) && GetRailType(neighbour) == RAILTYPE_ELECTRIC) foundation = GetRailFoundation(tileh[TS_NEIGHBOUR], trackconfig[TS_NEIGHBOUR]);
 
		if (IsBridgeTile(neighbour)) {
 
			foundation = GetBridgeFoundation(tileh[TS_NEIGHBOUR], DiagDirToAxis(GetBridgeRampDirection(neighbour)));
 
		}
 

	
 
		if (foundation != 0) {
 
			if (foundation < 15) {
 
				tileh[TS_NEIGHBOUR] = SLOPE_FLAT;
 
			} else {
 
				tileh[TS_NEIGHBOUR] = _inclined_tileh[foundation - 15];
 
			}
 
		}
 

	
 
		AdjustTileh(neighbour, &tileh[TS_NEIGHBOUR]);
 

	
 
		/* If we have a straight (and level) track, we want a pylon only every 2 tiles
 
		 * Delete the PCP if this is the case. */
 
		/* Level means that the slope is the same, or the track is flat */
 
		if (tileh[TS_HOME] == tileh[TS_NEIGHBOUR] || (isflat[TS_HOME] && isflat[TS_NEIGHBOUR])) {
 
			for (k = 0; k < NUM_IGNORE_GROUPS; k++)
 
				if (PPPpreferred[i] == IgnoredPCP[k][tlg][i]) CLRBIT(PCPstatus, i);
 
		}
 

	
 
		/* Now decide where we draw our pylons. First try the preferred PPPs, but they may not exist.
 
		 * In that case, we try the any of the allowed ones. if they don't exist either, don't draw
 
		 * anything. Note that the preferred PPPs still contain the end-of-line markers.
 
		 * Remove those (simply by ANDing with allowed, since these markers are never allowed) */
 
		if ((PPPallowed[i] & PPPpreferred[i]) != 0) PPPallowed[i] &= PPPpreferred[i];
 

	
 
		if (MayHaveBridgeAbove(ti->tile) && IsBridgeAbove(ti->tile)) {
 
			Track bridgetrack = GetBridgeAxis(ti->tile) == AXIS_X ? TRACK_X : TRACK_Y;
 
			uint height = GetBridgeHeight(GetNorthernBridgeEnd(ti->tile));
 

	
 
			if ((height <= TilePixelHeight(ti->tile) + TILE_HEIGHT) &&
 
			(i == PCPpositions[bridgetrack][0] || i == PCPpositions[bridgetrack][1])) SETBIT(OverridePCP, i);
 
		}
 

	
 
		if (PPPallowed[i] != 0 && HASBIT(PCPstatus, i) && !HASBIT(OverridePCP, i)) {
 
			for (k = 0; k < DIR_END; k++) {
 
				byte temp = PPPorder[i][GetTLG(ti->tile)][k];
 

	
 
				if (HASBIT(PPPallowed[i], temp)) {
 
					uint x  = ti->x + x_pcp_offsets[i] + x_ppp_offsets[temp];
 
					uint y  = ti->y + y_pcp_offsets[i] + y_ppp_offsets[temp];
 

	
 
					/* Don't build the pylon if it would be outside the tile */
 
					if (!HASBIT(OwnedPPPonPCP[i], temp)) {
 
						/* We have a neighour that will draw it, bail out */
 
						if (trackconfig[TS_NEIGHBOUR] != 0) break;
 
						continue; /* No neighbour, go looking for a better position */
 
					}
 

	
 
					AddSortableSpriteToDraw(pylons_normal[temp], x, y, 1, 1, 10,
 
							GetSlopeZ(ti->x + x_pcp_offsets[i], ti->y + y_pcp_offsets[i]));
 
					break; /* We already have drawn a pylon, bail out */
 
				}
 
			}
 
		}
 
	}
 

	
 
	/* Don't draw a wire under a low bridge */
 
	if (MayHaveBridgeAbove(ti->tile) && IsBridgeAbove(ti->tile) && !(_display_opt & DO_TRANS_BUILDINGS)) {
 
		uint height = GetBridgeHeight(GetNorthernBridgeEnd(ti->tile));
 

	
 
		if (height <= TilePixelHeight(ti->tile) + TILE_HEIGHT) return;
 
	}
 

	
 
	/* Drawing of pylons is finished, now draw the wires */
 
	for (t = 0; t < TRACK_END; t++) {
 
		if (HASBIT(trackconfig[TS_HOME], t)) {
 

	
 
			byte PCPconfig = HASBIT(PCPstatus, PCPpositions[t][0]) +
 
				(HASBIT(PCPstatus, PCPpositions[t][1]) << 1);
 

	
 
			const SortableSpriteStruct *sss;
 
			int tileh_selector = !(tileh[TS_HOME] % 3) * tileh[TS_HOME] / 3; /* tileh for the slopes, 0 otherwise */
 

	
 
			assert(PCPconfig != 0); /* We have a pylon on neither end of the wire, that doesn't work (since we have no sprites for that) */
 
			assert(!IsSteepSlope(tileh[TS_HOME]));
 
			sss = &CatenarySpriteData[Wires[tileh_selector][t][PCPconfig]];
 

	
 
			AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
 
				sss->x_size, sss->y_size, sss->z_size, GetSlopeZ(ti->x + min(sss->x_offset, TILE_SIZE - 1), ti->y + min(sss->y_offset, TILE_SIZE - 1)) + sss->z_offset);
 
		}
 
	}
 
}
 

	
 
static void DrawCatenaryOnBridge(const TileInfo *ti)
 
{
 
	TileIndex end = GetSouthernBridgeEnd(ti->tile);
 
	TileIndex start = GetOtherBridgeEnd(end);
 

	
 
	uint length = GetBridgeLength(start, end);
 
	uint num = DistanceMax(ti->tile, start);
 
	uint height;
 

	
 
	const SortableSpriteStruct *sss;
 
	Axis axis = GetBridgeAxis(ti->tile);
 
	TLG tlg = GetTLG(ti->tile);
 

	
 
	CatenarySprite offset = axis == AXIS_X ? 0 : WIRE_Y_FLAT_BOTH - WIRE_X_FLAT_BOTH;
 

	
 
	if ((length % 2) && num == length) {
 
		/* Draw the "short" wire on the southern end of the bridge
 
		 * only needed if the length of the bridge is odd */
 
		sss = &CatenarySpriteData[WIRE_X_FLAT_BOTH + offset];
 
	} else {
 
		/* Draw "long" wires on all other tiles of the bridge (one pylon every two tiles) */
 
		sss = &CatenarySpriteData[WIRE_X_FLAT_SW + (num % 2) + offset];
 
	}
 

	
 
	height = GetBridgeHeight(end);
 

	
 
	AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
 
		sss->x_size, sss->y_size, sss->z_size, height + sss->z_offset
 
	);
 

	
 
	/* Finished with wires, draw pylons */
 
	/* every other tile needs a pylon on the northern end */
 
	if (num % 2) {
 
		if (axis == AXIS_X) {
 
			AddSortableSpriteToDraw(pylons_bridge[0 + HASBIT(tlg, 0)], ti->x, ti->y + 4 + 8 * HASBIT(tlg, 0), 1, 1, 10, height);
 
		} else {
 
			AddSortableSpriteToDraw(pylons_bridge[2 + HASBIT(tlg, 1)], ti->x + 4 + 8 * HASBIT(tlg, 1), ti->y, 1, 1, 10, height);
 
		}
 
	}
 

	
 
	/* need a pylon on the southern end of the bridge */
 
	if (DistanceMax(ti->tile, start) == length) {
 
		if (axis == AXIS_X) {
 
			AddSortableSpriteToDraw(pylons_bridge[0 + HASBIT(tlg, 0)], ti->x + 16, ti->y + 4 + 8 * HASBIT(tlg, 0), 1, 1, 10, height);
 
		} else {
 
			AddSortableSpriteToDraw(pylons_bridge[2 + HASBIT(tlg, 1)], ti->x + 4 + 8 * HASBIT(tlg, 1), ti->y + 16, 1, 1, 10, height);
 
		}
 
	}
 
}
 

	
 
void DrawCatenary(const TileInfo *ti)
 
{
 
	if (MayHaveBridgeAbove(ti->tile) && IsBridgeAbove(ti->tile)) {
 
		TileIndex head = GetNorthernBridgeEnd(ti->tile);
 

	
 
		if (GetBridgeTransportType(head) == TRANSPORT_RAIL && GetRailType(head) == RAILTYPE_ELECTRIC) {
 
			DrawCatenaryOnBridge(ti);
 
		}
 
	}
 
	if (_patches.disable_elrails) return;
 

	
 
	switch (GetTileType(ti->tile)) {
 
		case MP_RAILWAY:
 
			if (IsRailDepot(ti->tile)) {
 
				const SortableSpriteStruct* sss = &CatenarySpriteData_Depot[GetRailDepotDirection(ti->tile)];
 

	
 
				AddSortableSpriteToDraw(
 
					sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
 
					sss->x_size, sss->y_size, sss->z_size,
 
					GetTileMaxZ(ti->tile) + sss->z_offset
 
				);
 
				return;
 
			}
 
			break;
 

	
 
		case MP_TUNNELBRIDGE:
 
		case MP_STREET:
 
		case MP_STATION:
 
			break;
 

	
 
		default: return;
 
	}
 
	DrawCatenaryRailway(ti);
 
}
 

	
 
int32 SettingsDisableElrail(int32 p1)
 
{
 
	EngineID e_id;
 
	Vehicle* v;
 
	Player *p;
 
	bool disable = (p1 != 0);
 

	
 
	/* we will now walk through all electric train engines and change their railtypes if it is the wrong one*/
 
	const RailType old_railtype = disable ? RAILTYPE_ELECTRIC : RAILTYPE_RAIL;
 
	const RailType new_railtype = disable ? RAILTYPE_RAIL : RAILTYPE_ELECTRIC;
 

	
 
	/* walk through all train engines */
 
	for (e_id = 0; e_id < NUM_TRAIN_ENGINES; e_id++) {
 
		const RailVehicleInfo *rv_info = RailVehInfo(e_id);
 
		Engine *e = GetEngine(e_id);
 
		/* if it is an electric rail engine and its railtype is the wrong one */
 
		if (rv_info->engclass == 2 && e->railtype == old_railtype) {
 
			/* change it to the proper one */
 
			e->railtype = new_railtype;
 
		}
 
	}
 

	
 
	/* when disabling elrails, make sure that all existing trains can run on
 
	*  normal rail too */
 
	if (disable) {
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->type == VEH_Train && v->u.rail.railtype == RAILTYPE_ELECTRIC) {
 
				/* this railroad vehicle is now compatible only with elrail,
 
				*  so add there also normal rail compatibility */
 
				v->u.rail.compatible_railtypes |= (1 << RAILTYPE_RAIL);
 
				v->u.rail.railtype = RAILTYPE_RAIL;
 
				SETBIT(v->u.rail.flags, VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL);
 
			}
 
		}
 
	}
 

	
 
	/* setup total power for trains */
 
	FOR_ALL_VEHICLES(v) {
 
		/* power is cached only for front engines */
 
		if (v->type == VEH_Train && IsFrontEngine(v)) TrainPowerChanged(v);
 
	}
 

	
 
	FOR_ALL_PLAYERS(p) p->avail_railtypes = GetPlayerRailtypes(p->index);
 

	
 
	/* This resets the _last_built_railtype, which will be invalid for electric
 
	* rails. It may have unintended consequences if that function is ever
 
	* extended, though. */
 
	ReinitGuiAfterToggleElrail(disable);
 
	return 0;
 
}
src/endian_check.c
Show inline comments
 
deleted file
src/endian_check.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include <stdio.h>
 

	
 
// This pretty simple file checks if the system is LITTLE_ENDIAN or BIG_ENDIAN
 
//  it does that by putting a 1 and a 0 in an array, and read it out as one
 
//  number. If it is 1, it is LITTLE_ENDIAN, if it is 256, it is BIG_ENDIAN
 
//
 
// After that it outputs the contents of an include files (endian.h)
 
//  that says or TTD_LITTLE_ENDIAN, or TTD_BIG_ENDIAN. Makefile takes
 
//  care of the real writing to the file.
 

	
 
int main (int argc, char *argv[]) {
 
	unsigned char EndianTest[2] = { 1, 0 };
 
	int force_BE = 0, force_LE = 0, force_PREPROCESSOR = 0;
 

	
 
	if (argc > 1 && strcmp(argv[1], "BE") == 0)
 
		force_BE = 1;
 
	if (argc > 1 && strcmp(argv[1], "LE") == 0)
 
		force_LE = 1;
 
	if (argc > 1 && strcmp(argv[1], "PREPROCESSOR") == 0)
 
		force_PREPROCESSOR = 1;
 

	
 
	printf("#ifndef ENDIAN_H\n#define ENDIAN_H\n");
 

	
 
	if (force_LE == 1) {
 
		printf("#define TTD_LITTLE_ENDIAN\n");
 
	} else {
 
		if (force_BE == 1) {
 
			printf("#define TTD_BIG_ENDIAN\n");
 
		} else {
 
			if (force_PREPROCESSOR == 1) {
 
				// adding support for universal binaries on OSX
 
				// Universal binaries supports both PPC and x86
 
				// If a compiler for OSX gets this setting, it will always pick the correct endian and no test is needed
 
				printf("#ifdef __BIG_ENDIAN__\n");
 
				printf("#define TTD_BIG_ENDIAN\n");
 
				printf("#else\n");
 
				printf("#define TTD_LITTLE_ENDIAN\n");
 
				printf("#endif\n");
 
			} else {
 
				if ( *(short *) EndianTest == 1 )
 
					printf("#define TTD_LITTLE_ENDIAN\n");
 
				else
 
					printf("#define TTD_BIG_ENDIAN\n");
 
			}
 
		}
 
	}
 
	printf("#endif\n");
 

	
 
	return 0;
 
}
src/engine.c
Show inline comments
 
deleted file
src/engine.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "table/strings.h"
 
#include "engine.h"
 
#include "gfx.h"
 
#include "player.h"
 
#include "command.h"
 
#include "vehicle.h"
 
#include "news.h"
 
#include "saveload.h"
 
#include "variables.h"
 
#include "train.h"
 
#include "newgrf_cargo.h"
 
#include "date.h"
 
#include "table/engines.h"
 

	
 
EngineInfo _engine_info[TOTAL_NUM_ENGINES];
 
RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES];
 
ShipVehicleInfo _ship_vehicle_info[NUM_SHIP_ENGINES];
 
AircraftVehicleInfo _aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES];
 
RoadVehicleInfo _road_vehicle_info[NUM_ROAD_ENGINES];
 

	
 
enum {
 
	ENGINE_AVAILABLE   = 1,
 
	ENGINE_INTRODUCING = 2,
 
	ENGINE_PREVIEWING  = 4,
 
};
 

	
 
enum {
 
	YEAR_ENGINE_AGING_STOPS = 2050,
 
};
 

	
 

	
 
void ShowEnginePreviewWindow(EngineID engine);
 

	
 
void DeleteCustomEngineNames(void)
 
{
 
	uint i;
 
	StringID old;
 

	
 
	for (i = 0; i != TOTAL_NUM_ENGINES; i++) {
 
		old = _engine_name_strings[i];
 
		_engine_name_strings[i] = i + STR_8000_KIRBY_PAUL_TANK_STEAM;
 
		DeleteName(old);
 
	}
 

	
 
	_vehicle_design_names &= ~1;
 
}
 

	
 
void LoadCustomEngineNames(void)
 
{
 
	/* XXX: not done */
 
	DEBUG(misc, 1, "LoadCustomEngineNames: not done");
 
}
 

	
 
static void SetupEngineNames(void)
 
{
 
	StringID *name;
 

	
 
	for (name = _engine_name_strings; name != endof(_engine_name_strings); name++)
 
		*name = STR_SV_EMPTY;
 

	
 
	DeleteCustomEngineNames();
 
	LoadCustomEngineNames();
 
}
 

	
 
static void AdjustAvailAircraft(void)
 
{
 
	byte avail = 0;
 
	if (_cur_year >= 1955) avail |= 2; // big airport
 
	if (_cur_year <  1960 || _patches.always_small_airport) avail |= 1;  // small airport
 
	if (_cur_year >= 1963) avail |= 4; // enable heliport
 

	
 
	if (avail != _avail_aircraft) {
 
		_avail_aircraft = avail;
 
		InvalidateWindow(WC_BUILD_STATION, 0);
 
	}
 
}
 

	
 
static void CalcEngineReliability(Engine *e)
 
{
 
	uint age = e->age;
 

	
 
	if (age < e->duration_phase_1) {
 
		uint start = e->reliability_start;
 
		e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
 
	} else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _patches.never_expire_vehicles) {
 
		/* We are at the peak of this engines life. It will have max reliability.
 
		 * This is also true if the engines never expire. They will not go bad over time */
 
		e->reliability = e->reliability_max;
 
	} else if ((age -= e->duration_phase_2) < e->duration_phase_3) {
 
		uint max = e->reliability_max;
 
		e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max;
 
	} else {
 
		/* time's up for this engine.
 
		 * We will now completely retire this design */
 
		e->player_avail = 0;
 
		e->reliability = e->reliability_final;
 
		InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Kick this engine out of the lists
 
	}
 
	InvalidateWindowClasses(WC_BUILD_VEHICLE); // Update to show the new reliability
 
}
 

	
 
void AddTypeToEngines(void)
 
{
 
	Engine* e = _engines;
 

	
 
	do e->type = VEH_Train;    while (++e < &_engines[ROAD_ENGINES_INDEX]);
 
	do e->type = VEH_Road;     while (++e < &_engines[SHIP_ENGINES_INDEX]);
 
	do e->type = VEH_Ship;     while (++e < &_engines[AIRCRAFT_ENGINES_INDEX]);
 
	do e->type = VEH_Aircraft; while (++e < &_engines[TOTAL_NUM_ENGINES]);
 
	do e->type = VEH_Special;  while (++e < endof(_engines));
 
}
 

	
 
void StartupEngines(void)
 
{
 
	Engine *e;
 
	const EngineInfo *ei;
 
	/* Aging of vehicles stops, so account for that when starting late */
 
	const Date aging_date = min(_date, ConvertYMDToDate(YEAR_ENGINE_AGING_STOPS, 0, 1));
 

	
 
	SetupEngineNames();
 

	
 
	for (e = _engines, ei = _engine_info; e != endof(_engines); e++, ei++) {
 
		uint32 r;
 

	
 
		e->age = 0;
 
		e->railtype = ei->railtype;
 
		e->flags = 0;
 
		e->player_avail = 0;
 

	
 
		// The magic value of 729 days below comes from the NewGRF spec. If the
 
		// base intro date is before 1922 then the random number of days is not
 
		// added.
 
		r = Random();
 
		e->intro_date = ei->base_intro <= ConvertYMDToDate(1922, 0, 1) ? ei->base_intro : (Date)GB(r, 0, 9) + ei->base_intro;
 
		if (e->intro_date <= _date) {
 
			e->age = (aging_date - e->intro_date) >> 5;
 
			e->player_avail = (byte)-1;
 
			e->flags |= ENGINE_AVAILABLE;
 
		}
 

	
 
		e->reliability_start = GB(r, 16, 14) + 0x7AE0;
 
		r = Random();
 
		e->reliability_max   = GB(r,  0, 14) + 0xBFFF;
 
		e->reliability_final = GB(r, 16, 14) + 0x3FFF;
 

	
 
		r = Random();
 
		e->duration_phase_1 = GB(r, 0, 5) + 7;
 
		e->duration_phase_2 = GB(r, 5, 4) + ei->base_life * 12 - 96;
 
		e->duration_phase_3 = GB(r, 9, 7) + 120;
 

	
 
		e->reliability_spd_dec = (ei->unk2&0x7F) << 2;
 

	
 
		/* my invented flag for something that is a wagon */
 
		if (ei->unk2 & 0x80) {
 
			e->age = 0xFFFF;
 
		} else {
 
			CalcEngineReliability(e);
 
		}
 

	
 
		e->lifelength = ei->lifelength + _patches.extend_vehicle_life;
 

	
 
		// prevent certain engines from ever appearing.
 
		if (!HASBIT(ei->climates, _opt.landscape)) {
 
			e->flags |= ENGINE_AVAILABLE;
 
			e->player_avail = 0;
 
		}
 

	
 
		/* This sets up type for the engine
 
		 * It is needed if you want to ask the engine what type it is
 
		 * It should hopefully be the same as when you ask a vehicle what it is
 
		 * but using this, you can ask what type an engine number is
 
		 * even if it is not a vehicle (yet)*/
 
	}
 

	
 
	AdjustAvailAircraft();
 
}
 

	
 
static void AcceptEnginePreview(Engine *e, PlayerID player)
 
{
 
	Player *p = GetPlayer(player);
 

	
 
	assert(e->railtype < RAILTYPE_END);
 
	SETBIT(e->player_avail, player);
 
	SETBIT(p->avail_railtypes, e->railtype);
 

	
 
	e->preview_player = 0xFF;
 
	if (player == _local_player) {
 
		InvalidateWindowClassesData(WC_BUILD_VEHICLE);
 
		InvalidateWindowClasses(WC_REPLACE_VEHICLE);
 
	}
 
}
 

	
 
static PlayerID GetBestPlayer(PlayerID pp)
 
{
 
	const Player *p;
 
	int32 best_hist;
 
	PlayerID best_player;
 
	uint mask = 0;
 

	
 
	do {
 
		best_hist = -1;
 
		best_player = PLAYER_SPECTATOR;
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active && p->block_preview == 0 && !HASBIT(mask, p->index) &&
 
					p->old_economy[0].performance_history > best_hist) {
 
				best_hist = p->old_economy[0].performance_history;
 
				best_player = p->index;
 
			}
 
		}
 

	
 
		if (best_player == PLAYER_SPECTATOR) return PLAYER_SPECTATOR;
 

	
 
		SETBIT(mask, best_player);
 
	} while (--pp != 0);
 

	
 
	return best_player;
 
}
 

	
 
void EnginesDailyLoop(void)
 
{
 
	EngineID i;
 

	
 
	if (_cur_year >= YEAR_ENGINE_AGING_STOPS) return;
 

	
 
	for (i = 0; i != lengthof(_engines); i++) {
 
		Engine *e = &_engines[i];
 

	
 
		if (e->flags & ENGINE_INTRODUCING) {
 
			if (e->flags & ENGINE_PREVIEWING) {
 
				if (e->preview_player != 0xFF && !--e->preview_wait) {
 
					e->flags &= ~ENGINE_PREVIEWING;
 
					DeleteWindowById(WC_ENGINE_PREVIEW, i);
 
					e->preview_player++;
 
				}
 
			} else if (e->preview_player != 0xFF) {
 
				PlayerID best_player = GetBestPlayer(e->preview_player);
 

	
 
				if (best_player == PLAYER_SPECTATOR) {
 
					e->preview_player = 0xFF;
 
					continue;
 
				}
 

	
 
				if (!IsHumanPlayer(best_player)) {
 
					/* XXX - TTDBUG: TTD has a bug here ???? */
 
					AcceptEnginePreview(e, best_player);
 
				} else {
 
					e->flags |= ENGINE_PREVIEWING;
 
					e->preview_wait = 20;
 
					if (IsInteractivePlayer(best_player)) ShowEnginePreviewWindow(i);
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 
/** Accept an engine prototype. XXX - it is possible that the top-player
 
 * changes while you are waiting to accept the offer? Then it becomes invalid
 
 * @param tile unused
 
 * @param p1 engine-prototype offered
 
 * @param p2 unused
 
 */
 
int32 CmdWantEnginePreview(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Engine *e;
 

	
 
	if (!IsEngineIndex(p1)) return CMD_ERROR;
 
	e = GetEngine(p1);
 
	if (GetBestPlayer(e->preview_player) != _current_player) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) AcceptEnginePreview(e, _current_player);
 

	
 
	return 0;
 
}
 

	
 
// Determine if an engine type is a wagon (and not a loco)
 
static bool IsWagon(EngineID index)
 
{
 
	return index < NUM_TRAIN_ENGINES && RailVehInfo(index)->flags & RVI_WAGON;
 
}
 

	
 
static void NewVehicleAvailable(Engine *e)
 
{
 
	Vehicle *v;
 
	Player *p;
 
	EngineID index = e - _engines;
 

	
 
	// In case the player didn't build the vehicle during the intro period,
 
	// prevent that player from getting future intro periods for a while.
 
	if (e->flags & ENGINE_INTRODUCING) {
 
		FOR_ALL_PLAYERS(p) {
 
			uint block_preview = p->block_preview;
 

	
 
			if (!HASBIT(e->player_avail, p->index)) continue;
 

	
 
			/* We assume the user did NOT build it.. prove me wrong ;) */
 
			p->block_preview = 20;
 

	
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->type == VEH_Train || v->type == VEH_Road || v->type == VEH_Ship ||
 
						(v->type == VEH_Aircraft && v->subtype <= 2)) {
 
					if (v->owner == p->index && v->engine_type == index) {
 
						/* The user did prove me wrong, so restore old value */
 
						p->block_preview = block_preview;
 
						break;
 
					}
 
				}
 
			}
 
		}
 
	}
 

	
 
	e->flags = (e->flags & ~ENGINE_INTRODUCING) | ENGINE_AVAILABLE;
 
	InvalidateWindowClassesData(WC_BUILD_VEHICLE);
 
	InvalidateWindowClasses(WC_REPLACE_VEHICLE);
 

	
 
	// Now available for all players
 
	e->player_avail = (byte)-1;
 

	
 
	// Do not introduce new rail wagons
 
	if (IsWagon(index)) return;
 

	
 
	// make maglev / monorail available
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active) {
 
			assert(e->railtype < RAILTYPE_END);
 
			SETBIT(p->avail_railtypes, e->railtype);
 
		}
 
	}
 

	
 
	if (index < NUM_TRAIN_ENGINES) {
 
		AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_TRAINAVAIL), 0, 0);
 
	} else if (index < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES) {
 
		AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_ROADAVAIL), 0, 0);
 
	} else if (index < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES) {
 
		AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_SHIPAVAIL), 0, 0);
 
	} else {
 
		AddNewsItem(index, NEWS_FLAGS(NM_CALLBACK, 0, NT_NEW_VEHICLES, DNC_AIRCRAFTAVAIL), 0, 0);
 
	}
 
}
 

	
 
void EnginesMonthlyLoop(void)
 
{
 
	Engine *e;
 

	
 
	if (_cur_year < YEAR_ENGINE_AGING_STOPS) {
 
		for (e = _engines; e != endof(_engines); e++) {
 
			// Age the vehicle
 
			if (e->flags & ENGINE_AVAILABLE && e->age != 0xFFFF) {
 
				e->age++;
 
				CalcEngineReliability(e);
 
			}
 

	
 
			if (!(e->flags & ENGINE_AVAILABLE) && _date >= (e->intro_date + 365)) {
 
				// Introduce it to all players
 
				NewVehicleAvailable(e);
 
			} else if (!(e->flags & (ENGINE_AVAILABLE|ENGINE_INTRODUCING)) && _date >= e->intro_date) {
 
				// Introduction date has passed.. show introducing dialog to one player.
 
				e->flags |= ENGINE_INTRODUCING;
 

	
 
				// Do not introduce new rail wagons
 
				if (!IsWagon(e - _engines))
 
					e->preview_player = 1; // Give to the player with the highest rating.
 
			}
 
		}
 
	}
 
	AdjustAvailAircraft();
 
}
 

	
 
/** Rename an engine.
 
 * @param tile unused
 
 * @param p1 engine ID to rename
 
 * @param p2 unused
 
 */
 
int32 CmdRenameEngine(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	StringID str;
 

	
 
	if (!IsEngineIndex(p1) || _cmd_text[0] == '\0') return CMD_ERROR;
 

	
 
	str = AllocateNameUnique(_cmd_text, 0);
 
	if (str == 0) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		StringID old_str = _engine_name_strings[p1];
 
		_engine_name_strings[p1] = str;
 
		DeleteName(old_str);
 
		_vehicle_design_names |= 3;
 
		MarkWholeScreenDirty();
 
	} else {
 
		DeleteName(str);
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
/*
 
 * returns true if an engine is valid, of the specified type, and buildable by
 
 * the given player, false otherwise
 
 *
 
 * engine = index of the engine to check
 
 * type   = the type the engine should be of (VEH_xxx)
 
 * player = index of the player
 
 */
 
bool IsEngineBuildable(EngineID engine, byte type, PlayerID player)
 
{
 
	const Engine *e;
 

	
 
	// check if it's an engine that is in the engine array
 
	if (!IsEngineIndex(engine)) return false;
 

	
 
	e = GetEngine(engine);
 

	
 
	// check if it's an engine of specified type
 
	if (e->type != type) return false;
 

	
 
	// check if it's available
 
	if (!HASBIT(e->player_avail, player)) return false;
 

	
 
	return true;
 
}
 

	
 
/************************************************************************
 
 * Engine Replacement stuff
 
 ************************************************************************/
 

	
 
static void EngineRenewPoolNewBlock(uint start_item);
 

	
 
DEFINE_OLD_POOL(EngineRenew, EngineRenew, EngineRenewPoolNewBlock, NULL)
 

	
 
static void EngineRenewPoolNewBlock(uint start_item)
 
{
 
	EngineRenew *er;
 

	
 
	/* 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 (er = GetEngineRenew(start_item); er != NULL; er = (er->index + 1U < GetEngineRenewPoolSize()) ? GetEngineRenew(er->index + 1U) : NULL) {
 
		er->index = start_item++;
 
		er->from = INVALID_ENGINE;
 
	}
 
}
 

	
 

	
 
static EngineRenew *AllocateEngineRenew(void)
 
{
 
	EngineRenew *er;
 

	
 
	/* 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 (er = GetEngineRenew(0); er != NULL; er = (er->index + 1U < GetEngineRenewPoolSize()) ? GetEngineRenew(er->index + 1U) : NULL) {
 
		if (IsValidEngineRenew(er)) continue;
 

	
 
		er->to = INVALID_ENGINE;
 
		er->next = NULL;
 
		return er;
 
	}
 

	
 
	/* Check if we can add a block to the pool */
 
	if (AddBlockToPool(&_EngineRenew_pool)) return AllocateEngineRenew();
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 * Retrieves the EngineRenew that specifies the replacement of the given
 
 * engine type from the given renewlist */
 
static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine)
 
{
 
	EngineRenew *er = (EngineRenew *)erl;
 

	
 
	while (er) {
 
		if (er->from == engine) return er;
 
		er = er->next;
 
	}
 
	return NULL;
 
}
 

	
 
void RemoveAllEngineReplacement(EngineRenewList *erl)
 
{
 
	EngineRenew *er = (EngineRenew *)(*erl);
 
	EngineRenew *next;
 

	
 
	while (er) {
 
		next = er->next;
 
		DeleteEngineRenew(er);
 
		er = next;
 
	}
 
	*erl = NULL; // Empty list
 
}
 

	
 
EngineID EngineReplacement(EngineRenewList erl, EngineID engine)
 
{
 
	const EngineRenew *er = GetEngineReplacement(erl, engine);
 
	return er == NULL ? INVALID_ENGINE : er->to;
 
}
 

	
 
int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, uint32 flags)
 
{
 
	EngineRenew *er;
 

	
 
	/* Check if the old vehicle is already in the list */
 
	er = GetEngineReplacement(*erl, old_engine);
 
	if (er != NULL) {
 
		if (flags & DC_EXEC) er->to = new_engine;
 
		return 0;
 
	}
 

	
 
	er = AllocateEngineRenew();
 
	if (er == NULL) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		er->from = old_engine;
 
		er->to = new_engine;
 

	
 
		/* Insert before the first element */
 
		er->next = (EngineRenew *)(*erl);
 
		*erl = (EngineRenewList)er;
 
	}
 

	
 
	return 0;
 
}
 

	
 
int32 RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, uint32 flags)
 
{
 
	EngineRenew *er = (EngineRenew *)(*erl);
 
	EngineRenew *prev = NULL;
 

	
 
	while (er)
 
	{
 
		if (er->from == engine) {
 
			if (flags & DC_EXEC) {
 
				if (prev == NULL) { // First element
 
					/* The second becomes the new first element */
 
					*erl = (EngineRenewList)er->next;
 
				} else {
 
					/* Cut this element out */
 
					prev->next = er->next;
 
				}
 
				DeleteEngineRenew(er);
 
			}
 
			return 0;
 
		}
 
		prev = er;
 
		er = er->next;
 
	}
 

	
 
	return CMD_ERROR;
 
}
 

	
 
static const SaveLoad _engine_renew_desc[] = {
 
	SLE_VAR(EngineRenew, from, SLE_UINT16),
 
	SLE_VAR(EngineRenew, to,   SLE_UINT16),
 

	
 
	SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS),
 

	
 
	SLE_END()
 
};
 

	
 
static void Save_ERNW(void)
 
{
 
	EngineRenew *er;
 

	
 
	FOR_ALL_ENGINE_RENEWS(er) {
 
		SlSetArrayIndex(er->index);
 
		SlObject(er, _engine_renew_desc);
 
	}
 
}
 

	
 
static void Load_ERNW(void)
 
{
 
	int index;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		EngineRenew *er;
 

	
 
		if (!AddBlockIfNeeded(&_EngineRenew_pool, index))
 
			error("EngineRenews: failed loading savegame: too many EngineRenews");
 

	
 
		er = GetEngineRenew(index);
 
		SlObject(er, _engine_renew_desc);
 
	}
 
}
 

	
 
static const SaveLoad _engine_desc[] = {
 
	SLE_CONDVAR(Engine, intro_date,          SLE_FILE_U16 | SLE_VAR_I32,  0,  30),
 
	SLE_CONDVAR(Engine, intro_date,          SLE_INT32,                  31, SL_MAX_VERSION),
 
	SLE_CONDVAR(Engine, age,                 SLE_FILE_U16 | SLE_VAR_I32,  0,  30),
 
	SLE_CONDVAR(Engine, age,                 SLE_INT32,                  31, SL_MAX_VERSION),
 
	    SLE_VAR(Engine, reliability,         SLE_UINT16),
 
	    SLE_VAR(Engine, reliability_spd_dec, SLE_UINT16),
 
	    SLE_VAR(Engine, reliability_start,   SLE_UINT16),
 
	    SLE_VAR(Engine, reliability_max,     SLE_UINT16),
 
	    SLE_VAR(Engine, reliability_final,   SLE_UINT16),
 
	    SLE_VAR(Engine, duration_phase_1,    SLE_UINT16),
 
	    SLE_VAR(Engine, duration_phase_2,    SLE_UINT16),
 
	    SLE_VAR(Engine, duration_phase_3,    SLE_UINT16),
 

	
 
	    SLE_VAR(Engine, lifelength,          SLE_UINT8),
 
	    SLE_VAR(Engine, flags,               SLE_UINT8),
 
	    SLE_VAR(Engine, preview_player,      SLE_UINT8),
 
	    SLE_VAR(Engine, preview_wait,        SLE_UINT8),
 
	    SLE_VAR(Engine, railtype,            SLE_UINT8),
 
	    SLE_VAR(Engine, player_avail,        SLE_UINT8),
 

	
 
	// reserve extra space in savegame here. (currently 16 bytes)
 
	SLE_CONDNULL(16, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static void Save_ENGN(void)
 
{
 
	uint i;
 

	
 
	for (i = 0; i != lengthof(_engines); i++) {
 
		SlSetArrayIndex(i);
 
		SlObject(&_engines[i], _engine_desc);
 
	}
 
}
 

	
 
static void Load_ENGN(void)
 
{
 
	int index;
 
	while ((index = SlIterateArray()) != -1) {
 
		SlObject(GetEngine(index), _engine_desc);
 
	}
 
}
 

	
 
static void LoadSave_ENGS(void)
 
{
 
	SlArray(_engine_name_strings, lengthof(_engine_name_strings), SLE_STRINGID);
 
}
 

	
 
const ChunkHandler _engine_chunk_handlers[] = {
 
	{ 'ENGN', Save_ENGN,     Load_ENGN,     CH_ARRAY          },
 
	{ 'ENGS', LoadSave_ENGS, LoadSave_ENGS, CH_RIFF           },
 
	{ 'ERNW', Save_ERNW,     Load_ERNW,     CH_ARRAY | CH_LAST},
 
};
 

	
 
void InitializeEngines(void)
 
{
 
	/* Clean the engine renew pool and create 1 block in it */
 
	CleanPool(&_EngineRenew_pool);
 
	AddBlockToPool(&_EngineRenew_pool);
 
}
src/engine_gui.c
Show inline comments
 
deleted file
src/engine_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "functions.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "engine.h"
 
#include "command.h"
 
#include "news.h"
 
#include "variables.h"
 
#include "newgrf_engine.h"
 

	
 

	
 
static StringID GetEngineCategoryName(EngineID engine)
 
{
 
	if (engine < NUM_TRAIN_ENGINES) {
 
		switch (GetEngine(engine)->railtype) {
 
			case RAILTYPE_RAIL:     return STR_8102_RAILROAD_LOCOMOTIVE;
 
			case RAILTYPE_ELECTRIC: return STR_8102_RAILROAD_LOCOMOTIVE;
 
			case RAILTYPE_MONO:     return STR_8106_MONORAIL_LOCOMOTIVE;
 
			case RAILTYPE_MAGLEV:   return STR_8107_MAGLEV_LOCOMOTIVE;
 
		}
 
	}
 

	
 
	if (engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES)
 
		return STR_8103_ROAD_VEHICLE;
 

	
 
	if (engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES)
 
		return STR_8105_SHIP;
 

	
 
	return STR_8104_AIRCRAFT;
 
}
 

	
 
static const Widget _engine_preview_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     5,     0,    10,     0,    13, STR_00C5,                                  STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     5,    11,   299,     0,    13, STR_8100_MESSAGE_FROM_VEHICLE_MANUFACTURE, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     5,     0,   299,    14,   191, 0x0,                                       STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,     5,    85,   144,   172,   183, STR_00C9_NO,                               STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,     5,   155,   214,   172,   183, STR_00C8_YES,                              STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
typedef void DrawEngineProc(int x, int y, EngineID engine, uint32 image_ormod);
 
typedef void DrawEngineInfoProc(EngineID, int x, int y, int maxw);
 

	
 
typedef struct DrawEngineInfo {
 
	DrawEngineProc *engine_proc;
 
	DrawEngineInfoProc *info_proc;
 
} DrawEngineInfo;
 

	
 
static void DrawTrainEngineInfo(EngineID engine, int x, int y, int maxw);
 
static void DrawRoadVehEngineInfo(EngineID engine, int x, int y, int maxw);
 
static void DrawShipEngineInfo(EngineID engine, int x, int y, int maxw);
 
static void DrawAircraftEngineInfo(EngineID engine, int x, int y, int maxw);
 

	
 
static const DrawEngineInfo _draw_engine_list[4] = {
 
	{DrawTrainEngine,DrawTrainEngineInfo},
 
	{DrawRoadVehEngine,DrawRoadVehEngineInfo},
 
	{DrawShipEngine,DrawShipEngineInfo},
 
	{DrawAircraftEngine,DrawAircraftEngineInfo},
 
};
 

	
 
static void EnginePreviewWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		EngineID engine = w->window_number;
 
		const DrawEngineInfo* dei;
 
		int width;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		SetDParam(0, GetEngineCategoryName(engine));
 
		DrawStringMultiCenter(150, 44, STR_8101_WE_HAVE_JUST_DESIGNED_A, 296);
 

	
 
		DrawStringCentered(w->width >> 1, 80, GetCustomEngineName(engine), 0x10);
 

	
 
		(dei = _draw_engine_list,engine < NUM_TRAIN_ENGINES) ||
 
		(dei++,engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES) ||
 
		(dei++,engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES) ||
 
		(dei++, true);
 

	
 
		width = w->width;
 
		dei->engine_proc(width >> 1, 100, engine, 0);
 
		dei->info_proc(engine, width >> 1, 130, width - 52);
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
			case 4:
 
				DoCommandP(0, w->window_number, 0, NULL, CMD_WANT_ENGINE_PREVIEW);
 
				/* Fallthrough */
 
			case 3:
 
				DeleteWindow(w);
 
				break;
 
		}
 
		break;
 
	}
 
}
 

	
 
static const WindowDesc _engine_preview_desc = {
 
	WDP_CENTER, WDP_CENTER, 300, 192,
 
	WC_ENGINE_PREVIEW,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_engine_preview_widgets,
 
	EnginePreviewWndProc
 
};
 

	
 

	
 
void ShowEnginePreviewWindow(EngineID engine)
 
{
 
	AllocateWindowDescFront(&_engine_preview_desc, engine);
 
}
 

	
 
static void DrawTrainEngineInfo(EngineID engine, int x, int y, int maxw)
 
{
 
	const RailVehicleInfo *rvi = RailVehInfo(engine);
 
	uint multihead = (rvi->flags & RVI_MULTIHEAD) ? 1 : 0;
 

	
 
	SetDParam(0, (_price.build_railvehicle >> 3) * rvi->base_cost >> 5);
 
	SetDParam(2, rvi->max_speed);
 
	SetDParam(3, rvi->power << multihead);
 
	SetDParam(1, rvi->weight << multihead);
 

	
 
	SetDParam(4, rvi->running_cost_base * _price.running_rail[rvi->running_cost_class] >> 8 << multihead);
 

	
 
	if (rvi->capacity != 0) {
 
		SetDParam(5, rvi->cargo_type);
 
		SetDParam(6, rvi->capacity << multihead);
 
	} else {
 
		SetDParam(5, CT_INVALID);
 
	}
 
	DrawStringMultiCenter(x, y, STR_VEHICLE_INFO_COST_WEIGHT_SPEED_POWER, maxw);
 
}
 

	
 
void DrawNewsNewTrainAvail(Window *w)
 
{
 
	EngineID engine;
 

	
 
	DrawNewsBorder(w);
 

	
 
	engine = WP(w,news_d).ni->string_id;
 
	SetDParam(0, GetEngineCategoryName(engine));
 
	DrawStringMultiCenter(w->width >> 1, 20, STR_8859_NEW_NOW_AVAILABLE, w->width - 2);
 

	
 
	GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
 

	
 
	SetDParam(0, GetCustomEngineName(engine));
 
	DrawStringMultiCenter(w->width >> 1, 57, STR_885A, w->width - 2);
 

	
 
	DrawTrainEngine(w->width >> 1, 88, engine, 0);
 
	GfxFillRect(25, 56, w->width - 56, 112, 0x323 | USE_COLORTABLE);
 
	DrawTrainEngineInfo(engine, w->width >> 1, 129, w->width - 52);
 
}
 

	
 
StringID GetNewsStringNewTrainAvail(const NewsItem *ni)
 
{
 
	EngineID engine = ni->string_id;
 
	SetDParam(0, STR_8859_NEW_NOW_AVAILABLE);
 
	SetDParam(1, GetEngineCategoryName(engine));
 
	SetDParam(2, GetCustomEngineName(engine));
 
	return STR_02B6;
 
}
 

	
 
static void DrawAircraftEngineInfo(EngineID engine, int x, int y, int maxw)
 
{
 
	const AircraftVehicleInfo *avi = AircraftVehInfo(engine);
 
	SetDParam(0, (_price.aircraft_base >> 3) * avi->base_cost >> 5);
 
	SetDParam(1, avi->max_speed * 128 / 10);
 
	SetDParam(2, avi->passenger_capacity);
 
	SetDParam(3, avi->mail_capacity);
 
	SetDParam(4, avi->running_cost * _price.aircraft_running >> 8);
 

	
 
	DrawStringMultiCenter(x, y, STR_A02E_COST_MAX_SPEED_CAPACITY, maxw);
 
}
 

	
 
void DrawNewsNewAircraftAvail(Window *w)
 
{
 
	EngineID engine;
 

	
 
	DrawNewsBorder(w);
 

	
 
	engine = WP(w,news_d).ni->string_id;
 

	
 
	DrawStringMultiCenter(w->width >> 1, 20, STR_A02C_NEW_AIRCRAFT_NOW_AVAILABLE, w->width - 2);
 
	GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
 

	
 
	SetDParam(0, GetCustomEngineName(engine));
 
	DrawStringMultiCenter(w->width >> 1, 57, STR_A02D, w->width - 2);
 

	
 
	DrawAircraftEngine(w->width >> 1, 93, engine, 0);
 
	GfxFillRect(25, 56, w->width - 56, 110, 0x323 | USE_COLORTABLE);
 
	DrawAircraftEngineInfo(engine, w->width >> 1, 131, w->width - 52);
 
}
 

	
 
StringID GetNewsStringNewAircraftAvail(const NewsItem *ni)
 
{
 
	EngineID engine = ni->string_id;
 
	SetDParam(0, STR_A02C_NEW_AIRCRAFT_NOW_AVAILABLE);
 
	SetDParam(1, GetCustomEngineName(engine));
 
	return STR_02B6;
 
}
 

	
 
static void DrawRoadVehEngineInfo(EngineID engine, int x, int y, int maxw)
 
{
 
	const RoadVehicleInfo *rvi = RoadVehInfo(engine);
 

	
 
	SetDParam(0, (_price.roadveh_base >> 3) * rvi->base_cost >> 5);
 
	SetDParam(1, rvi->max_speed / 2);
 
	SetDParam(2, rvi->running_cost * _price.roadveh_running >> 8);
 
	SetDParam(3, rvi->cargo_type);
 
	SetDParam(4, rvi->capacity);
 

	
 
	DrawStringMultiCenter(x, y, STR_902A_COST_SPEED_RUNNING_COST, maxw);
 
}
 

	
 
void DrawNewsNewRoadVehAvail(Window *w)
 
{
 
	EngineID engine;
 

	
 
	DrawNewsBorder(w);
 

	
 
	engine = WP(w,news_d).ni->string_id;
 
	DrawStringMultiCenter(w->width >> 1, 20, STR_9028_NEW_ROAD_VEHICLE_NOW_AVAILABLE, w->width - 2);
 
	GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
 

	
 
	SetDParam(0, GetCustomEngineName(engine));
 
	DrawStringMultiCenter(w->width >> 1, 57, STR_9029, w->width - 2);
 

	
 
	DrawRoadVehEngine(w->width >> 1, 88, engine, 0);
 
	GfxFillRect(25, 56, w->width - 56, 112, 0x323 | USE_COLORTABLE);
 
	DrawRoadVehEngineInfo(engine, w->width >> 1, 129, w->width - 52);
 
}
 

	
 
StringID GetNewsStringNewRoadVehAvail(const NewsItem *ni)
 
{
 
	EngineID engine = ni->string_id;
 
	SetDParam(0, STR_9028_NEW_ROAD_VEHICLE_NOW_AVAILABLE);
 
	SetDParam(1, GetCustomEngineName(engine));
 
	return STR_02B6;
 
}
 

	
 
static void DrawShipEngineInfo(EngineID engine, int x, int y, int maxw)
 
{
 
	const ShipVehicleInfo *svi = ShipVehInfo(engine);
 
	SetDParam(0, svi->base_cost * (_price.ship_base >> 3) >> 5);
 
	SetDParam(1, svi->max_speed / 2);
 
	SetDParam(2, svi->cargo_type);
 
	SetDParam(3, svi->capacity);
 
	SetDParam(4, svi->running_cost * _price.ship_running >> 8);
 
	DrawStringMultiCenter(x, y, STR_982E_COST_MAX_SPEED_CAPACITY, maxw);
 
}
 

	
 
void DrawNewsNewShipAvail(Window *w)
 
{
 
	EngineID engine;
 

	
 
	DrawNewsBorder(w);
 

	
 
	engine = WP(w,news_d).ni->string_id;
 

	
 
	DrawStringMultiCenter(w->width >> 1, 20, STR_982C_NEW_SHIP_NOW_AVAILABLE, w->width - 2);
 
	GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
 

	
 
	SetDParam(0, GetCustomEngineName(engine));
 
	DrawStringMultiCenter(w->width >> 1, 57, STR_982D, w->width - 2);
 

	
 
	DrawShipEngine(w->width >> 1, 93, engine, 0);
 
	GfxFillRect(25, 56, w->width - 56, 110, 0x323 | USE_COLORTABLE);
 
	DrawShipEngineInfo(engine, w->width >> 1, 131, w->width - 52);
 
}
 

	
 
StringID GetNewsStringNewShipAvail(const NewsItem *ni)
 
{
 
	EngineID engine = ni->string_id;
 
	SetDParam(0, STR_982C_NEW_SHIP_NOW_AVAILABLE);
 
	SetDParam(1, GetCustomEngineName(engine));
 
	return STR_02B6;
 
}
src/fileio.c
Show inline comments
 
deleted file
src/fileio.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "fileio.h"
 
#include "functions.h"
 
#include "string.h"
 
#include "macros.h"
 
#include "variables.h"
 

	
 
/*************************************************/
 
/* FILE IO ROUTINES ******************************/
 
/*************************************************/
 

	
 
#define FIO_BUFFER_SIZE 512
 

	
 
typedef struct {
 
	byte *buffer, *buffer_end;          ///< position pointer in local buffer and last valid byte of buffer
 
	uint32 pos;                         ///< current (system) position in file
 
	FILE *cur_fh;                       ///< current file handle
 
	FILE *handles[64];                  ///< array of file handles we can have open
 
	byte buffer_start[FIO_BUFFER_SIZE]; ///< local buffer when read from file
 
} Fio;
 

	
 
static Fio _fio;
 

	
 
// Get current position in file
 
uint32 FioGetPos(void)
 
{
 
	return _fio.pos + (_fio.buffer - _fio.buffer_start) - FIO_BUFFER_SIZE;
 
}
 

	
 
void FioSeekTo(uint32 pos, int mode)
 
{
 
	if (mode == SEEK_CUR) pos += FioGetPos();
 
	_fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
 
	_fio.pos = pos;
 
	fseek(_fio.cur_fh, _fio.pos, SEEK_SET);
 
}
 

	
 
// Seek to a file and a position
 
void FioSeekToFile(uint32 pos)
 
{
 
	FILE *f = _fio.handles[pos >> 24];
 
	assert(f != NULL);
 
	_fio.cur_fh = f;
 
	FioSeekTo(GB(pos, 0, 24), SEEK_SET);
 
}
 

	
 
byte FioReadByte(void)
 
{
 
	if (_fio.buffer == _fio.buffer_end) {
 
		_fio.pos += FIO_BUFFER_SIZE;
 
		fread(_fio.buffer = _fio.buffer_start, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
 
	}
 
	return *_fio.buffer++;
 
}
 

	
 
void FioSkipBytes(int n)
 
{
 
	for (;;) {
 
		int m = min(_fio.buffer_end - _fio.buffer, n);
 
		_fio.buffer += m;
 
		n -= m;
 
		if (n == 0) break;
 
		FioReadByte();
 
		n--;
 
	}
 
}
 

	
 
uint16 FioReadWord(void)
 
{
 
	byte b = FioReadByte();
 
	return (FioReadByte() << 8) | b;
 
}
 

	
 
uint32 FioReadDword(void)
 
{
 
	uint b = FioReadWord();
 
	return (FioReadWord() << 16) | b;
 
}
 

	
 
void FioReadBlock(void *ptr, uint size)
 
{
 
	FioSeekTo(FioGetPos(), SEEK_SET);
 
	_fio.pos += size;
 
	fread(ptr, 1, size, _fio.cur_fh);
 
}
 

	
 
static inline void FioCloseFile(int slot)
 
{
 
	if (_fio.handles[slot] != NULL) {
 
		fclose(_fio.handles[slot]);
 
		_fio.handles[slot] = NULL;
 
	}
 
}
 

	
 
void FioCloseAll(void)
 
{
 
	int i;
 

	
 
	for (i = 0; i != lengthof(_fio.handles); i++)
 
		FioCloseFile(i);
 
}
 

	
 
bool FioCheckFileExists(const char *filename)
 
{
 
	FILE *f = FioFOpenFile(filename);
 
	if (f == NULL) return false;
 

	
 
	fclose(f);
 
	return true;
 
}
 

	
 
FILE *FioFOpenFile(const char *filename)
 
{
 
	FILE *f;
 
	char buf[MAX_PATH];
 

	
 
	snprintf(buf, lengthof(buf), "%s%s", _paths.data_dir, filename);
 

	
 
	f = fopen(buf, "rb");
 
#if !defined(WIN32)
 
	if (f == NULL) {
 
		strtolower(buf + strlen(_paths.data_dir) - 1);
 
		f = fopen(buf, "rb");
 

	
 
#if defined SECOND_DATA_DIR
 
		// tries in the 2nd data directory
 
		if (f == NULL) {
 
			snprintf(buf, lengthof(buf), "%s%s", _paths.second_data_dir, filename);
 
			strtolower(buf + strlen(_paths.second_data_dir) - 1);
 
			f = fopen(buf, "rb");
 
		}
 
#endif
 
	}
 
#endif
 

	
 
	return f;
 
}
 

	
 
void FioOpenFile(int slot, const char *filename)
 
{
 
	FILE *f = FioFOpenFile(filename);
 

	
 
	if (f == NULL) error("Cannot open file '%s%s'", _paths.data_dir, filename);
 

	
 
	FioCloseFile(slot); // if file was opened before, close it
 
	_fio.handles[slot] = f;
 
	FioSeekToFile(slot << 24);
 
}
src/fios.c
Show inline comments
 
deleted file
src/fios.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/** @file fios.c
 
 * This file contains functions for building file lists for the save/load dialogs.
 
 */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "hal.h"
 
#include "string.h"
 
#include "variables.h"
 
#include "functions.h"
 
#include "heightmap.h"
 
#include "table/strings.h"
 
#include "fios.h"
 
#include <sys/types.h>
 
#include <sys/stat.h>
 

	
 
#ifdef WIN32
 
# include <io.h>
 
#else
 
# include <unistd.h>
 
# include <dirent.h>
 
#endif /* WIN32 */
 

	
 
/* Variables to display file lists */
 
int _fios_num;
 

	
 
static char *_fios_path;
 
static FiosItem *_fios_items;
 
static int _fios_count, _fios_alloc;
 

	
 
/* OS-specific functions are taken from their respective files (win32/unix/os2 .c) */
 
extern bool FiosIsRoot(const char *path);
 
extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
 
extern void FiosGetDrives(void);
 
extern bool FiosGetDiskFreeSpace(const char *path, uint32 *tot);
 

	
 
/* get the name of an oldstyle savegame */
 
extern void GetOldSaveGameName(char *title, const char *path, const char *file);
 

	
 
/**
 
 * Allocate a new FiosItem.
 
 * @return A pointer to the newly allocated FiosItem.
 
 */
 
FiosItem *FiosAlloc(void)
 
{
 
	if (_fios_count == _fios_alloc) {
 
		_fios_alloc += 256;
 
		_fios_items = realloc(_fios_items, _fios_alloc * sizeof(FiosItem));
 
	}
 
	return &_fios_items[_fios_count++];
 
}
 

	
 
/**
 
 * Compare two FiosItem's. Used with qsort when sorting the file list.
 
 * @param a A pointer to the first FiosItem to compare.
 
 * @param a A pointer to the second FiosItem to compare.
 
 * @return -1, 0 or 1, depending on how the two items should be sorted.
 
 */
 
int CDECL compare_FiosItems(const void *a, const void *b)
 
{
 
	const FiosItem *da = (const FiosItem *)a;
 
	const FiosItem *db = (const FiosItem *)b;
 
	int r;
 

	
 
	if (_savegame_sort_order & SORT_BY_NAME) {
 
		r = strcasecmp(da->title, db->title);
 
	} else {
 
		r = da->mtime < db->mtime ? -1 : 1;
 
	}
 

	
 
	if (_savegame_sort_order & SORT_DESCENDING) r = -r;
 
	return r;
 
}
 

	
 
/**
 
 * Free the list of savegames
 
 */
 
void FiosFreeSavegameList(void)
 
{
 
	free(_fios_items);
 
	_fios_items = NULL;
 
	_fios_alloc = _fios_count = 0;
 
}
 

	
 
/**
 
 * Get descriptive texts. Returns the path and free space
 
 * left on the device
 
 * @param path string describing the path
 
 * @param total_free total free space in megabytes, optional (can be NULL)
 
 * @return StringID describing the path (free space or failure)
 
 */
 
StringID FiosGetDescText(const char **path, uint32 *total_free)
 
{
 
	*path = _fios_path;
 
	return FiosGetDiskFreeSpace(*path, total_free) ? STR_4005_BYTES_FREE : STR_4006_UNABLE_TO_READ_DRIVE;
 
}
 

	
 
/* Browse to a new path based on the passed FiosItem struct
 
 * @param *item FiosItem object telling us what to do
 
 * @return a string if we have given a file as a target, otherwise NULL */
 
char *FiosBrowseTo(const FiosItem *item)
 
{
 
	char *s;
 
	char *path = _fios_path;
 

	
 
	switch (item->type) {
 
#if defined(WIN32) || defined(__OS2__)
 
	case FIOS_TYPE_DRIVE: sprintf(path, "%c:" PATHSEP, item->title[0]); break;
 
#endif
 

	
 
	case FIOS_TYPE_PARENT:
 
		/* Check for possible NULL ptr (not required for UNIXes, but AmigaOS-alikes) */
 
		if ((s = strrchr(path, PATHSEPCHAR)) != NULL) {
 
			s[1] = '\0'; // go up a directory
 
			if (!FiosIsRoot(path)) s[0] = '\0';
 
		}
 
#if defined(__MORPHOS__) || defined(__AMIGAOS__)
 
		/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
 
		else if ((s = strrchr(path, ':')) != NULL) s[1] = '\0';
 
#endif
 
		break;
 

	
 
	case FIOS_TYPE_DIR:
 
		if (!FiosIsRoot(path)) strcat(path, PATHSEP);
 
		strcat(path, item->name);
 
		break;
 

	
 
	case FIOS_TYPE_DIRECT:
 
		sprintf(path, "%s" PATHSEP, item->name);
 
		s = strrchr(path, PATHSEPCHAR);
 
		if (s != NULL && s[1] == '\0') s[0] = '\0'; // strip trailing slash
 
		break;
 

	
 
	case FIOS_TYPE_FILE:
 
	case FIOS_TYPE_OLDFILE:
 
	case FIOS_TYPE_SCENARIO:
 
	case FIOS_TYPE_OLD_SCENARIO:
 
	case FIOS_TYPE_PNG:
 
	case FIOS_TYPE_BMP:
 
	{
 
		static char str_buffr[512];
 

	
 
#if defined(__MORPHOS__) || defined(__AMIGAOS__)
 
		/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
 
		if (FiosIsRoot(path)) {
 
			snprintf(str_buffr, lengthof(str_buffr), "%s:%s", path, item->name);
 
		} else // XXX - only next line!
 
#endif
 
		snprintf(str_buffr, lengthof(str_buffr), "%s" PATHSEP "%s", path, item->name);
 

	
 
		return str_buffr;
 
	}
 
	}
 

	
 
	return NULL;
 
}
 

	
 
void FiosMakeSavegameName(char *buf, const char *name, size_t size)
 
{
 
	const char *extension, *period;
 

	
 
	extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
 

	
 
	/* Don't append the extension if it is already there */
 
	period = strrchr(name, '.');
 
	if (period != NULL && strcasecmp(period, extension) == 0) extension = "";
 

	
 
	snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
 
}
 

	
 
#if defined(WIN32) || defined(WIN64)
 
# define unlink _wunlink
 
#endif
 

	
 
bool FiosDelete(const char *name)
 
{
 
	char filename[512];
 

	
 
	FiosMakeSavegameName(filename, name, lengthof(filename));
 
	return unlink(OTTD2FS(filename)) == 0;
 
}
 

	
 
bool FileExists(const char *filename)
 
{
 
	return access(filename, 0) == 0;
 
}
 

	
 
typedef byte fios_getlist_callback_proc(int mode, const char *filename, const char *ext, char *title);
 

	
 
/** Create a list of the files in a directory, according to some arbitrary rule.
 
 *  @param num Will be filled with the amount of items.
 
 *  @param mode The mode we are in. Some modes don't allow 'parent'.
 
 *  @param callback The function that is called where you need to do the filtering.
 
 *  @return Return the list of files. */
 
static FiosItem *FiosGetFileList(int mode, fios_getlist_callback_proc *callback_proc)
 
{
 
	struct stat sb;
 
	struct dirent *dirent;
 
	DIR *dir;
 
	FiosItem *fios;
 
	int sort_start;
 

	
 
	/* A parent directory link exists if we are not in the root directory */
 
	if (!FiosIsRoot(_fios_path) && mode != SLD_NEW_GAME) {
 
		fios = FiosAlloc();
 
		fios->type = FIOS_TYPE_PARENT;
 
		fios->mtime = 0;
 
		ttd_strlcpy(fios->name, "..", lengthof(fios->name));
 
		ttd_strlcpy(fios->title, ".. (Parent directory)", lengthof(fios->title));
 
	}
 

	
 
	/* Show subdirectories */
 
	if (mode != SLD_NEW_GAME && (dir = opendir(_fios_path)) != NULL) {
 
		while ((dirent = readdir(dir)) != NULL) {
 
			const char *d_name = FS2OTTD(dirent->d_name);
 

	
 
			/* found file must be directory, but not '.' or '..' */
 
			if (FiosIsValidFile(_fios_path, dirent, &sb) && (sb.st_mode & S_IFDIR) &&
 
				strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
 
				fios = FiosAlloc();
 
				fios->type = FIOS_TYPE_DIR;
 
				fios->mtime = 0;
 
				ttd_strlcpy(fios->name, d_name, lengthof(fios->name));
 
				snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name);
 
				str_validate(fios->title);
 
			}
 
		}
 
		closedir(dir);
 
	}
 

	
 
	/* Sort the subdirs always by name, ascending, remember user-sorting order */
 
	{
 
		byte order = _savegame_sort_order;
 
		_savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
 
		qsort(_fios_items, _fios_count, sizeof(FiosItem), compare_FiosItems);
 
		_savegame_sort_order = order;
 
	}
 

	
 
	/* This is where to start sorting for the filenames */
 
	sort_start = _fios_count;
 

	
 
	/* Show files */
 
	dir = opendir(_fios_path);
 
	if (dir != NULL) {
 
		while ((dirent = readdir(dir)) != NULL) {
 
			char fios_title[64];
 
			char *t;
 
			char *d_name = (char*)FS2OTTD(dirent->d_name);
 
			byte type;
 

	
 
			if (!FiosIsValidFile(_fios_path, dirent, &sb) || !(sb.st_mode & S_IFREG)) continue;
 

	
 
			/* File has no extension, skip it */
 
			if ((t = strrchr(d_name, '.')) == NULL) continue;
 
			fios_title[0] = '\0'; // reset the title;
 

	
 
			type = callback_proc(mode, d_name, t, fios_title);
 
			if (type != FIOS_TYPE_INVALID) {
 
				fios = FiosAlloc();
 
				fios->mtime = sb.st_mtime;
 
				fios->type = type;
 
				ttd_strlcpy(fios->name, d_name, lengthof(fios->name));
 

	
 
				/* Some callbacks want to lookup the title of the file. Allow that.
 
				 * If we just copy the title from the filename, strip the extension */
 
				t = (fios_title[0] == '\0') ? *t = '\0', d_name : fios_title;
 
				ttd_strlcpy(fios->title, t, lengthof(fios->title));
 
				str_validate(fios->title);
 
			}
 
		}
 
		closedir(dir);
 
	}
 

	
 
	qsort(_fios_items + sort_start, _fios_count - sort_start, sizeof(FiosItem), compare_FiosItems);
 

	
 
	/* Show drives */
 
	if (mode != SLD_NEW_GAME) FiosGetDrives();
 

	
 
	_fios_num = _fios_count;
 
	return _fios_items;
 
}
 

	
 
/**
 
 * Callback for FiosGetFileList. It tells if a file is a savegame or not.
 
 * @param mode Save/load mode.
 
 * @param file Name of the file to check.
 
 * @param ext A pointer to the extension identifier inside file
 
 * @param title Buffer if a callback wants to lookup the title of the file
 
 * @return a FIOS_TYPE_* type of the found file, FIOS_TYPE_INVALID if not a savegame
 
 * @see FiosGetFileList
 
 * @see FiosGetSavegameList
 
 */
 
static byte FiosGetSavegameListCallback(int mode, const char *file, const char *ext, char *title)
 
{
 
	/* Show savegame files
 
	 * .SAV OpenTTD saved game
 
	 * .SS1 Transport Tycoon Deluxe preset game
 
	 * .SV1 Transport Tycoon Deluxe (Patch) saved game
 
	 * .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game */
 
	if (strcasecmp(ext, ".sav") == 0) return FIOS_TYPE_FILE;
 

	
 
	if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
 
		if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
 
				strcasecmp(ext, ".sv2") == 0) {
 
			GetOldSaveGameName(title, _fios_path, file);
 
			return FIOS_TYPE_OLDFILE;
 
		}
 
	}
 

	
 
	return FIOS_TYPE_INVALID;
 
}
 

	
 
/**
 
 * Get a list of savegames.
 
 * @param mode Save/load mode.
 
 * @return A pointer to an array of FiosItem representing all the files to be shown in the save/load dialog.
 
 * @see FiosGetFileList
 
 */
 
FiosItem *FiosGetSavegameList(int mode)
 
{
 
	static char *_fios_save_path = NULL;
 

	
 
	if (_fios_save_path == NULL) {
 
		_fios_save_path = malloc(MAX_PATH);
 
		ttd_strlcpy(_fios_save_path, _paths.save_dir, MAX_PATH);
 
	}
 

	
 
	_fios_path = _fios_save_path;
 

	
 
	return FiosGetFileList(mode, &FiosGetSavegameListCallback);
 
}
 

	
 
/**
 
 * Callback for FiosGetFileList. It tells if a file is a scenario or not.
 
 * @param mode Save/load mode.
 
 * @param file Name of the file to check.
 
 * @param ext A pointer to the extension identifier inside file
 
 * @param title Buffer if a callback wants to lookup the title of the file
 
 * @return a FIOS_TYPE_* type of the found file, FIOS_TYPE_INVALID if not a scenario
 
 * @see FiosGetFileList
 
 * @see FiosGetScenarioList
 
 */
 
static byte FiosGetScenarioListCallback(int mode, const char *file, const char *ext, char *title)
 
{
 
	/* Show scenario files
 
	 * .SCN OpenTTD style scenario file
 
	 * .SV0 Transport Tycoon Deluxe (Patch) scenario
 
	 * .SS0 Transport Tycoon Deluxe preset scenario */
 
	if (strcasecmp(ext, ".scn") == 0) return FIOS_TYPE_SCENARIO;
 

	
 
	if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO || mode == SLD_NEW_GAME) {
 
		if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
 
			GetOldSaveGameName(title, _fios_path, file);
 
			return FIOS_TYPE_OLD_SCENARIO;
 
		}
 
	}
 

	
 
	return FIOS_TYPE_INVALID;
 
}
 

	
 
/**
 
 * Get a list of scenarios.
 
 * @param mode Save/load mode.
 
 * @return A pointer to an array of FiosItem representing all the files to be shown in the save/load dialog.
 
 * @see FiosGetFileList
 
 */
 
FiosItem *FiosGetScenarioList(int mode)
 
{
 
	static char *_fios_scn_path = NULL;
 

	
 
	if (_fios_scn_path == NULL) {
 
		_fios_scn_path = malloc(MAX_PATH);
 
		ttd_strlcpy(_fios_scn_path, _paths.scenario_dir, MAX_PATH);
 
	}
 

	
 
	_fios_path = _fios_scn_path;
 

	
 
	return FiosGetFileList(mode, &FiosGetScenarioListCallback);
 
}
 

	
 
static byte FiosGetHeightmapListCallback(int mode, const char *file, const char *ext, char *title)
 
{
 
	/* Show heightmap files
 
	 * .PNG PNG Based heightmap files
 
	 * .BMP BMP Based heightmap files
 
	 */
 

	
 
#ifdef WITH_PNG
 
	if (strcasecmp(ext, ".png") == 0) return FIOS_TYPE_PNG;
 
#endif /* WITH_PNG */
 

	
 
	if (strcasecmp(ext, ".bmp") == 0) return FIOS_TYPE_BMP;
 

	
 
	return FIOS_TYPE_INVALID;
 
}
 

	
 
// Get a list of Heightmaps
 
FiosItem *FiosGetHeightmapList(int mode)
 
{
 
	static char *_fios_hmap_path = NULL;
 

	
 
	if (_fios_hmap_path == NULL) {
 
		_fios_hmap_path = malloc(MAX_PATH);
 
		strcpy(_fios_hmap_path, _paths.heightmap_dir);
 
	}
 

	
 
	_fios_path = _fios_hmap_path;
 

	
 
	return FiosGetFileList(mode, &FiosGetHeightmapListCallback);
 
}
src/fontcache.c
Show inline comments
 
deleted file
src/fontcache.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "macros.h"
 
#include "debug.h"
 
#include "table/sprites.h"
 
#include "table/control_codes.h"
 
#include "spritecache.h"
 
#include "gfx.h"
 
#include "string.h"
 
#include "fontcache.h"
 

	
 
#ifdef WITH_FREETYPE
 

	
 
#include <ft2build.h>
 
#include FT_FREETYPE_H
 
#include FT_GLYPH_H
 

	
 
#ifdef WITH_FONTCONFIG
 
#include <fontconfig/fontconfig.h>
 
#endif
 

	
 
static FT_Library _library = NULL;
 
static FT_Face _face_small = NULL;
 
static FT_Face _face_medium = NULL;
 
static FT_Face _face_large = NULL;
 

	
 
FreeTypeSettings _freetype;
 

	
 
enum {
 
	FACE_COLOUR = 1,
 
	SHADOW_COLOUR = 2,
 
};
 

	
 
/** Get the font loaded into a Freetype face by using a font-name.
 
 * If no appropiate font is found, the function returns an error */
 
#ifdef WIN32
 
#include <windows.h>
 
#include <tchar.h>
 
#include <shlobj.h> // SHGetFolderPath
 
#include "win32.h"
 

	
 
/* Get the font file to be loaded into Freetype by looping the registry
 
 * location where windows lists all installed fonts. Not very nice, will
 
 * surely break if the registry path changes, but it works. Much better
 
 * solution would be to use CreateFont, and extract the font data from it
 
 * by GetFontData. The problem with this is that the font file needs to be
 
 * kept in memory then until the font is no longer needed. This could mean
 
 * an additional memory usage of 30MB (just for fonts!) when using an eastern
 
 * font for all font sizes */
 
#define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
 
#define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
 
static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
 
{
 
	FT_Error err = FT_Err_Cannot_Open_Resource;
 
	HKEY hKey;
 
	LONG ret;
 
	TCHAR vbuffer[MAX_PATH], dbuffer[256];
 
	TCHAR *font_namep;
 
	char *font_path;
 
	uint index;
 

	
 
	/* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
 
	 * "Windows NT" key, on Windows 9x in the Windows key. To save us having
 
	 * to retrieve the windows version, we'll just query both */
 
	ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
 
	if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
 

	
 
	if (ret != ERROR_SUCCESS) {
 
		DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
 
		return err;
 
	}
 

	
 
	/* For Unicode we need some conversion between widechar and
 
	 * normal char to match the data returned by RegEnumValue,
 
	 * otherwise just use parameter */
 
#if defined(UNICODE)
 
	font_namep = malloc(MAX_PATH * sizeof(TCHAR));
 
	MB_TO_WIDE_BUFFER(font_name, font_namep, MAX_PATH * sizeof(TCHAR));
 
#else
 
	font_namep = (char*)font_name; // only cast because in unicode pointer is not const
 
#endif
 

	
 
	for (index = 0;; index++) {
 
		TCHAR *s;
 
		DWORD vbuflen = lengthof(vbuffer);
 
		DWORD dbuflen = lengthof(dbuffer);
 

	
 
		ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
 
		if (ret != ERROR_SUCCESS) goto registry_no_font_found;
 

	
 
		/* The font names in the registry are of the following 3 forms:
 
		 * - ADMUI3.fon
 
		 * - Book Antiqua Bold (TrueType)
 
		 * - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
 
		 * We will strip the font-type '()' if any and work with the font name
 
		 * itself, which must match exactly; if...
 
		 * TTC files, font files which contain more than one font are seperated
 
		 * byt '&'. Our best bet will be to do substr match for the fontname
 
		 * and then let FreeType figure out which index to load */
 
		s = _tcschr(vbuffer, _T('('));
 
		if (s != NULL) s[-1] = '\0';
 

	
 
		if (_tcschr(vbuffer, _T('&')) == NULL) {
 
			if (_tcsicmp(vbuffer, font_namep) == 0) break;
 
		} else {
 
			if (_tcsstr(vbuffer, font_namep) != NULL) break;
 
		}
 
	}
 

	
 
	if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
 
		DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
 
		goto folder_error;
 
	}
 

	
 
	/* Some fonts are contained in .ttc files, TrueType Collection fonts. These
 
	 * contain multiple fonts inside this single file. GetFontData however
 
	 * returns the whole file, so we need to check each font inside to get the
 
	 * proper font.
 
	 * Also note that FreeType does not support UNICODE filesnames! */
 
#if defined(UNICODE)
 
	/* We need a cast here back from wide because FreeType doesn't support
 
	 * widechar filenames. Just use the buffer we allocated before for the
 
	 * font_name search */
 
	font_path = (char*)font_namep;
 
	WIDE_TO_MB_BUFFER(vbuffer, font_path, MAX_PATH * sizeof(TCHAR));
 
#else
 
	font_path = vbuffer;
 
#endif
 

	
 
	ttd_strlcat(font_path, "\\", MAX_PATH * sizeof(TCHAR));
 
	ttd_strlcat(font_path, WIDE_TO_MB(dbuffer), MAX_PATH * sizeof(TCHAR));
 
	index = 0;
 
	do {
 
		err = FT_New_Face(_library, font_path, index, face);
 
		if (err != FT_Err_Ok) break;
 

	
 
		if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
 
		err = FT_Err_Cannot_Open_Resource;
 

	
 
	} while ((FT_Long)++index != (*face)->num_faces);
 

	
 

	
 
folder_error:
 
#if defined(UNICODE)
 
	free(font_path);
 
#endif
 
registry_no_font_found:
 
	RegCloseKey(hKey);
 
	return err;
 
}
 
#else
 
# ifdef WITH_FONTCONFIG
 
static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
 
{
 
	FT_Error err = FT_Err_Cannot_Open_Resource;
 

	
 
	if (!FcInit()) {
 
		ShowInfoF("Unable to load font configuration");
 
	} else {
 
		FcPattern *match;
 
		FcPattern *pat;
 
		FcFontSet *fs;
 
		FcResult  result;
 
		char *font_style;
 
		char *font_family;
 

	
 
		/* Split & strip the font's style */
 
		font_family = strdup(font_name);
 
		font_style = strchr(font_family, ',');
 
		if (font_style != NULL) {
 
			font_style[0] = '\0';
 
			font_style++;
 
			while (*font_style == ' ' || *font_style == '\t') font_style++;
 
		}
 

	
 
		/* Resolve the name and populate the information structure */
 
		pat = FcNameParse((FcChar8*)font_family);
 
		if (font_style != NULL) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
 
		FcConfigSubstitute(0, pat, FcMatchPattern);
 
		FcDefaultSubstitute(pat);
 
		fs = FcFontSetCreate();
 
		match = FcFontMatch(0, pat, &result);
 

	
 
		if (fs != NULL && match != NULL) {
 
			int i;
 
			FcChar8 *family;
 
			FcChar8 *style;
 
			FcChar8 *file;
 
			FcFontSetAdd(fs, match);
 

	
 
			for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
 
				/* Try the new filename */
 
				if (FcPatternGetString(fs->fonts[i], FC_FILE,   0, &file)   == FcResultMatch &&
 
						FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
 
						FcPatternGetString(fs->fonts[i], FC_STYLE,  0, &style)  == FcResultMatch) {
 

	
 
					/* The correct style? */
 
					if (font_style != NULL && strcasecmp(font_style, (char*)style) != 0) continue;
 

	
 
					/* Font config takes the best shot, which, if the family name is spelled
 
					* wrongly a 'random' font, so check whether the family name is the
 
					* same as the supplied name */
 
					if (strcasecmp(font_family, (char*)family) == 0) {
 
						err = FT_New_Face(_library, (char *)file, 0, face);
 
					}
 
				}
 
			}
 
		}
 

	
 
		free(font_family);
 
		FcPatternDestroy(pat);
 
		FcFontSetDestroy(fs);
 
		FcFini();
 
	}
 

	
 
	return err;
 
}
 
# else
 
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
 
# endif /* WITH_FONTCONFIG */
 

	
 
#endif
 

	
 
/**
 
 * Loads the freetype font.
 
 * First type to load the fontname as if it were a path. If that fails,
 
 * try to resolve the filename of the font using fontconfig, where the
 
 * format is 'font family name' or 'font family name, font style'.
 
 */
 
static void LoadFreeTypeFont(const char *font_name, FT_Face *face, const char *type)
 
{
 
	FT_Error error;
 

	
 
	if (strlen(font_name) == 0) return;
 

	
 
	error = FT_New_Face(_library, font_name, 0, face);
 

	
 
	if (error != FT_Err_Ok) error = GetFontByFaceName(font_name, face);
 

	
 
	if (error == FT_Err_Ok) {
 
		DEBUG(freetype, 2, "Requested '%s', using '%s %s'", font_name, (*face)->family_name, (*face)->style_name);
 

	
 
		/* Attempt to select the unicode character map */
 
		error = FT_Select_Charmap(*face, ft_encoding_unicode);
 
		if (error == FT_Err_Ok) return; // Success
 

	
 
		if (error == FT_Err_Invalid_CharMap_Handle) {
 
			/* Try to pick a different character map instead. We default to
 
			 * the first map, but platform_id 0 encoding_id 0 should also
 
			 * be unicode (strange system...) */
 
			FT_CharMap found = (*face)->charmaps[0];
 
			int i;
 

	
 
			for (i = 0; i < (*face)->num_charmaps; i++) {
 
				FT_CharMap charmap = (*face)->charmaps[i];
 
				if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
 
					found = charmap;
 
				}
 
			}
 

	
 
			if (found != NULL) {
 
				error = FT_Set_Charmap(*face, found);
 
				if (error == FT_Err_Ok) return;
 
			}
 
		}
 
	}
 

	
 
	FT_Done_Face(*face);
 
	*face = NULL;
 

	
 
	ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, type, error);
 
}
 

	
 

	
 
void InitFreeType(void)
 
{
 
	if (strlen(_freetype.small_font) == 0 && strlen(_freetype.medium_font) == 0 && strlen(_freetype.large_font) == 0) {
 
		DEBUG(freetype, 1, "No font faces specified, using sprite fonts instead");
 
		return;
 
	}
 

	
 
	if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
 
		ShowInfoF("Unable to initialize FreeType, using sprite fonts instead");
 
		return;
 
	}
 

	
 
	DEBUG(freetype, 2, "Initialized");
 

	
 
	/* Load each font */
 
	LoadFreeTypeFont(_freetype.small_font,  &_face_small,  "small");
 
	LoadFreeTypeFont(_freetype.medium_font, &_face_medium, "medium");
 
	LoadFreeTypeFont(_freetype.large_font,  &_face_large,  "large");
 

	
 
	/* Set each font size */
 
	if (_face_small  != NULL) FT_Set_Pixel_Sizes(_face_small,  0, _freetype.small_size);
 
	if (_face_medium != NULL) FT_Set_Pixel_Sizes(_face_medium, 0, _freetype.medium_size);
 
	if (_face_large  != NULL) FT_Set_Pixel_Sizes(_face_large,  0, _freetype.large_size);
 
}
 

	
 

	
 
static FT_Face GetFontFace(FontSize size)
 
{
 
	switch (size) {
 
		default: NOT_REACHED();
 
		case FS_NORMAL: return _face_medium;
 
		case FS_SMALL:  return _face_small;
 
		case FS_LARGE:  return _face_large;
 
	}
 
}
 

	
 

	
 
typedef struct GlyphEntry {
 
	Sprite *sprite;
 
	byte width;
 
} GlyphEntry;
 

	
 

	
 
/* The glyph cache. This is structured to reduce memory consumption.
 
 * 1) There is a 'segment' table for each font size.
 
 * 2) Each segment table is a discrete block of characters.
 
 * 3) Each block contains 256 (aligned) characters sequential characters.
 
 *
 
 * The cache is accessed in the following way:
 
 * For character 0x0041  ('A'): _glyph_ptr[FS_NORMAL][0x00][0x41]
 
 * For character 0x20AC (Euro): _glyph_ptr[FS_NORMAL][0x20][0xAC]
 
 *
 
 * Currently only 256 segments are allocated, "limiting" us to 65536 characters.
 
 * This can be simply changed in the two functions Get & SetGlyphPtr.
 
 */
 
static GlyphEntry **_glyph_ptr[FS_END];
 

	
 

	
 
static GlyphEntry *GetGlyphPtr(FontSize size, WChar key)
 
{
 
	if (_glyph_ptr[size] == NULL) return NULL;
 
	if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) return NULL;
 
	return &_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)];
 
}
 

	
 

	
 
static void SetGlyphPtr(FontSize size, WChar key, const GlyphEntry *glyph)
 
{
 
	if (_glyph_ptr[size] == NULL) {
 
		DEBUG(freetype, 3, "Allocating root glyph cache for size %u", size);
 
		_glyph_ptr[size] = calloc(256, sizeof(**_glyph_ptr));
 
	}
 

	
 
	if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) {
 
		DEBUG(freetype, 3, "Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), size);
 
		_glyph_ptr[size][GB(key, 8, 8)] = calloc(256, sizeof(***_glyph_ptr));
 
	}
 

	
 
	DEBUG(freetype, 4, "Set glyph for unicode character 0x%04X, size %u", key, size);
 
	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].sprite = glyph->sprite;
 
	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].width  = glyph->width;
 
}
 

	
 

	
 
const Sprite *GetGlyph(FontSize size, WChar key)
 
{
 
	FT_Face face = GetFontFace(size);
 
	FT_GlyphSlot slot;
 
	GlyphEntry new_glyph;
 
	GlyphEntry *glyph;
 
	Sprite *sprite;
 
	int width;
 
	int height;
 
	int x;
 
	int y;
 
	int y_adj;
 

	
 
	assert(IsPrintable(key));
 

	
 
	/* Bail out if no face loaded, or for our special characters */
 
	if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
 
		SpriteID sprite = GetUnicodeGlyph(size, key);
 
		if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
 
		return GetSprite(sprite);
 
	}
 

	
 
	/* Check for the glyph in our cache */
 
	glyph = GetGlyphPtr(size, key);
 
	if (glyph != NULL && glyph->sprite != NULL) return glyph->sprite;
 

	
 
	slot = face->glyph;
 

	
 
	FT_Load_Char(face, key, FT_LOAD_DEFAULT);
 
	FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
 

	
 
	/* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
 
	width  = max(1, slot->bitmap.width + (size == FS_NORMAL));
 
	height = max(1, slot->bitmap.rows  + (size == FS_NORMAL));
 

	
 
	/* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
 
	sprite = calloc(width * height + 8, 1);
 
	sprite->info   = 1;
 
	sprite->width  = width;
 
	sprite->height = height;
 
	sprite->x_offs = slot->bitmap_left;
 
	// XXX 2 should be determined somehow... it's right for the normal face
 
	y_adj = (size == FS_NORMAL) ? 2 : 0;
 
	sprite->y_offs = GetCharacterHeight(size) - slot->bitmap_top - y_adj;
 

	
 
	/* Draw shadow for medium size */
 
	if (size == FS_NORMAL) {
 
		for (y = 0; y < slot->bitmap.rows; y++) {
 
			for (x = 0; x < slot->bitmap.width; x++) {
 
				if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
 
					sprite->data[1 + x + (1 + y) * sprite->width] = SHADOW_COLOUR;
 
				}
 
			}
 
		}
 
	}
 

	
 
	for (y = 0; y < slot->bitmap.rows; y++) {
 
		for (x = 0; x < slot->bitmap.width; x++) {
 
			if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
 
				sprite->data[x + y * sprite->width] = FACE_COLOUR;
 
			}
 
		}
 
	}
 

	
 
	new_glyph.sprite = sprite;
 
	new_glyph.width  = (slot->advance.x >> 6) + (size != FS_NORMAL);
 

	
 
	SetGlyphPtr(size, key, &new_glyph);
 

	
 
	return sprite;
 
}
 

	
 

	
 
uint GetGlyphWidth(FontSize size, WChar key)
 
{
 
	FT_Face face = GetFontFace(size);
 
	GlyphEntry *glyph;
 

	
 
	if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
 
		SpriteID sprite = GetUnicodeGlyph(size, key);
 
		if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
 
		return SpriteExists(sprite) ? GetSprite(sprite)->width + (size != FS_NORMAL) : 0;
 
	}
 

	
 
	glyph = GetGlyphPtr(size, key);
 
	if (glyph == NULL || glyph->sprite == NULL) {
 
		GetGlyph(size, key);
 
		glyph = GetGlyphPtr(size, key);
 
	}
 

	
 
	return glyph->width;
 
}
 

	
 

	
 
#endif /* WITH_FREETYPE */
 

	
 
/* Sprite based glyph mapping */
 

	
 
#include "table/unicode.h"
 

	
 
static SpriteID **_unicode_glyph_map[FS_END];
 

	
 

	
 
/** Get the SpriteID of the first glyph for the given font size */
 
static SpriteID GetFontBase(FontSize size)
 
{
 
	switch (size) {
 
		default: NOT_REACHED();
 
		case FS_NORMAL: return SPR_ASCII_SPACE;
 
		case FS_SMALL:  return SPR_ASCII_SPACE_SMALL;
 
		case FS_LARGE:  return SPR_ASCII_SPACE_BIG;
 
	}
 
}
 

	
 

	
 
SpriteID GetUnicodeGlyph(FontSize size, uint32 key)
 
{
 
	if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) return 0;
 
	return _unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)];
 
}
 

	
 

	
 
void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite)
 
{
 
	if (_unicode_glyph_map[size] == NULL) _unicode_glyph_map[size] = calloc(256, sizeof(*_unicode_glyph_map[size]));
 
	if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) _unicode_glyph_map[size][GB(key, 8, 8)] = calloc(256, sizeof(**_unicode_glyph_map[size]));
 
	_unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)] = sprite;
 
}
 

	
 

	
 
void InitializeUnicodeGlyphMap(void)
 
{
 
	FontSize size;
 
	SpriteID base;
 
	SpriteID sprite;
 
	uint i;
 

	
 
	for (size = FS_NORMAL; size != FS_END; size++) {
 
		/* Clear out existing glyph map if it exists */
 
		if (_unicode_glyph_map[size] != NULL) {
 
			for (i = 0; i < 256; i++) {
 
				if (_unicode_glyph_map[size][i] != NULL) free(_unicode_glyph_map[size][i]);
 
			}
 
			free(_unicode_glyph_map[size]);
 
			_unicode_glyph_map[size] = NULL;
 
		}
 

	
 
		base = GetFontBase(size);
 
		for (i = ASCII_LETTERSTART; i < 256; i++) {
 
			sprite = base + i - ASCII_LETTERSTART;
 
			if (!SpriteExists(sprite)) continue;
 
			SetUnicodeGlyph(size, i, sprite);
 
			SetUnicodeGlyph(size, i + SCC_SPRITE_START, sprite);
 
		}
 
		for (i = 0; i < lengthof(_default_unicode_map); i++) {
 
			sprite = base + _default_unicode_map[i].key - ASCII_LETTERSTART;
 
			SetUnicodeGlyph(size, _default_unicode_map[i].code, sprite);
 
		}
 
	}
 
}
 

	
 

	
 

	
src/genworld.c
Show inline comments
 
deleted file
src/genworld.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "player.h"
 
#include "table/sprites.h"
 
#include "variables.h"
 
#include "thread.h"
 
#include "genworld.h"
 
#include "gfx.h"
 
#include "gfxinit.h"
 
#include "gui.h"
 
#include "network/network.h"
 
#include "debug.h"
 
#include "settings.h"
 
#include "heightmap.h"
 
#include "date.h"
 

	
 
void GenerateLandscape(byte mode);
 
void GenerateClearTile(void);
 
void GenerateIndustries(void);
 
void GenerateUnmovables(void);
 
bool GenerateTowns(void);
 
void GenerateTrees(void);
 

	
 
void StartupEconomy(void);
 
void StartupPlayers(void);
 
void StartupDisasters(void);
 

	
 
void InitializeGame(int mode, uint size_x, uint size_y);
 

	
 
void ConvertGroundTilesIntoWaterTiles(void);
 

	
 
/* Please only use this variable in genworld.h and genworld.c and
 
 *  nowhere else. For speed improvements we need it to be global, but
 
 *  in no way the meaning of it is to use it anywhere else besides
 
 *  in the genworld.h and genworld.c! -- TrueLight */
 
gw_info _gw;
 

	
 
/**
 
 * Set the status of the Paint flag.
 
 *  If it is true, the thread will hold with any futher generating till
 
 *  the drawing of the screen is done. This is handled by
 
 *  SetGeneratingWorldProgress(), so calling that function will stall
 
 *  from time to time.
 
 */
 
void SetGeneratingWorldPaintStatus(bool status)
 
{
 
	_gw.wait_for_draw = status;
 
}
 

	
 
/**
 
 * Returns true if the thread wants the main program to do a (full) paint.
 
 *  If this returns false, please do not update the screen. Because we are
 
 *  writing in a thread, it can cause damaged data (reading and writing the
 
 *  same tile at the same time).
 
 */
 
bool IsGeneratingWorldReadyForPaint(void)
 
{
 
	/* If we are in quit_thread mode, ignore this and always return false. This
 
	 *  forces the screen to not be drawn, and the GUI not to wait for a draw. */
 
	if (!_gw.active || _gw.quit_thread || !_gw.threaded) return false;
 

	
 
	return _gw.wait_for_draw;
 
}
 

	
 
/**
 
 * Tells if the world generation is done in a thread or not.
 
 */
 
bool IsGenerateWorldThreaded(void)
 
{
 
	return _gw.threaded && !_gw.quit_thread;
 
}
 

	
 
/**
 
 * The internal, real, generate function.
 
 */
 
static void *_GenerateWorld(void *arg)
 
{
 
	_generating_world = true;
 
	if (_network_dedicated) DEBUG(net, 0, "Generating map, please wait...");
 
	/* Set the Random() seed to generation_seed so we produce the same map with the same seed */
 
	if (_patches.generation_seed == GENERATE_NEW_SEED) _patches.generation_seed = _patches_newgame.generation_seed = InteractiveRandom();
 
	_random_seeds[0][0] = _random_seeds[0][1] = _patches.generation_seed;
 
	SetGeneratingWorldProgress(GWP_MAP_INIT, 2);
 
	SetObjectToPlace(SPR_CURSOR_ZZZ, 0, 0, 0);
 

	
 
	IncreaseGeneratingWorldProgress(GWP_MAP_INIT);
 
	// Must start economy early because of the costs.
 
	StartupEconomy();
 

	
 
	// Don't generate landscape items when in the scenario editor.
 
	if (_gw.mode == GW_EMPTY) {
 
		SetGeneratingWorldProgress(GWP_UNMOVABLE, 1);
 

	
 
		/* Make the map the height of the patch setting */
 
		if (_game_mode != GM_MENU) FlatEmptyWorld(_patches.se_flat_world_height);
 

	
 
		ConvertGroundTilesIntoWaterTiles();
 
		IncreaseGeneratingWorldProgress(GWP_UNMOVABLE);
 
	} else {
 
		GenerateLandscape(_gw.mode);
 
		GenerateClearTile();
 

	
 
		// only generate towns, tree and industries in newgame mode.
 
		if (_game_mode != GM_EDITOR) {
 
			GenerateTowns();
 
			GenerateIndustries();
 
			GenerateUnmovables();
 
			GenerateTrees();
 
		}
 
	}
 

	
 
	// These are probably pointless when inside the scenario editor.
 
	SetGeneratingWorldProgress(GWP_GAME_INIT, 3);
 
	StartupPlayers();
 
	IncreaseGeneratingWorldProgress(GWP_GAME_INIT);
 
	StartupEngines();
 
	IncreaseGeneratingWorldProgress(GWP_GAME_INIT);
 
	StartupDisasters();
 
	_generating_world = false;
 

	
 
	// No need to run the tile loop in the scenario editor.
 
	if (_gw.mode != GW_EMPTY) {
 
		uint i;
 

	
 
		SetGeneratingWorldProgress(GWP_RUNTILELOOP, 0x500);
 
		for (i = 0; i < 0x500; i++) {
 
			RunTileLoop();
 
			IncreaseGeneratingWorldProgress(GWP_RUNTILELOOP);
 
		}
 
	}
 

	
 
	ResetObjectToPlace();
 
	SetLocalPlayer(_gw.lp);
 

	
 
	SetGeneratingWorldProgress(GWP_GAME_START, 1);
 
	/* Call any callback */
 
	if (_gw.proc != NULL) _gw.proc();
 
	IncreaseGeneratingWorldProgress(GWP_GAME_START);
 

	
 
	if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
 
	/* Show all vital windows again, because we have hidden them */
 
	if (_gw.threaded && _game_mode != GM_MENU) ShowVitalWindows();
 
	_gw.active   = false;
 
	_gw.thread   = NULL;
 
	_gw.proc     = NULL;
 
	_gw.threaded = false;
 

	
 
	DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0);
 
	MarkWholeScreenDirty();
 

	
 
	if (_network_dedicated) DEBUG(net, 0, "Map generated, starting game");
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 * Set here the function, if any, that you want to be called when landscape
 
 *  generation is done.
 
 */
 
void GenerateWorldSetCallback(gw_done_proc *proc)
 
{
 
	_gw.proc = proc;
 
}
 

	
 
/**
 
 * Set here the function, if any, that you want to be called when landscape
 
 *  generation is aborted.
 
 */
 
void GenerateWorldSetAbortCallback(gw_abort_proc *proc)
 
{
 
	_gw.abortp = proc;
 
}
 

	
 
/**
 
 * This will wait for the thread to finish up his work. It will not continue
 
 *  till the work is done.
 
 */
 
void WaitTillGeneratedWorld(void)
 
{
 
	if (_gw.thread == NULL) return;
 
	_gw.quit_thread = true;
 
	OTTDJoinThread(_gw.thread);
 
	_gw.thread   = NULL;
 
	_gw.threaded = false;
 
}
 

	
 
/**
 
 * Initializes the abortion process
 
 */
 
void AbortGeneratingWorld(void)
 
{
 
	_gw.abort = true;
 
}
 

	
 
/**
 
 * Is the generation being aborted?
 
 */
 
bool IsGeneratingWorldAborted(void)
 
{
 
	return _gw.abort;
 
}
 

	
 
/**
 
 * Really handle the abortion, i.e. clean up some of the mess
 
 */
 
void HandleGeneratingWorldAbortion(void)
 
{
 
	/* Clean up - in SE create an empty map, otherwise, go to intro menu */
 
	_switch_mode = (_game_mode == GM_EDITOR) ? SM_EDITOR : SM_MENU;
 

	
 
	if (_gw.abortp != NULL) _gw.abortp();
 

	
 
	if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
 
	/* Show all vital windows again, because we have hidden them */
 
	if (_gw.threaded && _game_mode != GM_MENU) ShowVitalWindows();
 
	_gw.active   = false;
 
	_gw.thread   = NULL;
 
	_gw.proc     = NULL;
 
	_gw.abortp   = NULL;
 
	_gw.threaded = false;
 

	
 
	DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0);
 
	MarkWholeScreenDirty();
 

	
 
	OTTDExitThread();
 
}
 

	
 
/**
 
 * Generate a world.
 
 * @param mode The mode of world generation (@see GenerateWorldModes).
 
 * @param size_x The X-size of the map.
 
 * @param size_y The Y-size of the map.
 
 */
 
void GenerateWorld(int mode, uint size_x, uint size_y)
 
{
 
	if (_gw.active) return;
 
	_gw.mode   = mode;
 
	_gw.size_x = size_x;
 
	_gw.size_y = size_y;
 
	_gw.active = true;
 
	_gw.abort  = false;
 
	_gw.abortp = NULL;
 
	_gw.lp     = _local_player;
 
	_gw.wait_for_draw = false;
 
	_gw.quit_thread   = false;
 
	_gw.threaded      = true;
 

	
 
	/* This disables some commands and stuff */
 
	SetLocalPlayer(PLAYER_SPECTATOR);
 
	/* Make sure everything is done via OWNER_NONE */
 
	_current_player = OWNER_NONE;
 

	
 
	/* Set the date before loading sprites as some newgrfs check it */
 
	SetDate(ConvertYMDToDate(_patches.starting_year, 0, 1));
 

	
 
	/* Load the right landscape stuff */
 
	GfxLoadSprites();
 
	LoadStringWidthTable();
 

	
 
	InitializeGame(IG_NONE, _gw.size_x, _gw.size_y);
 
	PrepareGenerateWorldProgress();
 

	
 
	/* Re-init the windowing system */
 
	ResetWindowSystem();
 

	
 
	/* Create toolbars */
 
	SetupColorsAndInitialWindow();
 

	
 
	if (_network_dedicated ||
 
	    (_gw.thread = OTTDCreateThread(&_GenerateWorld, NULL)) == NULL) {
 
		DEBUG(misc, 1, "Cannot create genworld thread, reverting to single-threaded mode");
 
		_gw.threaded = false;
 
		_GenerateWorld(NULL);
 
		return;
 
	}
 

	
 
	/* Remove any open window */
 
	DeleteAllNonVitalWindows();
 
	/* Hide vital windows, because we don't allow to use them */
 
	HideVitalWindows();
 

	
 
	/* Don't show the dialog if we don't have a thread */
 
	ShowGenerateWorldProgress();
 

	
 
	/* Centre the view on the map */
 
	if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) {
 
		ScrollMainWindowToTile(TileXY(MapSizeX() / 2, MapSizeY() / 2));
 
	}
 
}
src/genworld_gui.c
Show inline comments
 
deleted file
src/genworld_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "heightmap.h"
 
#include "functions.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "strings.h"
 
#include "gfxinit.h"
 
#include "player.h"
 
#include "command.h"
 
#include "sound.h"
 
#include "variables.h"
 
#include "string.h"
 
#include "settings.h"
 
#include "debug.h"
 
#include "genworld.h"
 
#include "network/network.h"
 
#include "thread.h"
 
#include "date.h"
 
#include "newgrf_config.h"
 

	
 
enum {
 
	START_DATE_QUERY,
 
	SNOW_LINE_QUERY,
 
	FLAT_WORLD_HEIGHT_QUERY,
 

	
 
	LEN_RND_SEED = 11,
 
	SEED_EDIT    = 15,
 
};
 

	
 
/**
 
 * In what 'mode' the GenerateLandscapeWindowProc is.
 
 */
 
typedef enum glwp_modes {
 
	GLWP_GENERATE,
 
	GLWP_HEIGHTMAP,
 
	GLWP_SCENARIO,
 
	GLWP_END
 
} glwp_modes;
 

	
 
static uint _heightmap_x = 0;
 
static uint _heightmap_y = 0;
 
static StringID _heightmap_str = STR_NULL;
 
static bool _goto_editor = false;
 

	
 
extern void SwitchMode(int new_mode);
 

	
 
static inline void SetNewLandscapeType(byte landscape)
 
{
 
	_opt_newgame.landscape = landscape;
 
	InvalidateWindowClasses(WC_SELECT_GAME);
 
	InvalidateWindowClasses(WC_GENERATE_LANDSCAPE);
 
}
 

	
 
// no longer static to allow calling from outside module
 
const Widget _generate_landscape_widgets[] = {
 
{  WWT_CLOSEBOX,  RESIZE_NONE, 13,   0,  10,   0,  13, STR_00C5,                     STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION, RESIZE_NONE, 13,  11, 337,   0,  13, STR_WORLD_GENERATION_CAPTION, STR_NULL},
 
{      WWT_PANEL, RESIZE_NONE, 13,   0, 337,  14, 267, 0x0,                          STR_NULL},
 

	
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12,  10,  86,  24,  78, SPR_SELECT_TEMPERATE,         STR_030E_SELECT_TEMPERATE_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12,  90, 166,  24,  78, SPR_SELECT_SUB_ARCTIC,        STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12, 170, 246,  24,  78, SPR_SELECT_SUB_TROPICAL,      STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12, 250, 326,  24,  78, SPR_SELECT_TOYLAND,           STR_0311_SELECT_TOYLAND_LANDSCAPE},
 

	
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 149,  90, 101, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 150, 161,  90, 101, STR_0225,                     STR_NULL}, // Mapsize X
 
{      WWT_PANEL, RESIZE_NONE, 12, 180, 215,  90, 101, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 216, 227,  90, 101, STR_0225,                     STR_NULL}, // Mapsize Y
 

	
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 163, 112, 123, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 112, 123, STR_0225,                     STR_NULL}, // Number of towns
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 163, 130, 141, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 130, 141, STR_0225,                     STR_NULL}, // Number of industries
 

	
 
{      WWT_PANEL, RESIZE_NONE, 15, 114, 207, 152, 163, 0x0,                          STR_RANDOM_SEED_HELP}, // Edit box for seed
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 216, 326, 152, 163, STR_RANDOM,                   STR_RANDOM_HELP},
 

	
 
{    WWT_TEXTBTN, RESIZE_NONE,  6, 243, 326, 228, 257, STR_GENERATE,                 STR_NULL}, // Generate button
 

	
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 216, 227, 112, 123, SPR_ARROW_DOWN,               STR_029E_MOVE_THE_STARTING_DATE},
 
{      WWT_PANEL, RESIZE_NONE, 12, 228, 314, 112, 123, 0x0,                          STR_NULL},
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 112, 123, SPR_ARROW_UP,                 STR_029F_MOVE_THE_STARTING_DATE},
 

	
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 282, 293, 130, 141, SPR_ARROW_DOWN,               STR_SNOW_LINE_DOWN},
 
{      WWT_PANEL, RESIZE_NONE, 12, 294, 314, 130, 141, 0x0,                          STR_NULL},
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 130, 141, SPR_ARROW_UP,                 STR_SNOW_LINE_UP},
 

	
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 219, 192, 203, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 192, 203, STR_0225,                     STR_NULL}, // Tree placer
 

	
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 219, 174, 185, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 174, 185, STR_0225,                     STR_NULL}, // Landscape generator
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 219, 210, 221, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 210, 221, STR_0225,                     STR_NULL}, // Terrain type
 
{      WWT_PANEL, RESIZE_NONE, 12, 113, 219, 228, 239, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 228, 239, STR_0225,                     STR_NULL}, // Water quantity
 
{      WWT_PANEL, RESIZE_NONE, 12, 113, 219, 246, 257, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 246, 257, STR_0225,                     STR_NULL}, // Map smoothness
 
{   WIDGETS_END},
 
};
 

	
 
const Widget _heightmap_load_widgets[] = {
 
{   WWT_CLOSEBOX, RESIZE_NONE, 13,   0,  10,   0,  13, STR_00C5,                     STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION, RESIZE_NONE, 13,  11, 337,   0,  13, STR_WORLD_GENERATION_CAPTION, STR_NULL},
 
{      WWT_PANEL, RESIZE_NONE, 13,   0, 337,  14, 235, 0x0,                          STR_NULL},
 

	
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12,  10,  86,  24,  78, SPR_SELECT_TEMPERATE,        STR_030E_SELECT_TEMPERATE_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12,  90, 166,  24,  78, SPR_SELECT_SUB_ARCTIC,       STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12, 170, 246,  24,  78, SPR_SELECT_SUB_TROPICAL,     STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12, 250, 326,  24,  78, SPR_SELECT_TOYLAND,          STR_0311_SELECT_TOYLAND_LANDSCAPE},
 

	
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 149, 112, 123, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 150, 161, 112, 123, STR_0225,                     STR_NULL}, // Mapsize X
 
{      WWT_PANEL, RESIZE_NONE, 12, 180, 215, 112, 123, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 216, 227, 112, 123, STR_0225,                     STR_NULL}, // Mapsize Y
 

	
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 163, 134, 145, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 134, 145, STR_0225,                     STR_NULL}, // Number of towns
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 163, 152, 163, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 152, 163, STR_0225,                     STR_NULL}, // Number of industries
 

	
 
{      WWT_PANEL, RESIZE_NONE, 15, 114, 194, 174, 185, 0x0,                          STR_RANDOM_SEED_HELP}, // Edit box for seed
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 203, 285, 174, 185, STR_RANDOM,                   STR_RANDOM_HELP},
 

	
 
{    WWT_TEXTBTN, RESIZE_NONE,  6, 243, 326, 196, 225, STR_GENERATE,                 STR_NULL}, // Generate button
 

	
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 216, 227, 134, 145, SPR_ARROW_DOWN,               STR_029E_MOVE_THE_STARTING_DATE},
 
{      WWT_PANEL, RESIZE_NONE, 12, 228, 314, 134, 145, 0x0,                          STR_NULL},
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 134, 145, SPR_ARROW_UP,                 STR_029F_MOVE_THE_STARTING_DATE},
 

	
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 282, 293, 152, 163, SPR_ARROW_DOWN,               STR_SNOW_LINE_DOWN},
 
{      WWT_PANEL, RESIZE_NONE, 12, 294, 314, 152, 163, 0x0,                          STR_NULL},
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 152, 163, SPR_ARROW_UP,                 STR_SNOW_LINE_UP},
 

	
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 219, 196, 207, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 196, 207, STR_0225,                     STR_NULL}, // Tree placer
 

	
 
{      WWT_PANEL, RESIZE_NONE, 12, 114, 219, 214, 225, 0x0,                          STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 214, 225, STR_0225,                     STR_NULL}, // Heightmap rotation
 
{   WIDGETS_END},
 
};
 

	
 
static void StartGeneratingLandscape(glwp_modes mode)
 
{
 
	/* If we want to go to the editor, and aren't yet, we need to delay
 
	 *  it as long as possible, else it gives nasty side-effects (aborting
 
	 *  results in ending up in the SE, which you don't want. Therefor we
 
	 *  use this switch to do it at the very end.
 
	 */
 
	if (_goto_editor) _game_mode = GM_EDITOR;
 

	
 
	DeleteWindowByClass(WC_GENERATE_LANDSCAPE);
 
	DeleteWindowByClass(WC_INDUSTRY_VIEW);
 
	DeleteWindowByClass(WC_TOWN_VIEW);
 
	DeleteWindowByClass(WC_LAND_INFO);
 

	
 
	/* Copy all XXX_newgame to XXX */
 
	UpdatePatches();
 
	_opt_ptr = &_opt;
 
	*_opt_ptr = _opt_newgame;
 
	ResetGRFConfig(true);
 

	
 
	SndPlayFx(SND_15_BEEP);
 
	switch (mode) {
 
	case GLWP_GENERATE:  _switch_mode = (_game_mode == GM_EDITOR) ? SM_GENRANDLAND    : SM_NEWGAME;         break;
 
	case GLWP_HEIGHTMAP: _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_HEIGHTMAP : SM_START_HEIGHTMAP; break;
 
	case GLWP_SCENARIO:  _switch_mode = SM_EDITOR; break;
 
	default: NOT_REACHED(); return;
 
	}
 
}
 

	
 
static void HeightmapScaledTooMuchCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) StartGeneratingLandscape((glwp_modes)w->window_number);
 
}
 

	
 
void GenerateLandscapeWndProc(Window *w, WindowEvent *e)
 
{
 
	static const StringID mapsizes[]    = {STR_64, STR_128, STR_256, STR_512, STR_1024, STR_2048, INVALID_STRING_ID};
 
	static const StringID elevations[]  = {STR_682A_VERY_FLAT, STR_682B_FLAT, STR_682C_HILLY, STR_682D_MOUNTAINOUS, INVALID_STRING_ID};
 
	static const StringID sea_lakes[]   = {STR_VERY_LOW, STR_6820_LOW, STR_6821_MEDIUM, STR_6822_HIGH, INVALID_STRING_ID};
 
	static const StringID smoothness[]  = {STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_ROUGH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_ROUGH, INVALID_STRING_ID};
 
	static const StringID tree_placer[] = {STR_CONFIG_PATCHES_TREE_PLACER_NONE, STR_CONFIG_PATCHES_TREE_PLACER_ORIGINAL, STR_CONFIG_PATCHES_TREE_PLACER_IMPROVED, INVALID_STRING_ID};
 
	static const StringID rotation[]    = {STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_COUNTER_CLOCKWISE, STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_CLOCKWISE, INVALID_STRING_ID};
 
	static const StringID landscape[]   = {STR_CONFIG_PATCHES_LAND_GENERATOR_ORIGINAL, STR_CONFIG_PATCHES_LAND_GENERATOR_TERRA_GENESIS, INVALID_STRING_ID};
 
	static const StringID num_towns[]   = {STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID};
 
	static const StringID num_inds[]    = {STR_26816_NONE, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID};
 

	
 
	/* Data used for the generate seed edit box */
 
	static querystr_d _genseed_query;
 
	static char _genseed_buffer[LEN_RND_SEED];
 

	
 
	uint mode = w->window_number;
 
	uint y;
 

	
 
	switch (e->event) {
 
	case WE_CREATE:
 
		LowerWindowWidget(w, _opt_newgame.landscape + 3);
 

	
 
		snprintf(_genseed_buffer, sizeof(_genseed_buffer), "%u", _patches_newgame.generation_seed);
 
		InitializeTextBuffer(&_genseed_query.text, _genseed_buffer, lengthof(_genseed_buffer), 120);
 
		_genseed_query.caption = STR_NULL;
 
		_genseed_query.afilter = CS_NUMERAL;
 
		break;
 

	
 
	case WE_PAINT:
 
		/* You can't select smoothness if not terragenesis */
 
		if (mode == GLWP_GENERATE) {
 
			SetWindowWidgetDisabledState(w, 32, _patches_newgame.land_generator == 0);
 
			SetWindowWidgetDisabledState(w, 33, _patches_newgame.land_generator == 0);
 
		}
 
		/* Disable snowline if not hilly */
 
		SetWindowWidgetDisabledState(w, 22, _opt_newgame.landscape != LT_HILLY);
 
		/* Disable town and industry in SE */
 
		SetWindowWidgetDisabledState(w, 11, _game_mode == GM_EDITOR);
 
		SetWindowWidgetDisabledState(w, 12, _game_mode == GM_EDITOR);
 
		SetWindowWidgetDisabledState(w, 13, _game_mode == GM_EDITOR);
 
		SetWindowWidgetDisabledState(w, 14, _game_mode == GM_EDITOR);
 
		SetWindowWidgetDisabledState(w, 24, _game_mode == GM_EDITOR);
 
		SetWindowWidgetDisabledState(w, 25, _game_mode == GM_EDITOR);
 

	
 
		SetWindowWidgetDisabledState(w, 18, _patches_newgame.starting_year <= MIN_YEAR);
 
		SetWindowWidgetDisabledState(w, 20, _patches_newgame.starting_year >= MAX_YEAR);
 
		SetWindowWidgetDisabledState(w, 21, _patches_newgame.snow_line_height <= 2 || _opt_newgame.landscape != LT_HILLY);
 
		SetWindowWidgetDisabledState(w, 23, _patches_newgame.snow_line_height >= 13 || _opt_newgame.landscape != LT_HILLY);
 

	
 
		SetWindowWidgetLoweredState(w, 3, _opt_newgame.landscape == LT_NORMAL);
 
		SetWindowWidgetLoweredState(w, 4, _opt_newgame.landscape == LT_HILLY);
 
		SetWindowWidgetLoweredState(w, 5, _opt_newgame.landscape == LT_DESERT);
 
		SetWindowWidgetLoweredState(w, 6, _opt_newgame.landscape == LT_CANDY);
 
		DrawWindowWidgets(w);
 

	
 
		y = (mode == GLWP_HEIGHTMAP) ? 22 : 0;
 

	
 
		DrawString( 12,  91 + y, STR_MAPSIZE, 0);
 
		DrawString(119,  91 + y, mapsizes[_patches_newgame.map_x - 6], 0x10);
 
		DrawString(168,  91 + y, STR_BY, 0);
 
		DrawString(182,  91 + y, mapsizes[_patches_newgame.map_y - 6], 0x10);
 

	
 
		DrawString( 12, 113 + y, STR_NUMBER_OF_TOWNS, 0);
 
		DrawString( 12, 131 + y, STR_NUMBER_OF_INDUSTRIES, 0);
 
		if (_game_mode == GM_EDITOR) {
 
			DrawString(118, 113 + y, STR_6836_OFF, 0x10);
 
			DrawString(118, 131 + y, STR_6836_OFF, 0x10);
 
		} else {
 
			DrawString(118, 113 + y, num_towns[_opt_newgame.diff.number_towns], 0x10);
 
			DrawString(118, 131 + y, num_inds[_opt_newgame.diff.number_industries], 0x10);
 
		}
 

	
 
		DrawString( 12, 153 + y, STR_RANDOM_SEED, 0);
 
		DrawEditBox(w, &_genseed_query, SEED_EDIT);
 

	
 
		DrawString(182, 113 + y, STR_DATE, 0);
 
		SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
 
		DrawStringCentered(271, 113 + y, STR_GENERATE_DATE, 0);
 

	
 
		DrawString(182, 131 + y, STR_SNOW_LINE_HEIGHT, 0);
 
		SetDParam(0, _patches_newgame.snow_line_height);
 
		DrawStringCentered(303, 131 + y, STR_SNOW_LINE_HEIGHT_NUM, 0x10);
 

	
 
		if (mode == GLWP_GENERATE) {
 
			DrawString( 12, 175, STR_LAND_GENERATOR, 0);
 
			DrawString(118, 175, landscape[_patches_newgame.land_generator], 0x10);
 

	
 
			DrawString( 12, 193, STR_TREE_PLACER, 0);
 
			DrawString(118, 193, tree_placer[_patches_newgame.tree_placer], 0x10);
 

	
 
			DrawString( 12, 211, STR_TERRAIN_TYPE, 0);
 
			DrawString(118, 211, elevations[_opt_newgame.diff.terrain_type], 0x10);
 

	
 
			DrawString( 12, 229, STR_QUANTITY_OF_SEA_LAKES, 0);
 
			DrawString(118, 229, sea_lakes[_opt_newgame.diff.quantity_sea_lakes], 0x10);
 

	
 
			DrawString( 12, 247, STR_SMOOTHNESS, 0);
 
			DrawString(118, 247, smoothness[_patches_newgame.tgen_smoothness], 0x10);
 
		} else {
 
			char buffer[512];
 

	
 
			if (_patches_newgame.heightmap_rotation == HM_CLOCKWISE) {
 
				SetDParam(0, _heightmap_y);
 
				SetDParam(1, _heightmap_x);
 
			} else {
 
				SetDParam(0, _heightmap_x);
 
				SetDParam(1, _heightmap_y);
 
			}
 
			GetString(buffer, STR_HEIGHTMAP_SIZE, lastof(buffer));
 
			DrawStringRightAligned(326, 91, STR_HEIGHTMAP_SIZE, 0x10);
 

	
 
			DrawString( 12,  91, STR_HEIGHTMAP_NAME, 0x10);
 
			SetDParam(0, _heightmap_str);
 
			DrawStringTruncated(114,  91, STR_ORANGE, 0x10, 326 - 114 - GetStringBoundingBox(buffer).width - 5);
 

	
 
			DrawString( 12, 197, STR_TREE_PLACER, 0);
 
			DrawString(118, 197, tree_placer[_patches_newgame.tree_placer], 0x10);
 

	
 
			DrawString( 12, 215, STR_HEIGHTMAP_ROTATION, 0);
 
			DrawString(118, 215, rotation[_patches_newgame.heightmap_rotation], 0x10);
 
		}
 

	
 
		break;
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 0: DeleteWindow(w); break;
 
		case 3: case 4: case 5: case 6:
 
			RaiseWindowWidget(w, _opt_newgame.landscape + 3);
 
			SetNewLandscapeType(e->we.click.widget - 3);
 
			break;
 
		case 7: case 8: // Mapsize X
 
			ShowDropDownMenu(w, mapsizes, _patches_newgame.map_x - 6, 8, 0, 0);
 
			break;
 
		case 9: case 10: // Mapsize Y
 
			ShowDropDownMenu(w, mapsizes, _patches_newgame.map_y - 6, 10, 0, 0);
 
			break;
 
		case 11: case 12: // Number of towns
 
			ShowDropDownMenu(w, num_towns, _opt_newgame.diff.number_towns, 12, 0, 0);
 
			break;
 
		case 13: case 14: // Number of industries
 
			ShowDropDownMenu(w, num_inds, _opt_newgame.diff.number_industries, 14, 0, 0);
 
			break;
 
		case 16: // Random seed
 
			_patches_newgame.generation_seed = InteractiveRandom();
 
			snprintf(_genseed_buffer, lengthof(_genseed_buffer), "%u", _patches_newgame.generation_seed);
 
			UpdateTextBufferSize(&_genseed_query.text);
 
			SetWindowDirty(w);
 
			break;
 
		case 17: // Generate
 
			if (mode == GLWP_HEIGHTMAP && (
 
					_heightmap_x * 2 < (1U << _patches_newgame.map_x) || _heightmap_x / 2 > (1U << _patches_newgame.map_x) ||
 
					_heightmap_y * 2 < (1U << _patches_newgame.map_y) || _heightmap_y / 2 > (1U << _patches_newgame.map_y))) {
 
				ShowQuery(
 
					STR_HEIGHTMAP_SCALE_WARNING_CAPTION,
 
					STR_HEIGHTMAP_SCALE_WARNING_MESSAGE,
 
					w,
 
					HeightmapScaledTooMuchCallback);
 
			} else {
 
				StartGeneratingLandscape(mode);
 
			}
 
			break;
 
		case 18: case 20: // Year buttons
 
			/* Don't allow too fast scrolling */
 
			if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
 
				HandleButtonClick(w, e->we.click.widget);
 
				SetWindowDirty(w);
 

	
 
				_patches_newgame.starting_year = clamp(_patches_newgame.starting_year + e->we.click.widget - 19, MIN_YEAR, MAX_YEAR);
 
			}
 
			_left_button_clicked = false;
 
			break;
 
		case 19: // Year text
 
			WP(w, def_d).data_3 = START_DATE_QUERY;
 
			SetDParam(0, _patches_newgame.starting_year);
 
			ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, w, CS_NUMERAL);
 
			break;
 
		case 21: case 23: // Snow line buttons
 
			/* Don't allow too fast scrolling */
 
			if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
 
				HandleButtonClick(w, e->we.click.widget);
 
				SetWindowDirty(w);
 

	
 
				_patches_newgame.snow_line_height = clamp(_patches_newgame.snow_line_height + e->we.click.widget - 22, 2, 13);
 
			}
 
			_left_button_clicked = false;
 
			break;
 
		case 22: // Snow line text
 
			WP(w, def_d).data_3 = SNOW_LINE_QUERY;
 
			SetDParam(0, _patches_newgame.snow_line_height);
 
			ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_SNOW_LINE_QUERY_CAPT, 3, 100, w, CS_NUMERAL);
 
			break;
 
		case 24: case 25: // Tree placer
 
			ShowDropDownMenu(w, tree_placer, _patches_newgame.tree_placer, 25, 0, 0);
 
			break;
 
		case 26: case 27: // Landscape generator OR Heightmap rotation
 
			if (mode == GLWP_HEIGHTMAP) {
 
				ShowDropDownMenu(w, rotation, _patches_newgame.heightmap_rotation, 27, 0, 0);
 
			} else {
 
				ShowDropDownMenu(w, landscape, _patches_newgame.land_generator, 27, 0, 0);
 
			}
 
			break;
 
		case 28: case 29: // Terrain type
 
			ShowDropDownMenu(w, elevations, _opt_newgame.diff.terrain_type, 29, 0, 0);
 
			break;
 
		case 30: case 31: // Water quantity
 
			ShowDropDownMenu(w, sea_lakes, _opt_newgame.diff.quantity_sea_lakes, 31, 0, 0);
 
			break;
 
		case 32: case 33: // Map smoothness
 
			ShowDropDownMenu(w, smoothness, _patches_newgame.tgen_smoothness, 33, 0, 0);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		HandleEditBox(w, &_genseed_query, SEED_EDIT);
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		HandleEditBoxKey(w, &_genseed_query, SEED_EDIT, e);
 
		/* the seed is unsigned, therefore atoi cannot be used.
 
		 * As 2^32 - 1 (MAX_UVALUE(uint32)) is a 'magic' value
 
		 * (use random seed) it should not be possible to be
 
		 * entered into the input field; the generate seed
 
		 * button can be used instead. */
 
		_patches_newgame.generation_seed = minu(strtoul(_genseed_buffer, NULL, sizeof(_genseed_buffer) - 1), MAX_UVALUE(uint32) - 1);
 
		break;
 

	
 
	case WE_DROPDOWN_SELECT:
 
		switch (e->we.dropdown.button) {
 
			case 8:  _patches_newgame.map_x = e->we.dropdown.index + 6; break;
 
			case 10: _patches_newgame.map_y = e->we.dropdown.index + 6; break;
 
			case 12:
 
				_opt_newgame.diff.number_towns = e->we.dropdown.index;
 
				if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
 
				DoCommandP(0, 2, _opt_newgame.diff.number_towns, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
 
				break;
 
			case 14:
 
				_opt_newgame.diff.number_industries = e->we.dropdown.index;
 
				if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
 
				DoCommandP(0, 3, _opt_newgame.diff.number_industries, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
 
				break;
 
			case 25:
 
				_patches_newgame.tree_placer = e->we.dropdown.index;
 
				break;
 
			case 27:
 
				if (mode == GLWP_HEIGHTMAP) {
 
					_patches_newgame.heightmap_rotation = e->we.dropdown.index;
 
				} else {
 
					_patches_newgame.land_generator = e->we.dropdown.index;
 
				}
 
				break;
 
			case 29:
 
				_opt_newgame.diff.terrain_type = e->we.dropdown.index;
 
				if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
 
				DoCommandP(0, 12, _opt_newgame.diff.terrain_type, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
 
				break;
 
			case 31:
 
				_opt_newgame.diff.quantity_sea_lakes = e->we.dropdown.index;
 
				if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
 
				DoCommandP(0, 13, _opt_newgame.diff.quantity_sea_lakes, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
 
				break;
 
			case 33:
 
				_patches_newgame.tgen_smoothness = e->we.dropdown.index;
 
				break;
 
		}
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
		if (e->we.edittext.str != NULL) {
 
			int32 value = atoi(e->we.edittext.str);
 

	
 
			switch (WP(w, def_d).data_3) {
 
			case START_DATE_QUERY:
 
				InvalidateWidget(w, 19);
 
				_patches_newgame.starting_year = clamp(value, MIN_YEAR, MAX_YEAR);
 
				break;
 
			case SNOW_LINE_QUERY:
 
				InvalidateWidget(w, 22);
 
				_patches_newgame.snow_line_height = clamp(value, 2, 13);
 
				break;
 
			}
 

	
 
			SetWindowDirty(w);
 
		}
 
		break;
 
	}
 
	}
 
}
 

	
 
const WindowDesc _generate_landscape_desc = {
 
	WDP_CENTER, WDP_CENTER, 338, 268,
 
	WC_GENERATE_LANDSCAPE, 0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_generate_landscape_widgets,
 
	GenerateLandscapeWndProc,
 
};
 

	
 
const WindowDesc _heightmap_load_desc = {
 
	WDP_CENTER, WDP_CENTER, 338, 236,
 
	WC_GENERATE_LANDSCAPE, 0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_heightmap_load_widgets,
 
	GenerateLandscapeWndProc,
 
};
 

	
 
static void _ShowGenerateLandscape(glwp_modes mode)
 
{
 
	Window *w;
 

	
 
	/* Don't kill WC_GENERATE_LANDSCAPE:GLWP_SCENARIO, because it resets
 
	 *  _goto_editor, which we maybe need later on. */
 
	DeleteWindowById(WC_GENERATE_LANDSCAPE, GLWP_GENERATE);
 
	DeleteWindowById(WC_GENERATE_LANDSCAPE, GLWP_HEIGHTMAP);
 

	
 
	/* Always give a new seed if not editor */
 
	if (_game_mode != GM_EDITOR) _patches_newgame.generation_seed = InteractiveRandom();
 

	
 
	if (mode == GLWP_HEIGHTMAP) {
 
		if (_heightmap_str != STR_NULL) DeleteName(_heightmap_str);
 

	
 
		_heightmap_x = 0;
 
		_heightmap_y = 0;
 
		_heightmap_str = AllocateName(_file_to_saveload.title, 0);
 
		/* If the function returns negative, it means there was a problem loading the heightmap */
 
		if (!GetHeightmapDimensions(_file_to_saveload.name, &_heightmap_x, &_heightmap_y))
 
			return;
 
	}
 

	
 
	w = AllocateWindowDescFront((mode == GLWP_HEIGHTMAP) ? &_heightmap_load_desc : &_generate_landscape_desc, mode);
 

	
 
	if (w != NULL) {
 

	
 
		InvalidateWindow(WC_GENERATE_LANDSCAPE, mode);
 
	}
 
}
 

	
 
void ShowGenerateLandscape(void)
 
{
 
	_ShowGenerateLandscape(GLWP_GENERATE);
 
}
 

	
 
void ShowHeightmapLoad(void)
 
{
 
	_ShowGenerateLandscape(GLWP_HEIGHTMAP);
 
}
 

	
 
void StartNewGameWithoutGUI(uint seed)
 
{
 
	/* GenerateWorld takes care of the possible GENERATE_NEW_SEED value in 'seed' */
 
	_patches_newgame.generation_seed = seed;
 

	
 
	StartGeneratingLandscape(GLWP_GENERATE);
 
}
 

	
 

	
 
void CreateScenarioWndProc(Window *w, WindowEvent *e)
 
{
 
	static const StringID mapsizes[] = {STR_64, STR_128, STR_256, STR_512, STR_1024, STR_2048, INVALID_STRING_ID};
 

	
 
	switch (e->event) {
 
	case WE_CREATE: LowerWindowWidget(w, _opt_newgame.landscape + 3); break;
 

	
 
	case WE_PAINT:
 
		SetWindowWidgetDisabledState(w, 14, _patches_newgame.starting_year <= MIN_YEAR);
 
		SetWindowWidgetDisabledState(w, 16, _patches_newgame.starting_year >= MAX_YEAR);
 
		SetWindowWidgetDisabledState(w, 17, _patches_newgame.se_flat_world_height <= 0);
 
		SetWindowWidgetDisabledState(w, 19, _patches_newgame.se_flat_world_height >= 15);
 

	
 
		SetWindowWidgetLoweredState(w, 3, _opt_newgame.landscape == LT_NORMAL);
 
		SetWindowWidgetLoweredState(w, 4, _opt_newgame.landscape == LT_HILLY);
 
		SetWindowWidgetLoweredState(w, 5, _opt_newgame.landscape == LT_DESERT);
 
		SetWindowWidgetLoweredState(w, 6, _opt_newgame.landscape == LT_CANDY);
 
		DrawWindowWidgets(w);
 

	
 
		DrawString( 12,  96, STR_MAPSIZE, 0);
 
		DrawString(167,  96, mapsizes[_patches_newgame.map_x - 6], 0x10);
 
		DrawString(216,  96, STR_BY, 0);
 
		DrawString(230,  96, mapsizes[_patches_newgame.map_y - 6], 0x10);
 

	
 
		DrawString(162, 118, STR_DATE, 0);
 
		SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
 
		DrawStringCentered(271, 118, STR_GENERATE_DATE, 0);
 

	
 
		DrawString(162, 136, STR_FLAT_WORLD_HEIGHT, 0);
 
		SetDParam(0, _patches_newgame.se_flat_world_height);
 
		DrawStringCentered(303, 136, STR_FLAT_WORLD_HEIGHT_NUM, 0x10);
 

	
 
		break;
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 0: DeleteWindow(w); break;
 
		case 3: case 4: case 5: case 6:
 
			RaiseWindowWidget(w, _opt_newgame.landscape + 3);
 
			SetNewLandscapeType(e->we.click.widget - 3);
 
			break;
 
		case 7: case 8: // Mapsize X
 
			ShowDropDownMenu(w, mapsizes, _patches_newgame.map_x - 6, 8, 0, 0);
 
			break;
 
		case 9: case 10: // Mapsize Y
 
			ShowDropDownMenu(w, mapsizes, _patches_newgame.map_y - 6, 10, 0, 0);
 
			break;
 
		case 11: // Empty world / flat world
 
			StartGeneratingLandscape(GLWP_SCENARIO);
 
			break;
 
		case 12: // Generate
 
			_goto_editor = true;
 
			ShowGenerateLandscape();
 
			break;
 
		case 13: // Heightmap
 
			_goto_editor = true;
 
			ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP);
 
			break;
 
		case 14: case 16: // Year buttons
 
			/* Don't allow too fast scrolling */
 
			if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
 
				HandleButtonClick(w, e->we.click.widget);
 
				SetWindowDirty(w);
 

	
 
				_patches_newgame.starting_year = clamp(_patches_newgame.starting_year + e->we.click.widget - 15, MIN_YEAR, MAX_YEAR);
 
			}
 
			_left_button_clicked = false;
 
			break;
 
		case 15: // Year text
 
			WP(w, def_d).data_3 = START_DATE_QUERY;
 
			SetDParam(0, _patches_newgame.starting_year);
 
			ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, w, CS_NUMERAL);
 
			break;
 
		case 17: case 19: // Height level buttons
 
			/* Don't allow too fast scrolling */
 
			if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
 
				HandleButtonClick(w, e->we.click.widget);
 
				SetWindowDirty(w);
 

	
 
				_patches_newgame.se_flat_world_height = clamp(_patches_newgame.se_flat_world_height + e->we.click.widget - 18, 0, 15);
 
			}
 
			_left_button_clicked = false;
 
			break;
 
		case 18: // Height level text
 
			WP(w, def_d).data_3 = FLAT_WORLD_HEIGHT_QUERY;
 
			SetDParam(0, _patches_newgame.se_flat_world_height);
 
			ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_FLAT_WORLD_HEIGHT_QUERY_CAPT, 3, 100, w, CS_NUMERAL);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_DROPDOWN_SELECT:
 
		switch (e->we.dropdown.button) {
 
			case 8:  _patches_newgame.map_x = e->we.dropdown.index + 6; break;
 
			case 10: _patches_newgame.map_y = e->we.dropdown.index + 6; break;
 
		}
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_DESTROY:
 
		_goto_editor = false;
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
		if (e->we.edittext.str != NULL) {
 
			int32 value = atoi(e->we.edittext.str);
 

	
 
			switch (WP(w, def_d).data_3) {
 
			case START_DATE_QUERY:
 
				InvalidateWidget(w, 15);
 
				_patches_newgame.starting_year = clamp(value, MIN_YEAR, MAX_YEAR);
 
				break;
 
			case FLAT_WORLD_HEIGHT_QUERY:
 
				InvalidateWidget(w, 18);
 
				_patches_newgame.se_flat_world_height = clamp(value, 0, 15);
 
				break;
 
			}
 

	
 
			SetWindowDirty(w);
 
		}
 
		break;
 
	}
 
	}
 
}
 

	
 
const Widget _create_scenario_widgets[] = {
 
{   WWT_CLOSEBOX, RESIZE_NONE, 13,   0,  10,   0,  13, STR_00C5,                STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION, RESIZE_NONE, 13,  11, 337,   0,  13, STR_SE_CAPTION,          STR_NULL},
 
{      WWT_PANEL, RESIZE_NONE, 13,   0, 337,  14, 179, 0x0,                     STR_NULL},
 

	
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12,  10,  86,  24,  78, SPR_SELECT_TEMPERATE,    STR_030E_SELECT_TEMPERATE_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12,  90, 166,  24,  78, SPR_SELECT_SUB_ARCTIC,   STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12, 170, 246,  24,  78, SPR_SELECT_SUB_TROPICAL, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12, 250, 326,  24,  78, SPR_SELECT_TOYLAND,      STR_0311_SELECT_TOYLAND_LANDSCAPE},
 

	
 
{      WWT_PANEL, RESIZE_NONE, 12, 162, 197,  95, 106, 0x0,                     STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 198, 209,  95, 106, STR_0225,                STR_NULL}, // Mapsize X
 
{      WWT_PANEL, RESIZE_NONE, 12, 228, 263,  95, 106, 0x0,                     STR_NULL},
 
{    WWT_TEXTBTN, RESIZE_NONE, 12, 264, 275,  95, 106, STR_0225,                STR_NULL}, // Mapsize Y
 

	
 
{    WWT_TEXTBTN, RESIZE_NONE,  6,  12, 145, 117, 128, STR_SE_FLAT_WORLD,       STR_SE_FLAT_WORLD_TIP},                      // Empty (sea-level) map
 
{    WWT_TEXTBTN, RESIZE_NONE,  6,  12, 145, 135, 146, STR_SE_RANDOM_LAND,      STR_022A_GENERATE_RANDOM_LAND}, // Generate
 
{    WWT_TEXTBTN, RESIZE_NONE,  6,  12, 145, 153, 164, STR_LOAD_GAME_HEIGHTMAP, STR_LOAD_SCEN_HEIGHTMAP},       // Heightmap
 

	
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 216, 227, 117, 128, SPR_ARROW_DOWN,          STR_029E_MOVE_THE_STARTING_DATE},
 
{      WWT_PANEL, RESIZE_NONE, 12, 228, 314, 117, 128, 0x0,                     STR_NULL},
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 117, 128, SPR_ARROW_UP,            STR_029F_MOVE_THE_STARTING_DATE},
 

	
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 282, 293, 135, 146, SPR_ARROW_DOWN,          STR_FLAT_WORLD_HEIGHT_DOWN},
 
{      WWT_PANEL, RESIZE_NONE, 12, 294, 314, 135, 146, 0x0,                     STR_NULL},
 
{     WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 135, 146, SPR_ARROW_UP,            STR_FLAT_WORLD_HEIGHT_UP},
 
{   WIDGETS_END},
 
};
 

	
 
const WindowDesc _create_scenario_desc = {
 
	WDP_CENTER, WDP_CENTER, 338, 180,
 
	WC_GENERATE_LANDSCAPE, 0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_create_scenario_widgets,
 
	CreateScenarioWndProc,
 
};
 

	
 
void ShowCreateScenario(void)
 
{
 
	DeleteWindowByClass(WC_GENERATE_LANDSCAPE);
 
	AllocateWindowDescFront(&_create_scenario_desc, GLWP_SCENARIO);
 
}
 

	
 

	
 
static const Widget _show_terrain_progress_widgets[] = {
 
{    WWT_CAPTION,   RESIZE_NONE,    14,     0,   180,     0,    13, STR_GENERATION_WORLD,   STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   180,    14,    96, 0x0,                    STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    15,    20,   161,    74,    85, STR_GENERATION_ABORT,   STR_NULL}, // Abort button
 
{   WIDGETS_END},
 
};
 

	
 
typedef struct tp_info {
 
	uint percent;
 
	StringID class;
 
	uint current;
 
	uint total;
 
	int timer;
 
} tp_info;
 

	
 
static tp_info _tp;
 

	
 
static void AbortGeneratingWorldCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) {
 
		AbortGeneratingWorld();
 
	} else if (IsGeneratingWorld() && !IsGeneratingWorldAborted()) {
 
		SetMouseCursor(SPR_CURSOR_ZZZ);
 
	}
 
}
 

	
 
static void ShowTerrainProgressProc(Window* w, WindowEvent* e)
 
{
 
	switch (e->event) {
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 2:
 
			if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
 
			ShowQuery(
 
				STR_GENERATION_ABORT_CAPTION,
 
				STR_GENERATION_ABORT_MESSAGE,
 
				w,
 
				AbortGeneratingWorldCallback
 
			);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 

	
 
		/* Draw the % complete with a bar and a text */
 
		DrawFrameRect(19, 20, (w->width - 18), 37, 14, FR_BORDERONLY);
 
		DrawFrameRect(20, 21, (int)((w->width - 40) * _tp.percent / 100) + 20, 36, 10, 0);
 
		SetDParam(0, _tp.percent);
 
		DrawStringCentered(90, 25, STR_PROGRESS, 0);
 

	
 
		/* Tell which class we are generating */
 
		DrawStringCentered(90, 46, _tp.class, 0);
 

	
 
		/* And say where we are in that class */
 
		SetDParam(0, _tp.current);
 
		SetDParam(1, _tp.total);
 
		DrawStringCentered(90, 58, STR_GENERATION_PROGRESS, 0);
 

	
 
		SetWindowDirty(w);
 
		break;
 
	}
 
}
 

	
 
static const WindowDesc _show_terrain_progress_desc = {
 
	WDP_CENTER, WDP_CENTER, 181, 97,
 
	WC_GENERATE_PROGRESS_WINDOW, 0,
 
	WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_show_terrain_progress_widgets,
 
	ShowTerrainProgressProc
 
};
 

	
 
/**
 
 * Initializes the progress counters to the starting point.
 
 */
 
void PrepareGenerateWorldProgress(void)
 
{
 
	_tp.class   = STR_WORLD_GENERATION;
 
	_tp.current = 0;
 
	_tp.total   = 0;
 
	_tp.percent = 0;
 
	_tp.timer   = 0; // Forces to paint the progress window immediatelly
 
}
 

	
 
/**
 
 * Show the window where a user can follow the process of the map generation.
 
 */
 
void ShowGenerateWorldProgress(void)
 
{
 
	AllocateWindowDescFront(&_show_terrain_progress_desc, 0);
 
}
 

	
 
static void _SetGeneratingWorldProgress(gwp_class class, uint progress, uint total)
 
{
 
	static const int percent_table[GWP_CLASS_COUNT + 1] = {0, 5, 15, 20, 40, 60, 65, 80, 85, 99, 100 };
 
	static const StringID class_table[GWP_CLASS_COUNT]  = {
 
		STR_WORLD_GENERATION,
 
		STR_022E_LANDSCAPE_GENERATION,
 
		STR_CLEARING_TILES,
 
		STR_022F_TOWN_GENERATION,
 
		STR_0230_INDUSTRY_GENERATION,
 
		STR_UNMOVABLE_GENERATION,
 
		STR_TREE_GENERATION,
 
		STR_SETTINGUP_GAME,
 
		STR_PREPARING_TILELOOP,
 
		STR_PREPARING_GAME
 
	};
 

	
 
	assert(class < GWP_CLASS_COUNT);
 

	
 
	/* Do not run this function if we aren't in a thread */
 
	if (!IsGenerateWorldThreaded() && !_network_dedicated) return;
 

	
 
	if (IsGeneratingWorldAborted()) HandleGeneratingWorldAbortion();
 

	
 
	if (total == 0) {
 
		assert(_tp.class == class_table[class]);
 
		_tp.current += progress;
 
	} else {
 
		_tp.class   = class_table[class];
 
		_tp.current = progress;
 
		_tp.total   = total;
 
		_tp.percent = percent_table[class];
 
	}
 

	
 
	/* Don't update the screen too often. So update it once in every 200ms.
 
	 * However, the _tick_counter increases by 8 every 30ms, so compensate
 
	 * for that. */
 
	if (!_network_dedicated && _tp.timer != 0 && _timer_counter - _tp.timer < (200 * 8 / 30)) return;
 

	
 
	/* Percentage is about the number of completed tasks, so 'current - 1' */
 
	_tp.percent = percent_table[class] + (percent_table[class + 1] - percent_table[class]) * (_tp.current == 0 ? 0 : _tp.current - 1) / _tp.total;
 
	_tp.timer = _timer_counter;
 

	
 
	if (_network_dedicated) {
 
		static uint last_percent = 0;
 

	
 
		/* Never display 0% */
 
		if (_tp.percent == 0) return;
 
		/* Reset if percent is lower then the last recorded */
 
		if (_tp.percent < last_percent) last_percent = 0;
 
		/* Display every 5%, but 6% is also very valid.. just not smaller steps then 5% */
 
		if (_tp.percent % 5 != 0 && _tp.percent <= last_percent + 5) return;
 
		/* Never show steps smaller then 2%, even if it is a mod 5% */
 
		if (_tp.percent <= last_percent + 2) return;
 

	
 
		DEBUG(net, 1, "Map generation percentage complete: %d", _tp.percent);
 
		last_percent = _tp.percent;
 

	
 
		/* Don't continue as dedicated never has a thread running */
 
		return;
 
	}
 

	
 
	InvalidateWindow(WC_GENERATE_PROGRESS_WINDOW, 0);
 
	MarkWholeScreenDirty();
 
	SetGeneratingWorldPaintStatus(true);
 

	
 
	/* We wait here till the paint is done, so we don't read and write
 
	 *  on the same tile at the same moment. Nasty hack, but that happens
 
	 *  if you implement threading afterwards */
 
	while (IsGeneratingWorldReadyForPaint()) { CSleep(10); }
 
}
 

	
 
/**
 
 * Set the total of a stage of the world generation.
 
 * @param class the current class we are in.
 
 * @param total Set the total expected items for this class.
 
 *
 
 * Warning: this function isn't clever. Don't go from class 4 to 3. Go upwards, always.
 
 *  Also, progress works if total is zero, total works if progress is zero.
 
 */
 
void SetGeneratingWorldProgress(gwp_class class, uint total)
 
{
 
	if (total == 0) return;
 

	
 
	_SetGeneratingWorldProgress(class, 0, total);
 
}
 

	
 
/**
 
 * Increases the current stage of the world generation with one.
 
 * @param class the current class we are in.
 
 *
 
 * Warning: this function isn't clever. Don't go from class 4 to 3. Go upwards, always.
 
 *  Also, progress works if total is zero, total works if progress is zero.
 
 */
 
void IncreaseGeneratingWorldProgress(gwp_class class)
 
{
 
	/* In fact the param 'class' isn't needed.. but for some security reasons, we want it around */
 
	_SetGeneratingWorldProgress(class, 1, 0);
 
}
src/gfx.c
Show inline comments
 
deleted file
src/gfx.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "macros.h"
 
#include "spritecache.h"
 
#include "strings.h"
 
#include "string.h"
 
#include "gfx.h"
 
#include "table/palettes.h"
 
#include "table/sprites.h"
 
#include "hal.h"
 
#include "variables.h"
 
#include "table/control_codes.h"
 
#include "fontcache.h"
 
#include "genworld.h"
 
#include "debug.h"
 

	
 
#ifdef _DEBUG
 
bool _dbg_screen_rect;
 
#endif
 

	
 
Colour _cur_palette[256];
 
byte _stringwidth_table[FS_END][224];
 

	
 
typedef enum BlitterModes {
 
	BM_NORMAL,
 
	BM_COLOUR_REMAP,
 
	BM_TRANSPARENT,
 
} BlitterMode;
 

	
 
static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode);
 

	
 
FontSize _cur_fontsize;
 
static FontSize _last_fontsize;
 
static Pixel _cursor_backup[64 * 64];
 
static Rect _invalid_rect;
 
static const byte *_color_remap_ptr;
 
static byte _string_colorremap[3];
 

	
 
#define DIRTY_BYTES_PER_LINE (MAX_SCREEN_WIDTH / 64)
 
static byte _dirty_blocks[DIRTY_BYTES_PER_LINE * MAX_SCREEN_HEIGHT / 8];
 

	
 
void memcpy_pitch(void *dst, void *src, int w, int h, int srcpitch, int dstpitch)
 
{
 
	byte *dstp = (byte*)dst;
 
	byte *srcp = (byte*)src;
 

	
 
	assert(h >= 0);
 
	for (; h != 0; --h) {
 
		memcpy(dstp, srcp, w);
 
		dstp += dstpitch;
 
		srcp += srcpitch;
 
	}
 
}
 

	
 
void GfxScroll(int left, int top, int width, int height, int xo, int yo)
 
{
 
	const Pixel *src;
 
	Pixel *dst;
 
	int p;
 
	int ht;
 

	
 
	if (xo == 0 && yo == 0) return;
 

	
 
	if (_cursor.visible) UndrawMouseCursor();
 
	UndrawTextMessage();
 

	
 
	p = _screen.pitch;
 

	
 
	if (yo > 0) {
 
		// Calculate pointers
 
		dst = _screen.dst_ptr + (top + height - 1) * p + left;
 
		src = dst - yo * p;
 

	
 
		// Decrease height and increase top
 
		top += yo;
 
		height -= yo;
 
		assert(height > 0);
 

	
 
		// Adjust left & width
 
		if (xo >= 0) {
 
			dst += xo;
 
			left += xo;
 
			width -= xo;
 
		} else {
 
			src -= xo;
 
			width += xo;
 
		}
 

	
 
		for (ht = height; ht > 0; --ht) {
 
			memcpy(dst, src, width);
 
			src -= p;
 
			dst -= p;
 
		}
 
	} else {
 
		// Calculate pointers
 
		dst = _screen.dst_ptr + top * p + left;
 
		src = dst - yo * p;
 

	
 
		// Decrese height. (yo is <=0).
 
		height += yo;
 
		assert(height > 0);
 

	
 
		// Adjust left & width
 
		if (xo >= 0) {
 
			dst += xo;
 
			left += xo;
 
			width -= xo;
 
		} else {
 
			src -= xo;
 
			width += xo;
 
		}
 

	
 
		// the y-displacement may be 0 therefore we have to use memmove,
 
		// because source and destination may overlap
 
		for (ht = height; ht > 0; --ht) {
 
			memmove(dst, src, width);
 
			src += p;
 
			dst += p;
 
		}
 
	}
 
	// This part of the screen is now dirty.
 
	_video_driver->make_dirty(left, top, width, height);
 
}
 

	
 

	
 
void GfxFillRect(int left, int top, int right, int bottom, int color)
 
{
 
	const DrawPixelInfo* dpi = _cur_dpi;
 
	Pixel *dst;
 
	const int otop = top;
 
	const int oleft = left;
 

	
 
	if (dpi->zoom != 0) return;
 
	if (left > right || top > bottom) return;
 
	if (right < dpi->left || left >= dpi->left + dpi->width) return;
 
	if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
 

	
 
	if ( (left -= dpi->left) < 0) left = 0;
 
	right = right - dpi->left + 1;
 
	if (right > dpi->width) right = dpi->width;
 
	right -= left;
 
	assert(right > 0);
 

	
 
	if ( (top -= dpi->top) < 0) top = 0;
 
	bottom = bottom - dpi->top + 1;
 
	if (bottom > dpi->height) bottom = dpi->height;
 
	bottom -= top;
 
	assert(bottom > 0);
 

	
 
	dst = dpi->dst_ptr + top * dpi->pitch + left;
 

	
 
	if (!(color & PALETTE_MODIFIER_GREYOUT)) {
 
		if (!(color & USE_COLORTABLE)) {
 
			do {
 
				memset(dst, color, right);
 
				dst += dpi->pitch;
 
			} while (--bottom);
 
		} else {
 
			/* use colortable mode */
 
			const byte* ctab = GetNonSprite(color & COLORTABLE_MASK) + 1;
 

	
 
			do {
 
				int i;
 
				for (i = 0; i != right; i++) dst[i] = ctab[dst[i]];
 
				dst += dpi->pitch;
 
			} while (--bottom);
 
		}
 
	} else {
 
		byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
 
		do {
 
			int i;
 
			for (i = (bo ^= 1); i < right; i += 2) dst[i] = (byte)color;
 
			dst += dpi->pitch;
 
		} while (--bottom > 0);
 
	}
 
}
 

	
 
static void GfxSetPixel(int x, int y, int color)
 
{
 
	const DrawPixelInfo* dpi = _cur_dpi;
 
	if ((x-=dpi->left) < 0 || x>=dpi->width || (y-=dpi->top)<0 || y>=dpi->height)
 
		return;
 
	dpi->dst_ptr[y * dpi->pitch + x] = color;
 
}
 

	
 
void GfxDrawLine(int x, int y, int x2, int y2, int color)
 
{
 
	int dy;
 
	int dx;
 
	int stepx;
 
	int stepy;
 
	int frac;
 

	
 
	// Check clipping first
 
	{
 
		DrawPixelInfo *dpi = _cur_dpi;
 
		int t;
 

	
 
		if (x < dpi->left && x2 < dpi->left) return;
 

	
 
		if (y < dpi->top && y2 < dpi->top) return;
 

	
 
		t = dpi->left + dpi->width;
 
		if (x > t && x2 > t) return;
 

	
 
		t = dpi->top + dpi->height;
 
		if (y > t && y2 > t) return;
 
	}
 

	
 
	dy = (y2 - y) * 2;
 
	if (dy < 0) {
 
		dy = -dy;
 
		stepy = -1;
 
	} else {
 
		stepy = 1;
 
	}
 

	
 
	dx = (x2 - x) * 2;
 
	if (dx < 0) {
 
		dx = -dx;
 
		stepx = -1;
 
	} else {
 
		stepx = 1;
 
	}
 

	
 
	GfxSetPixel(x, y, color);
 
	if (dx > dy) {
 
		frac = dy - (dx >> 1);
 
		while (x != x2) {
 
			if (frac >= 0) {
 
				y += stepy;
 
				frac -= dx;
 
			}
 
			x += stepx;
 
			frac += dy;
 
			GfxSetPixel(x, y, color);
 
		}
 
	} else {
 
		frac = dx - (dy >> 1);
 
		while (y != y2) {
 
			if (frac >= 0) {
 
				x += stepx;
 
				frac -= dy;
 
			}
 
			y += stepy;
 
			frac += dx;
 
			GfxSetPixel(x, y, color);
 
		}
 
	}
 
}
 

	
 

	
 
/** Truncate a given string to a maximum width if neccessary.
 
 * If the string is truncated, add three dots ('...') to show this.
 
 * @param *dest string that is checked and possibly truncated
 
 * @param maxw maximum width in pixels of the string
 
 * @return new width of (truncated) string */
 
static int TruncateString(char *str, int maxw)
 
{
 
	int w = 0;
 
	FontSize size = _cur_fontsize;
 
	int ddd, ddd_w;
 

	
 
	WChar c;
 
	char *ddd_pos;
 

	
 
	ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
 

	
 
	for (ddd_pos = str; (c = Utf8Consume((const char **)&str)) != '\0'; ) {
 
		if (IsPrintable(c)) {
 
			w += GetCharacterWidth(size, c);
 

	
 
			if (w >= maxw) {
 
				// string got too big... insert dotdotdot
 
				ddd_pos[0] = ddd_pos[1] = ddd_pos[2] = '.';
 
				ddd_pos[3] = 0;
 
				return ddd_w;
 
			}
 
		} else {
 
			if (c == SCC_SETX) str++;
 
			else if (c == SCC_SETXY) str += 2;
 
			else if (c == SCC_TINYFONT) {
 
				size = FS_SMALL;
 
				ddd = GetCharacterWidth(size, '.') * 3;
 
			} else if (c == SCC_BIGFONT) {
 
				size = FS_LARGE;
 
				ddd = GetCharacterWidth(size, '.') * 3;
 
			}
 
		}
 

	
 
		// Remember the last position where three dots fit.
 
		if (w + ddd < maxw) {
 
			ddd_w = w + ddd;
 
			ddd_pos = str;
 
		}
 
	}
 

	
 
	return w;
 
}
 

	
 
static inline int TruncateStringID(StringID src, char *dest, int maxw, const char* last)
 
{
 
	GetString(dest, src, last);
 
	return TruncateString(dest, maxw);
 
}
 

	
 
/* returns right coordinate */
 
int DrawString(int x, int y, StringID str, uint16 color)
 
{
 
	char buffer[512];
 

	
 
	GetString(buffer, str, lastof(buffer));
 
	return DoDrawString(buffer, x, y, color);
 
}
 

	
 
int DrawStringTruncated(int x, int y, StringID str, uint16 color, uint maxw)
 
{
 
	char buffer[512];
 
	TruncateStringID(str, buffer, maxw, lastof(buffer));
 
	return DoDrawString(buffer, x, y, color);
 
}
 

	
 

	
 
int DrawStringRightAligned(int x, int y, StringID str, uint16 color)
 
{
 
	char buffer[512];
 
	int w;
 

	
 
	GetString(buffer, str, lastof(buffer));
 
	w = GetStringBoundingBox(buffer).width;
 
	DoDrawString(buffer, x - w, y, color);
 

	
 
	return w;
 
}
 

	
 
void DrawStringRightAlignedTruncated(int x, int y, StringID str, uint16 color, uint maxw)
 
{
 
	char buffer[512];
 

	
 
	TruncateStringID(str, buffer, maxw, lastof(buffer));
 
	DoDrawString(buffer, x - GetStringBoundingBox(buffer).width, y, color);
 
}
 

	
 
void DrawStringRightAlignedUnderline(int x, int y, StringID str, uint16 color)
 
{
 
	int w = DrawStringRightAligned(x, y, str, color);
 
	GfxFillRect(x - w, y + 10, x, y + 10, _string_colorremap[1]);
 
}
 

	
 

	
 
int DrawStringCentered(int x, int y, StringID str, uint16 color)
 
{
 
	char buffer[512];
 
	int w;
 

	
 
	GetString(buffer, str, lastof(buffer));
 

	
 
	w = GetStringBoundingBox(buffer).width;
 
	DoDrawString(buffer, x - w / 2, y, color);
 

	
 
	return w;
 
}
 

	
 
int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, uint16 color)
 
{
 
	char buffer[512];
 
	int w = TruncateStringID(str, buffer, xr - xl, lastof(buffer));
 
	return DoDrawString(buffer, (xl + xr - w) / 2, y, color);
 
}
 

	
 
int DoDrawStringCentered(int x, int y, const char *str, uint16 color)
 
{
 
	int w = GetStringBoundingBox(str).width;
 
	DoDrawString(str, x - w / 2, y, color);
 
	return w;
 
}
 

	
 
void DrawStringCenterUnderline(int x, int y, StringID str, uint16 color)
 
{
 
	int w = DrawStringCentered(x, y, str, color);
 
	GfxFillRect(x - (w >> 1), y + 10, x - (w >> 1) + w, y + 10, _string_colorremap[1]);
 
}
 

	
 
void DrawStringCenterUnderlineTruncated(int xl, int xr, int y, StringID str, uint16 color)
 
{
 
	int w = DrawStringCenteredTruncated(xl, xr, y, str, color);
 
	GfxFillRect((xl + xr - w) / 2, y + 10, (xl + xr + w) / 2, y + 10, _string_colorremap[1]);
 
}
 

	
 
/** 'Correct' a string to a maximum length. Longer strings will be cut into
 
 * additional lines at whitespace characters if possible. The string parameter
 
 * is modified with terminating characters mid-string which are the
 
 * placeholders for the newlines.<br/>
 
 * The string WILL be truncated if there was no whitespace for the current
 
 * line's maximum width.
 
 *
 
 * @note To know if the the terminating '\0' is the string end or just a
 
 * newline, the returned 'num' value should be consulted. The num'th '\0',
 
 * starting with index 0 is the real string end.
 
 *
 
 * @param str string to check and correct for length restrictions
 
 * @param maxw the maximum width the string can have on one line
 
 * @return return a 32bit wide number consisting of 2 packed values:
 
 *  0 - 15 the number of lines ADDED to the string
 
 * 16 - 31 the fontsize in which the length calculation was done at */
 
uint32 FormatStringLinebreaks(char *str, int maxw)
 
{
 
	FontSize size = _cur_fontsize;
 
	int num = 0;
 

	
 
	assert(maxw > 0);
 

	
 
	for (;;) {
 
		char *last_space = NULL;
 
		int w = 0;
 

	
 
		for (;;) {
 
			WChar c = Utf8Consume((const char **)&str);
 
			/* whitespace is where we will insert the line-break */
 
			if (c == ' ') last_space = str;
 

	
 
			if (IsPrintable(c)) {
 
				w += GetCharacterWidth(size, c);
 
				/* string is longer than maximum width so we need to decide what to
 
				 * do. We can do two things:
 
				 * 1. If no whitespace was found at all up until now (on this line) then
 
				 *    we will truncate the string and bail out.
 
				 * 2. In all other cases force a linebreak at the last seen whitespace */
 
				if (w > maxw) {
 
					if (last_space == NULL) {
 
						str[-1] = '\0';
 
						return num + (size << 16);
 
					}
 
					str = last_space;
 
					break;
 
				}
 
			} else {
 
				switch (c) {
 
					case '\0': return num + (size << 16); break;
 
					case SCC_SETX:  str++; break;
 
					case SCC_SETXY: str +=2; break;
 
					case SCC_TINYFONT: size = FS_SMALL; break;
 
					case SCC_BIGFONT:  size = FS_LARGE; break;
 
					case '\n': goto end_of_inner_loop;
 
				}
 
			}
 
		}
 
end_of_inner_loop:
 
		/* string didn't fit on line, so 'dummy' terminate and increase linecount */
 
		num++;
 
		str[-1] = '\0';
 
	}
 
}
 

	
 
/** Draw a given string with the centre around the given x coordinates
 
 * @param x Centre the string around this pixel width
 
 * @param y Draw the string at this pixel height (first line's bottom)
 
 * @param str String to draw
 
 * @param max Maximum width the string can have before it is wrapped */
 
void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
 
{
 
	char buffer[512];
 
	uint32 tmp;
 
	int num, w, mt;
 
	const char *src;
 
	WChar c;
 

	
 
	GetString(buffer, str, lastof(buffer));
 

	
 
	tmp = FormatStringLinebreaks(buffer, maxw);
 
	num = GB(tmp, 0, 16);
 

	
 
	mt = GetCharacterHeight(GB(tmp, 16, 16));
 

	
 
	y -= (mt >> 1) * num;
 

	
 
	src = buffer;
 

	
 
	for (;;) {
 
		w = GetStringBoundingBox(src).width;
 
		DoDrawString(src, x - (w>>1), y, 0xFE);
 
		_cur_fontsize = _last_fontsize;
 

	
 
		for (;;) {
 
			c = Utf8Consume(&src);
 
			if (c == 0) {
 
				y += mt;
 
				if (--num < 0) {
 
					_cur_fontsize = FS_NORMAL;
 
					return;
 
				}
 
				break;
 
			} else if (c == SCC_SETX) {
 
				src++;
 
			} else if (c == SCC_SETXY) {
 
				src+=2;
 
			}
 
		}
 
	}
 
}
 

	
 

	
 
uint DrawStringMultiLine(int x, int y, StringID str, int maxw)
 
{
 
	char buffer[512];
 
	uint32 tmp;
 
	int num, mt;
 
	uint total_height;
 
	const char *src;
 
	WChar c;
 

	
 
	GetString(buffer, str, lastof(buffer));
 

	
 
	tmp = FormatStringLinebreaks(buffer, maxw);
 
	num = GB(tmp, 0, 16);
 

	
 
	mt = GetCharacterHeight(GB(tmp, 16, 16));
 
	total_height = (num + 1) * mt;
 

	
 
	src = buffer;
 

	
 
	for (;;) {
 
		DoDrawString(src, x, y, 0xFE);
 
		_cur_fontsize = _last_fontsize;
 

	
 
		for (;;) {
 
			c = Utf8Consume(&src);
 
			if (c == 0) {
 
				y += mt;
 
				if (--num < 0) {
 
					_cur_fontsize = FS_NORMAL;
 
					return total_height;
 
				}
 
				break;
 
			} else if (c == SCC_SETX) {
 
				src++;
 
			} else if (c == SCC_SETXY) {
 
				src+=2;
 
			}
 
		}
 
	}
 
}
 

	
 
/** Return the string dimension in pixels. The height and width are returned
 
 * in a single BoundingRect value. TINYFONT, BIGFONT modifiers are only
 
 * supported as the first character of the string. The returned dimensions
 
 * are therefore a rough estimation correct for all the current strings
 
 * but not every possible combination
 
 * @param str string to calculate pixel-width
 
 * @return string width and height in pixels */
 
BoundingRect GetStringBoundingBox(const char *str)
 
{
 
	FontSize size = _cur_fontsize;
 
	BoundingRect br;
 
	int max_width;
 
	WChar c;
 

	
 
	br.width = br.height = max_width = 0;
 
	for (;;) {
 
		c = Utf8Consume(&str);
 
		if (c == 0) break;
 
		if (IsPrintable(c)) {
 
			br.width += GetCharacterWidth(size, c);
 
		} else {
 
			switch (c) {
 
				case SCC_SETX: br.width += (byte)*str++; break;
 
				case SCC_SETXY:
 
					br.width += (byte)*str++;
 
					br.height += (byte)*str++;
 
					break;
 
				case SCC_TINYFONT: size = FS_SMALL; break;
 
				case SCC_BIGFONT:  size = FS_LARGE; break;
 
				case '\n':
 
					br.height += GetCharacterHeight(size);
 
					if (br.width > max_width) max_width = br.width;
 
					br.width = 0;
 
					break;
 
			}
 
		}
 
	}
 
	br.height += GetCharacterHeight(size);
 

	
 
	br.width  = max(br.width, max_width);
 
	return br;
 
}
 

	
 
/** Draw a string at the given coordinates with the given colour
 
 * @param string the string to draw
 
 * @param x offset from left side of the screen, if negative offset from the right side
 
 * @param x offset from top side of the screen, if negative offset from the bottom
 
 * @param real_color colour of the string, see _string_colormap in
 
 * table/palettes.h or docs/ottd-colourtext-palette.png
 
 * @return the x-coordinates where the drawing has finished. If nothing is drawn
 
 * the originally passed x-coordinate is returned */
 
int DoDrawString(const char *string, int x, int y, uint16 real_color)
 
{
 
	DrawPixelInfo *dpi = _cur_dpi;
 
	FontSize size = _cur_fontsize;
 
	WChar c;
 
	byte color;
 
	int xo = x, yo = y;
 

	
 
	color = real_color & 0xFF;
 

	
 
	if (color != 0xFE) {
 
		if (x >= dpi->left + dpi->width ||
 
				x + _screen.width*2 <= dpi->left ||
 
				y >= dpi->top + dpi->height ||
 
				y + _screen.height <= dpi->top)
 
					return x;
 

	
 
		if (color != 0xFF) {
 
switch_color:;
 
			if (real_color & IS_PALETTE_COLOR) {
 
				_string_colorremap[1] = color;
 
				_string_colorremap[2] = 215;
 
			} else {
 
				_string_colorremap[1] = _string_colormap[color].text;
 
				_string_colorremap[2] = _string_colormap[color].shadow;
 
			}
 
			_color_remap_ptr = _string_colorremap;
 
		}
 
	}
 

	
 
check_bounds:
 
	if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) {
 
skip_char:;
 
		for (;;) {
 
			c = Utf8Consume(&string);
 
			if (!IsPrintable(c)) goto skip_cont;
 
		}
 
	}
 

	
 
	for (;;) {
 
		c = Utf8Consume(&string);
 
skip_cont:;
 
		if (c == 0) {
 
			_last_fontsize = size;
 
			return x;
 
		}
 
		if (IsPrintable(c)) {
 
			if (x >= dpi->left + dpi->width) goto skip_char;
 
			if (x + 26 >= dpi->left) {
 
				GfxMainBlitter(GetGlyph(size, c), x, y, BM_COLOUR_REMAP);
 
			}
 
			x += GetCharacterWidth(size, c);
 
		} else if (c == '\n') { // newline = {}
 
			x = xo;
 
			y += GetCharacterHeight(size);
 
			goto check_bounds;
 
		} else if (c >= SCC_BLUE && c <= SCC_BLACK) { // change color?
 
			color = (byte)(c - SCC_BLUE);
 
			goto switch_color;
 
		} else if (c == SCC_SETX) { // {SETX}
 
			x = xo + (byte)*string++;
 
		} else if (c == SCC_SETXY) {// {SETXY}
 
			x = xo + (byte)*string++;
 
			y = yo + (byte)*string++;
 
		} else if (c == SCC_TINYFONT) { // {TINYFONT}
 
			size = FS_SMALL;
 
		} else if (c == SCC_BIGFONT) { // {BIGFONT}
 
			size = FS_LARGE;
 
		} else {
 
			DEBUG(misc, 0, "[utf8] unknown string command character %d", c);
 
		}
 
	}
 
}
 

	
 
int DoDrawStringTruncated(const char *str, int x, int y, uint16 color, uint maxw)
 
{
 
	char buffer[512];
 
	ttd_strlcpy(buffer, str, sizeof(buffer));
 
	TruncateString(buffer, maxw);
 
	return DoDrawString(buffer, x, y, color);
 
}
 

	
 
void DrawSprite(uint32 img, int x, int y)
 
{
 
	if (img & PALETTE_MODIFIER_COLOR) {
 
		_color_remap_ptr = GetNonSprite(GB(img, PALETTE_SPRITE_START, PALETTE_SPRITE_WIDTH)) + 1;
 
		GfxMainBlitter(GetSprite(img & SPRITE_MASK), x, y, BM_COLOUR_REMAP);
 
	} else if (img & PALETTE_MODIFIER_TRANSPARENT) {
 
		_color_remap_ptr = GetNonSprite(GB(img, PALETTE_SPRITE_START, PALETTE_SPRITE_WIDTH)) + 1;
 
		GfxMainBlitter(GetSprite(img & SPRITE_MASK), x, y, BM_TRANSPARENT);
 
	} else {
 
		GfxMainBlitter(GetSprite(img & SPRITE_MASK), x, y, BM_NORMAL);
 
	}
 
}
 

	
 
typedef struct BlitterParams {
 
	int start_x, start_y;
 
	const byte *sprite;
 
	Pixel *dst;
 
	BlitterMode mode;
 
	int width, height;
 
	int width_org;
 
	int pitch;
 
} BlitterParams;
 

	
 
static void GfxBlitTileZoomIn(BlitterParams *bp)
 
{
 
	const byte *src_o = bp->sprite;
 
	const byte *src;
 
	int num, skip;
 
	byte done;
 
	Pixel *dst;
 
	const byte *ctab;
 

	
 
	src_o += ReadLE16Aligned(src_o + bp->start_y * 2);
 
	switch (bp->mode) {
 
		case BM_COLOUR_REMAP:
 
			do {
 
				do {
 
					done = src_o[0];
 
					num = done & 0x7F;
 
					skip = src_o[1];
 
					src = src_o + 2;
 
					src_o += num + 2;
 

	
 
					dst = bp->dst;
 

	
 
					if ( (skip -= bp->start_x) > 0) {
 
						dst += skip;
 
					} else {
 
						src -= skip;
 
						num += skip;
 
						if (num <= 0) continue;
 
						skip = 0;
 
					}
 

	
 
					skip = skip + num - bp->width;
 
					if (skip > 0) {
 
						num -= skip;
 
						if (num <= 0) continue;
 
					}
 

	
 
					ctab = _color_remap_ptr;
 

	
 
					for (; num >= 4; num -=4) {
 
						dst[3] = ctab[src[3]];
 
						dst[2] = ctab[src[2]];
 
						dst[1] = ctab[src[1]];
 
						dst[0] = ctab[src[0]];
 
						dst += 4;
 
						src += 4;
 
					}
 
					for (; num != 0; num--) *dst++ = ctab[*src++];
 
				} while (!(done & 0x80));
 

	
 
				bp->dst += bp->pitch;
 
			} while (--bp->height != 0);
 
			break;
 

	
 
		case BM_TRANSPARENT:
 
			do {
 
				do {
 
					done = src_o[0];
 
					num = done & 0x7F;
 
					skip = src_o[1];
 
					src_o += num + 2;
 

	
 
					dst = bp->dst;
 

	
 
					if ( (skip -= bp->start_x) > 0) {
 
						dst += skip;
 
					} else {
 
						num += skip;
 
						if (num <= 0) continue;
 
						skip = 0;
 
					}
 

	
 
					skip = skip + num - bp->width;
 
					if (skip > 0) {
 
						num -= skip;
 
						if (num <= 0) continue;
 
					}
 

	
 
					ctab = _color_remap_ptr;
 
					for (; num != 0; num--) {
 
						*dst = ctab[*dst];
 
						dst++;
 
					}
 
				} while (!(done & 0x80));
 

	
 
				bp->dst += bp->pitch;
 
			} while (--bp->height != 0);
 
			break;
 

	
 
		default:
 
			do {
 
				do {
 
					done = src_o[0];
 
					num = done & 0x7F;
 
					skip = src_o[1];
 
					src = src_o + 2;
 
					src_o += num + 2;
 

	
 
					dst = bp->dst;
 

	
 
					if ( (skip -= bp->start_x) > 0) {
 
						dst += skip;
 
					} else {
 
						src -= skip;
 
						num += skip;
 
						if (num <= 0) continue;
 
						skip = 0;
 
					}
 

	
 
					skip = skip + num - bp->width;
 
					if (skip > 0) {
 
						num -= skip;
 
						if (num <= 0) continue;
 
					}
 
#if defined(_WIN32)
 
					if (num & 1) *dst++ = *src++;
 
					if (num & 2) { *(uint16*)dst = *(uint16*)src; dst += 2; src += 2; }
 
					if (num >>= 2) {
 
						do {
 
							*(uint32*)dst = *(uint32*)src;
 
							dst += 4;
 
							src += 4;
 
						} while (--num != 0);
 
					}
 
#else
 
					memcpy(dst, src, num);
 
#endif
 
				} while (!(done & 0x80));
 

	
 
				bp->dst += bp->pitch;
 
			} while (--bp->height != 0);
 
			break;
 
	}
 
}
 

	
 
static void GfxBlitZoomInUncomp(BlitterParams *bp)
 
{
 
	const byte *src = bp->sprite;
 
	Pixel *dst = bp->dst;
 
	int height = bp->height;
 
	int width = bp->width;
 
	int i;
 

	
 
	assert(height > 0);
 
	assert(width > 0);
 

	
 
	switch (bp->mode) {
 
		case BM_COLOUR_REMAP: {
 
			const byte *ctab = _color_remap_ptr;
 

	
 
			do {
 
				for (i = 0; i != width; i++) {
 
					byte b = ctab[src[i]];
 

	
 
					if (b != 0) dst[i] = b;
 
				}
 
				src += bp->width_org;
 
				dst += bp->pitch;
 
			} while (--height != 0);
 
			break;
 
		}
 

	
 
		case BM_TRANSPARENT: {
 
			const byte *ctab = _color_remap_ptr;
 

	
 
			do {
 
				for (i = 0; i != width; i++)
 
					if (src[i] != 0) dst[i] = ctab[dst[i]];
 
				src += bp->width_org;
 
				dst += bp->pitch;
 
			} while (--height != 0);
 
			break;
 
		}
 

	
 
		default:
 
			do {
 
				int n = width;
 

	
 
				for (; n >= 4; n -= 4) {
 
					if (src[0] != 0) dst[0] = src[0];
 
					if (src[1] != 0) dst[1] = src[1];
 
					if (src[2] != 0) dst[2] = src[2];
 
					if (src[3] != 0) dst[3] = src[3];
 

	
 
					dst += 4;
 
					src += 4;
 
				}
 

	
 
				for (; n != 0; n--) {
 
					if (src[0] != 0) dst[0] = src[0];
 
					src++;
 
					dst++;
 
				}
 

	
 
				src += bp->width_org - width;
 
				dst += bp->pitch - width;
 
			} while (--height != 0);
 
			break;
 
	}
 
}
 

	
 
static void GfxBlitTileZoomMedium(BlitterParams *bp)
 
{
 
	const byte *src_o = bp->sprite;
 
	const byte *src;
 
	int num, skip;
 
	byte done;
 
	Pixel *dst;
 
	const byte *ctab;
 

	
 
	src_o += ReadLE16Aligned(src_o + bp->start_y * 2);
 
	switch (bp->mode) {
 
		case BM_COLOUR_REMAP:
 
			do {
 
				do {
 
					done = src_o[0];
 
					num = done & 0x7F;
 
					skip = src_o[1];
 
					src = src_o + 2;
 
					src_o += num + 2;
 

	
 
					dst = bp->dst;
 

	
 
					if (skip & 1) {
 
						skip++;
 
						src++;
 
						if (--num == 0) continue;
 
					}
 

	
 
					if ( (skip -= bp->start_x) > 0) {
 
						dst += skip >> 1;
 
					} else {
 
						src -= skip;
 
						num += skip;
 
						if (num <= 0) continue;
 
						skip = 0;
 
					}
 

	
 
					skip = skip + num - bp->width;
 
					if (skip > 0) {
 
						num -= skip;
 
						if (num <= 0) continue;
 
					}
 

	
 
					ctab = _color_remap_ptr;
 
					num = (num + 1) >> 1;
 
					for (; num != 0; num--) {
 
							*dst = ctab[*src];
 
							dst++;
 
							src += 2;
 
					}
 
				} while (!(done & 0x80));
 
				bp->dst += bp->pitch;
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
			} while (--bp->height != 0);
 
			break;
 

	
 
		case BM_TRANSPARENT:
 
			do {
 
				do {
 
					done = src_o[0];
 
					num = done & 0x7F;
 
					skip = src_o[1];
 
					src_o += num + 2;
 

	
 
					dst = bp->dst;
 

	
 
					if (skip & 1) {
 
						skip++;
 
						if (--num == 0) continue;
 
					}
 

	
 
					if ( (skip -= bp->start_x) > 0) {
 
						dst += skip >> 1;
 
					} else {
 
						num += skip;
 
						if (num <= 0) continue;
 
						skip = 0;
 
					}
 

	
 
					skip = skip + num - bp->width;
 
					if (skip > 0) {
 
						num -= skip;
 
						if (num <= 0) continue;
 
					}
 

	
 
					ctab = _color_remap_ptr;
 
					num = (num + 1) >> 1;
 
					for (; num != 0; num--) {
 
							*dst = ctab[*dst];
 
							dst++;
 
					}
 
				} while (!(done & 0x80));
 
				bp->dst += bp->pitch;
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
			} while (--bp->height != 0);
 
			break;
 

	
 
		default:
 
			do {
 
				do {
 
					done = src_o[0];
 
					num = done & 0x7F;
 
					skip = src_o[1];
 
					src = src_o + 2;
 
					src_o += num + 2;
 

	
 
					dst = bp->dst;
 

	
 
					if (skip & 1) {
 
						skip++;
 
						src++;
 
						if (--num == 0) continue;
 
					}
 

	
 
					if ( (skip -= bp->start_x) > 0) {
 
						dst += skip >> 1;
 
					} else {
 
						src -= skip;
 
						num += skip;
 
						if (num <= 0) continue;
 
						skip = 0;
 
					}
 

	
 
					skip = skip + num - bp->width;
 
					if (skip > 0) {
 
						num -= skip;
 
						if (num <= 0) continue;
 
					}
 

	
 
					num = (num + 1) >> 1;
 

	
 
					for (; num != 0; num--) {
 
							*dst = *src;
 
							dst++;
 
							src += 2;
 
					}
 

	
 
				} while (!(done & 0x80));
 

	
 
				bp->dst += bp->pitch;
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
			} while (--bp->height != 0);
 
			break;
 
	}
 
}
 

	
 
static void GfxBlitZoomMediumUncomp(BlitterParams *bp)
 
{
 
	const byte *src = bp->sprite;
 
	Pixel *dst = bp->dst;
 
	int height = bp->height;
 
	int width = bp->width;
 
	int i;
 

	
 
	assert(height > 0);
 
	assert(width > 0);
 

	
 
	switch (bp->mode) {
 
		case BM_COLOUR_REMAP: {
 
			const byte *ctab = _color_remap_ptr;
 

	
 
			for (height >>= 1; height != 0; height--) {
 
				for (i = 0; i != width >> 1; i++) {
 
					byte b = ctab[src[i * 2]];
 

	
 
					if (b != 0) dst[i] = b;
 
				}
 
				src += bp->width_org * 2;
 
				dst += bp->pitch;
 
			}
 
			break;
 
		}
 

	
 
		case BM_TRANSPARENT: {
 
			const byte *ctab = _color_remap_ptr;
 

	
 
			for (height >>= 1; height != 0; height--) {
 
				for (i = 0; i != width >> 1; i++)
 
					if (src[i * 2] != 0) dst[i] = ctab[dst[i]];
 
				src += bp->width_org * 2;
 
				dst += bp->pitch;
 
			}
 
			break;
 
		}
 

	
 
		default:
 
			for (height >>= 1; height != 0; height--) {
 
				for (i = 0; i != width >> 1; i++)
 
					if (src[i * 2] != 0) dst[i] = src[i * 2];
 
				src += bp->width_org * 2;
 
				dst += bp->pitch;
 
			}
 
			break;
 
	}
 
}
 

	
 
static void GfxBlitTileZoomOut(BlitterParams *bp)
 
{
 
	const byte *src_o = bp->sprite;
 
	const byte *src;
 
	int num, skip;
 
	byte done;
 
	Pixel *dst;
 
	const byte *ctab;
 

	
 
	src_o += ReadLE16Aligned(src_o + bp->start_y * 2);
 
	switch (bp->mode) {
 
		case BM_COLOUR_REMAP:
 
			for (;;) {
 
				do {
 
					done = src_o[0];
 
					num = done & 0x7F;
 
					skip = src_o[1];
 
					src = src_o + 2;
 
					src_o += num + 2;
 

	
 
					dst = bp->dst;
 

	
 
					if (skip & 1) {
 
						skip++;
 
						src++;
 
						if (--num == 0) continue;
 
					}
 

	
 
					if (skip & 2) {
 
						skip += 2;
 
						src += 2;
 
						num -= 2;
 
						if (num <= 0) continue;
 
					}
 

	
 
					if ( (skip -= bp->start_x) > 0) {
 
						dst += skip >> 2;
 
					} else {
 
						src -= skip;
 
						num += skip;
 
						if (num <= 0) continue;
 
						skip = 0;
 
					}
 

	
 
					skip = skip + num - bp->width;
 
					if (skip > 0) {
 
						num -= skip;
 
						if (num <= 0) continue;
 
					}
 

	
 
					ctab = _color_remap_ptr;
 
					num = (num + 3) >> 2;
 
					for (; num != 0; num--) {
 
							*dst = ctab[*src];
 
							dst++;
 
							src += 4;
 
					}
 
				} while (!(done & 0x80));
 
				bp->dst += bp->pitch;
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
				if (--bp->height == 0) return;
 
			}
 
			break;
 

	
 
		case BM_TRANSPARENT:
 
			for (;;) {
 
				do {
 
					done = src_o[0];
 
					num = done & 0x7F;
 
					skip = src_o[1];
 
					src_o += num + 2;
 

	
 
					dst = bp->dst;
 

	
 
					if (skip & 1) {
 
						skip++;
 
						if (--num == 0) continue;
 
					}
 

	
 
					if (skip & 2) {
 
						skip += 2;
 
						num -= 2;
 
						if (num <= 0) continue;
 
					}
 

	
 
					if ( (skip -= bp->start_x) > 0) {
 
						dst += skip >> 2;
 
					} else {
 
						num += skip;
 
						if (num <= 0) continue;
 
						skip = 0;
 
					}
 

	
 
					skip = skip + num - bp->width;
 
					if (skip > 0) {
 
						num -= skip;
 
						if (num <= 0) continue;
 
					}
 

	
 
					ctab = _color_remap_ptr;
 
					num = (num + 3) >> 2;
 
					for (; num != 0; num--) {
 
							*dst = ctab[*dst];
 
							dst++;
 
					}
 

	
 
				} while (!(done & 0x80));
 
				bp->dst += bp->pitch;
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
				if (--bp->height == 0) return;
 
			}
 
			break;
 

	
 
		default:
 
			for (;;) {
 
				do {
 
					done = src_o[0];
 
					num = done & 0x7F;
 
					skip = src_o[1];
 
					src = src_o + 2;
 
					src_o += num + 2;
 

	
 
					dst = bp->dst;
 

	
 
					if (skip & 1) {
 
						skip++;
 
						src++;
 
						if (--num == 0) continue;
 
					}
 

	
 
					if (skip & 2) {
 
						skip += 2;
 
						src += 2;
 
						num -= 2;
 
						if (num <= 0) continue;
 
					}
 

	
 
					if ( (skip -= bp->start_x) > 0) {
 
						dst += skip >> 2;
 
					} else {
 
						src -= skip;
 
						num += skip;
 
						if (num <= 0) continue;
 
						skip = 0;
 
					}
 

	
 
					skip = skip + num - bp->width;
 
					if (skip > 0) {
 
						num -= skip;
 
						if (num <= 0) continue;
 
					}
 

	
 
					num = (num + 3) >> 2;
 

	
 
					for (; num != 0; num--) {
 
							*dst = *src;
 
							dst++;
 
							src += 4;
 
					}
 
				} while (!(done & 0x80));
 

	
 
				bp->dst += bp->pitch;
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
				if (--bp->height == 0) return;
 

	
 
				do {
 
					done = src_o[0];
 
					src_o += (done & 0x7F) + 2;
 
				} while (!(done & 0x80));
 
				if (--bp->height == 0) return;
 
			}
 
			break;
 
	}
 
}
 

	
 
static void GfxBlitZoomOutUncomp(BlitterParams *bp)
 
{
 
	const byte *src = bp->sprite;
 
	Pixel *dst = bp->dst;
 
	int height = bp->height;
 
	int width = bp->width;
 
	int i;
 

	
 
	assert(height > 0);
 
	assert(width > 0);
 

	
 
	switch (bp->mode) {
 
		case BM_COLOUR_REMAP: {
 
			const byte *ctab = _color_remap_ptr;
 

	
 
			for (height >>= 2; height != 0; height--) {
 
				for (i = 0; i != width >> 2; i++) {
 
					byte b = ctab[src[i * 4]];
 

	
 
					if (b != 0) dst[i] = b;
 
				}
 
				src += bp->width_org * 4;
 
				dst += bp->pitch;
 
			}
 
			break;
 
		}
 

	
 
		case BM_TRANSPARENT: {
 
			const byte *ctab = _color_remap_ptr;
 

	
 
			for (height >>= 2; height != 0; height--) {
 
				for (i = 0; i != width >> 2; i++)
 
					if (src[i * 4] != 0) dst[i] = ctab[dst[i]];
 
				src += bp->width_org * 4;
 
				dst += bp->pitch;
 
			}
 
			break;
 
		}
 

	
 
		default:
 
			for (height >>= 2; height != 0; height--) {
 
				for (i = 0; i != width >> 2; i++)
 
					if (src[i * 4] != 0) dst[i] = src[i * 4];
 
				src += bp->width_org * 4;
 
				dst += bp->pitch;
 
			}
 
			break;
 
	}
 
}
 

	
 

	
 
static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode)
 
{
 
	const DrawPixelInfo *dpi = _cur_dpi;
 
	int start_x, start_y;
 
	BlitterParams bp;
 
	int zoom_mask = ~((1 << dpi->zoom) - 1);
 

	
 
	/* decode sprite header */
 
	x += sprite->x_offs;
 
	y += sprite->y_offs;
 
	bp.width_org = bp.width = sprite->width;
 
	bp.height = sprite->height;
 
	bp.sprite = sprite->data;
 
	bp.dst = dpi->dst_ptr;
 
	bp.mode = mode;
 
	bp.pitch = dpi->pitch;
 

	
 
	assert(bp.height > 0);
 
	assert(bp.width > 0);
 

	
 
	if (sprite->info & 8) {
 
		/* tile blit */
 
		start_y = 0;
 

	
 
		if (dpi->zoom > 0) {
 
			start_y += bp.height & ~zoom_mask;
 
			bp.height &= zoom_mask;
 
			if (bp.height == 0) return;
 
			y &= zoom_mask;
 
		}
 

	
 
		if ( (y -= dpi->top) < 0) {
 
			bp.height += y;
 
			if (bp.height <= 0) return;
 
			start_y -= y;
 
			y = 0;
 
		} else {
 
			bp.dst += bp.pitch * (y >> dpi->zoom);
 
		}
 
		bp.start_y = start_y;
 

	
 
		if ( (y = y + bp.height - dpi->height) > 0) {
 
			bp.height -= y;
 
			if (bp.height <= 0) return;
 
		}
 

	
 
		start_x = 0;
 
		x &= zoom_mask;
 
		if ( (x -= dpi->left) < 0) {
 
			bp.width += x;
 
			if (bp.width <= 0) return;
 
			start_x -= x;
 
			x = 0;
 
		}
 
		bp.start_x = start_x;
 
		bp.dst += x >> dpi->zoom;
 

	
 
		if ( (x = x + bp.width - dpi->width) > 0) {
 
			bp.width -= x;
 
			if (bp.width <= 0) return;
 
		}
 

	
 
		switch (dpi->zoom) {
 
			default: NOT_REACHED();
 
			case 0: GfxBlitTileZoomIn(&bp);     break;
 
			case 1: GfxBlitTileZoomMedium(&bp); break;
 
			case 2: GfxBlitTileZoomOut(&bp);    break;
 
		}
 
	} else {
 
		bp.sprite += bp.width * (bp.height & ~zoom_mask);
 
		bp.height &= zoom_mask;
 
		if (bp.height == 0) return;
 

	
 
		y &= zoom_mask;
 

	
 
		if ( (y -= dpi->top) < 0) {
 
			bp.height += y;
 
			if (bp.height <= 0) return;
 
			bp.sprite -= bp.width * y;
 
			y = 0;
 
		} else {
 
			bp.dst += bp.pitch * (y >> dpi->zoom);
 
		}
 

	
 
		if (bp.height > dpi->height - y) {
 
			bp.height = dpi->height - y;
 
			if (bp.height <= 0) return;
 
		}
 

	
 
		x &= zoom_mask;
 

	
 
		if ( (x -= dpi->left) < 0) {
 
			bp.width += x;
 
			if (bp.width <= 0) return;
 
			bp.sprite -= x;
 
			x = 0;
 
		}
 
		bp.dst += x >> dpi->zoom;
 

	
 
		if (bp.width > dpi->width - x) {
 
			bp.width = dpi->width - x;
 
			if (bp.width <= 0) return;
 
		}
 

	
 
		switch (dpi->zoom) {
 
			default: NOT_REACHED();
 
			case 0: GfxBlitZoomInUncomp(&bp);     break;
 
			case 1: GfxBlitZoomMediumUncomp(&bp); break;
 
			case 2: GfxBlitZoomOutUncomp(&bp);    break;
 
		}
 
	}
 
}
 

	
 
void DoPaletteAnimations(void);
 

	
 
void GfxInitPalettes(void)
 
{
 
	memcpy(_cur_palette, _palettes[_use_dos_palette ? 1 : 0], sizeof(_cur_palette));
 

	
 
	_pal_first_dirty = 0;
 
	_pal_last_dirty = 255;
 
	DoPaletteAnimations();
 
}
 

	
 
#define EXTR(p, q) (((uint16)(_timer_counter * (p)) * (q)) >> 16)
 
#define EXTR2(p, q) (((uint16)(~_timer_counter * (p)) * (q)) >> 16)
 

	
 
void DoPaletteAnimations(void)
 
{
 
	const Colour *s;
 
	Colour *d;
 
	/* Amount of colors to be rotated.
 
	 * A few more for the DOS palette, because the water colors are
 
	 * 245-254 for DOS and 217-226 for Windows.  */
 
	const ExtraPaletteValues *ev = &_extra_palette_values;
 
	int c = _use_dos_palette ? 38 : 28;
 
	Colour old_val[38]; // max(38, 28)
 
	uint i;
 
	uint j;
 

	
 
	d = &_cur_palette[217];
 
	memcpy(old_val, d, c * sizeof(*old_val));
 

	
 
	// Dark blue water
 
	s = (_opt.landscape == LT_CANDY) ? ev->ac : ev->a;
 
	j = EXTR(320, 5);
 
	for (i = 0; i != 5; i++) {
 
		*d++ = s[j];
 
		j++;
 
		if (j == 5) j = 0;
 
	}
 

	
 
	// Glittery water
 
	s = (_opt.landscape == LT_CANDY) ? ev->bc : ev->b;
 
	j = EXTR(128, 15);
 
	for (i = 0; i != 5; i++) {
 
		*d++ = s[j];
 
		j += 3;
 
		if (j >= 15) j -= 15;
 
	}
 

	
 
	s = ev->e;
 
	j = EXTR2(512, 5);
 
	for (i = 0; i != 5; i++) {
 
		*d++ = s[j];
 
		j++;
 
		if (j == 5) j = 0;
 
	}
 

	
 
	// Oil refinery fire animation
 
	s = ev->oil_ref;
 
	j = EXTR2(512, 7);
 
	for (i = 0; i != 7; i++) {
 
		*d++ = s[j];
 
		j++;
 
		if (j == 7) j = 0;
 
	}
 

	
 
	// Radio tower blinking
 
	{
 
		byte i = (_timer_counter >> 1) & 0x7F;
 
		byte v;
 

	
 
		(v = 255, i < 0x3f) ||
 
		(v = 128, i < 0x4A || i >= 0x75) ||
 
		(v = 20);
 
		d->r = v;
 
		d->g = 0;
 
		d->b = 0;
 
		d++;
 

	
 
		i ^= 0x40;
 
		(v = 255, i < 0x3f) ||
 
		(v = 128, i < 0x4A || i >= 0x75) ||
 
		(v = 20);
 
		d->r = v;
 
		d->g = 0;
 
		d->b = 0;
 
		d++;
 
	}
 

	
 
	// Handle lighthouse and stadium animation
 
	s = ev->lighthouse;
 
	j = EXTR(256, 4);
 
	for (i = 0; i != 4; i++) {
 
		*d++ = s[j];
 
		j++;
 
		if (j == 4) j = 0;
 
	}
 

	
 
	// Animate water for old DOS graphics
 
	if (_use_dos_palette) {
 
		// Dark blue water DOS
 
		s = (_opt.landscape == LT_CANDY) ? ev->ac : ev->a;
 
		j = EXTR(320, 5);
 
		for (i = 0; i != 5; i++) {
 
			*d++ = s[j];
 
			j++;
 
			if (j == 5) j = 0;
 
		}
 

	
 
		// Glittery water DOS
 
		s = (_opt.landscape == LT_CANDY) ? ev->bc : ev->b;
 
		j = EXTR(128, 15);
 
		for (i = 0; i != 5; i++) {
 
			*d++ = s[j];
 
			j += 3;
 
			if (j >= 15) j -= 15;
 
		}
 
	}
 

	
 
	if (memcmp(old_val, &_cur_palette[217], c * sizeof(*old_val)) != 0) {
 
		if (_pal_first_dirty > 217) _pal_first_dirty = 217;
 
		if (_pal_last_dirty < 217 + c) _pal_last_dirty = 217 + c;
 
	}
 
}
 

	
 

	
 
void LoadStringWidthTable(void)
 
{
 
	uint i;
 

	
 
	/* Normal font */
 
	for (i = 0; i != 224; i++) {
 
		_stringwidth_table[FS_NORMAL][i] = GetGlyphWidth(FS_NORMAL, i + 32);
 
	}
 

	
 
	/* Small font */
 
	for (i = 0; i != 224; i++) {
 
		_stringwidth_table[FS_SMALL][i] = GetGlyphWidth(FS_SMALL, i + 32);
 
	}
 

	
 
	/* Large font */
 
	for (i = 0; i != 224; i++) {
 
		_stringwidth_table[FS_LARGE][i] = GetGlyphWidth(FS_LARGE, i + 32);
 
	}
 
}
 

	
 

	
 
byte GetCharacterWidth(FontSize size, WChar key)
 
{
 
	if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
 

	
 
	return GetGlyphWidth(size, key);
 
}
 

	
 

	
 
void ScreenSizeChanged(void)
 
{
 
	// check the dirty rect
 
	if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
 
	if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
 

	
 
	// screen size changed and the old bitmap is invalid now, so we don't want to undraw it
 
	_cursor.visible = false;
 
}
 

	
 
void UndrawMouseCursor(void)
 
{
 
	if (_cursor.visible) {
 
		_cursor.visible = false;
 
		memcpy_pitch(
 
			_screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch,
 
			_cursor_backup,
 
			_cursor.draw_size.x, _cursor.draw_size.y, _cursor.draw_size.x, _screen.pitch);
 

	
 
		_video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
 
	}
 
}
 

	
 
void DrawMouseCursor(void)
 
{
 
	int x;
 
	int y;
 
	int w;
 
	int h;
 

	
 
	/* Redraw mouse cursor but only when it's inside the window */
 
	if (!_cursor.in_window) return;
 

	
 
	// Don't draw the mouse cursor if it's already drawn
 
	if (_cursor.visible) {
 
		if (!_cursor.dirty) return;
 
		UndrawMouseCursor();
 
	}
 

	
 
	w = _cursor.size.x;
 
	x = _cursor.pos.x + _cursor.offs.x;
 
	if (x < 0) {
 
		w += x;
 
		x = 0;
 
	}
 
	if (w > _screen.width - x) w = _screen.width - x;
 
	if (w <= 0) return;
 
	_cursor.draw_pos.x = x;
 
	_cursor.draw_size.x = w;
 

	
 
	h = _cursor.size.y;
 
	y = _cursor.pos.y + _cursor.offs.y;
 
	if (y < 0) {
 
		h += y;
 
		y = 0;
 
	}
 
	if (h > _screen.height - y) h = _screen.height - y;
 
	if (h <= 0) return;
 
	_cursor.draw_pos.y = y;
 
	_cursor.draw_size.y = h;
 

	
 
	assert(w * h < (int)sizeof(_cursor_backup));
 

	
 
	// Make backup of stuff below cursor
 
	memcpy_pitch(
 
		_cursor_backup,
 
		_screen.dst_ptr + _cursor.draw_pos.x + _cursor.draw_pos.y * _screen.pitch,
 
		_cursor.draw_size.x, _cursor.draw_size.y, _screen.pitch, _cursor.draw_size.x);
 

	
 
	// Draw cursor on screen
 
	_cur_dpi = &_screen;
 
	DrawSprite(_cursor.sprite, _cursor.pos.x, _cursor.pos.y);
 

	
 
	_video_driver->make_dirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
 

	
 
	_cursor.visible = true;
 
	_cursor.dirty = false;
 
}
 

	
 
#if defined(_DEBUG)
 
static void DbgScreenRect(int left, int top, int right, int bottom)
 
{
 
	DrawPixelInfo dp;
 
	DrawPixelInfo *old;
 

	
 
	old = _cur_dpi;
 
	_cur_dpi = &dp;
 
	dp = _screen;
 
	GfxFillRect(left, top, right - 1, bottom - 1, rand() & 255);
 
	_cur_dpi = old;
 
}
 
#endif
 

	
 
void RedrawScreenRect(int left, int top, int right, int bottom)
 
{
 
	assert(right <= _screen.width && bottom <= _screen.height);
 
	if (_cursor.visible) {
 
		if (right > _cursor.draw_pos.x &&
 
				left < _cursor.draw_pos.x + _cursor.draw_size.x &&
 
				bottom > _cursor.draw_pos.y &&
 
				top < _cursor.draw_pos.y + _cursor.draw_size.y) {
 
			UndrawMouseCursor();
 
		}
 
	}
 
	UndrawTextMessage();
 

	
 
#if defined(_DEBUG)
 
	if (_dbg_screen_rect)
 
		DbgScreenRect(left, top, right, bottom);
 
	else
 
#endif
 
		DrawOverlappedWindowForAll(left, top, right, bottom);
 

	
 
	_video_driver->make_dirty(left, top, right - left, bottom - top);
 
}
 

	
 
void DrawDirtyBlocks(void)
 
{
 
	byte *b = _dirty_blocks;
 
	const int w = ALIGN(_screen.width, 64);
 
	const int h = ALIGN(_screen.height, 8);
 
	int x;
 
	int y;
 

	
 
	if (IsGeneratingWorld() && !IsGeneratingWorldReadyForPaint()) return;
 

	
 
	y = 0;
 
	do {
 
		x = 0;
 
		do {
 
			if (*b != 0) {
 
				int left;
 
				int top;
 
				int right = x + 64;
 
				int bottom = y;
 
				byte *p = b;
 
				int h2;
 

	
 
				// First try coalescing downwards
 
				do {
 
					*p = 0;
 
					p += DIRTY_BYTES_PER_LINE;
 
					bottom += 8;
 
				} while (bottom != h && *p != 0);
 

	
 
				// Try coalescing to the right too.
 
				h2 = (bottom - y) >> 3;
 
				assert(h2 > 0);
 
				p = b;
 

	
 
				while (right != w) {
 
					byte *p2 = ++p;
 
					int h = h2;
 
					// Check if a full line of dirty flags is set.
 
					do {
 
						if (!*p2) goto no_more_coalesc;
 
						p2 += DIRTY_BYTES_PER_LINE;
 
					} while (--h != 0);
 

	
 
					// Wohoo, can combine it one step to the right!
 
					// Do that, and clear the bits.
 
					right += 64;
 

	
 
					h = h2;
 
					p2 = p;
 
					do {
 
						*p2 = 0;
 
						p2 += DIRTY_BYTES_PER_LINE;
 
					} while (--h != 0);
 
				}
 
				no_more_coalesc:
 

	
 
				left = x;
 
				top = y;
 

	
 
				if (left   < _invalid_rect.left  ) left   = _invalid_rect.left;
 
				if (top    < _invalid_rect.top   ) top    = _invalid_rect.top;
 
				if (right  > _invalid_rect.right ) right  = _invalid_rect.right;
 
				if (bottom > _invalid_rect.bottom) bottom = _invalid_rect.bottom;
 

	
 
				if (left < right && top < bottom) {
 
					RedrawScreenRect(left, top, right, bottom);
 
				}
 

	
 
			}
 
		} while (b++, (x += 64) != w);
 
	} while (b += -(w >> 6) + DIRTY_BYTES_PER_LINE, (y += 8) != h);
 

	
 
	_invalid_rect.left = w;
 
	_invalid_rect.top = h;
 
	_invalid_rect.right = 0;
 
	_invalid_rect.bottom = 0;
 

	
 
	/* If we are generating a world, and waiting for a paint run, mark it here
 
	 *  as done painting, so we can continue generating. */
 
	if (IsGeneratingWorld() && IsGeneratingWorldReadyForPaint()) {
 
		SetGeneratingWorldPaintStatus(false);
 
	}
 
}
 

	
 

	
 
void SetDirtyBlocks(int left, int top, int right, int bottom)
 
{
 
	byte *b;
 
	int width;
 
	int height;
 

	
 
	if (left < 0) left = 0;
 
	if (top < 0) top = 0;
 
	if (right > _screen.width) right = _screen.width;
 
	if (bottom > _screen.height) bottom = _screen.height;
 

	
 
	if (left >= right || top >= bottom) return;
 

	
 
	if (left   < _invalid_rect.left  ) _invalid_rect.left   = left;
 
	if (top    < _invalid_rect.top   ) _invalid_rect.top    = top;
 
	if (right  > _invalid_rect.right ) _invalid_rect.right  = right;
 
	if (bottom > _invalid_rect.bottom) _invalid_rect.bottom = bottom;
 

	
 
	left >>= 6;
 
	top  >>= 3;
 

	
 
	b = _dirty_blocks + top * DIRTY_BYTES_PER_LINE + left;
 

	
 
	width  = ((right  - 1) >> 6) - left + 1;
 
	height = ((bottom - 1) >> 3) - top  + 1;
 

	
 
	assert(width > 0 && height > 0);
 

	
 
	do {
 
		int i = width;
 

	
 
		do b[--i] = 0xFF; while (i);
 

	
 
		b += DIRTY_BYTES_PER_LINE;
 
	} while (--height != 0);
 
}
 

	
 
void MarkWholeScreenDirty(void)
 
{
 
	SetDirtyBlocks(0, 0, _screen.width, _screen.height);
 
}
 

	
 
/** Set up a clipping area for only drawing into a certain area. To do this,
 
 * Fill a DrawPixelInfo object with the supplied relative rectangle, backup
 
 * the original (calling) _cur_dpi and assign the just returned DrawPixelInfo
 
 * _cur_dpi. When you are done, give restore _cur_dpi's original value
 
 * @param *n the DrawPixelInfo that will be the clipping rectangle box allowed
 
 * for drawing
 
 * @param left,top,width,height the relative coordinates of the clipping
 
 * rectangle relative to the current _cur_dpi. This will most likely be the
 
 * offset from the calling window coordinates
 
 * @return return false if the requested rectangle is not possible with the
 
 * current dpi pointer. Only continue of the return value is true, or you'll
 
 * get some nasty results */
 
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
 
{
 
	const DrawPixelInfo *o = _cur_dpi;
 

	
 
	n->zoom = 0;
 

	
 
	assert(width > 0);
 
	assert(height > 0);
 

	
 
	if ((left -= o->left) < 0) {
 
		width += left;
 
		if (width <= 0) return false;
 
		n->left = -left;
 
		left = 0;
 
	} else {
 
		n->left = 0;
 
	}
 

	
 
	if (width > o->width - left) {
 
		width = o->width - left;
 
		if (width <= 0) return false;
 
	}
 
	n->width = width;
 

	
 
	if ((top -= o->top) < 0) {
 
		height += top;
 
		if (height <= 0) return false;
 
		n->top = -top;
 
		top = 0;
 
	} else {
 
		n->top = 0;
 
	}
 

	
 
	n->dst_ptr = o->dst_ptr + left + top * (n->pitch = o->pitch);
 

	
 
	if (height > o->height - top) {
 
		height = o->height - top;
 
		if (height <= 0) return false;
 
	}
 
	n->height = height;
 

	
 
	return true;
 
}
 

	
 
static void SetCursorSprite(CursorID cursor)
 
{
 
	CursorVars *cv = &_cursor;
 
	const Sprite *p;
 

	
 
	if (cv->sprite == cursor) return;
 

	
 
	p = GetSprite(cursor & SPRITE_MASK);
 
	cv->sprite = cursor;
 
	cv->size.y = p->height;
 
	cv->size.x = p->width;
 
	cv->offs.x = p->x_offs;
 
	cv->offs.y = p->y_offs;
 

	
 
	cv->dirty = true;
 
}
 

	
 
static void SwitchAnimatedCursor(void)
 
{
 
	CursorVars *cv = &_cursor;
 
	const CursorID *cur = cv->animate_cur;
 
	CursorID sprite;
 

	
 
	// ANIM_CURSOR_END is 0xFFFF in table/animcursors.h
 
	if (cur == NULL || *cur == 0xFFFF) cur = cv->animate_list;
 

	
 
	sprite = cur[0];
 
	cv->animate_timeout = cur[1];
 
	cv->animate_cur = cur + 2;
 

	
 
	SetCursorSprite(sprite);
 
}
 

	
 
void CursorTick(void)
 
{
 
	if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0)
 
		SwitchAnimatedCursor();
 
}
 

	
 
void SetMouseCursor(CursorID cursor)
 
{
 
	// Turn off animation
 
	_cursor.animate_timeout = 0;
 
	// Set cursor
 
	SetCursorSprite(cursor);
 
}
 

	
 
void SetAnimatedMouseCursor(const CursorID *table)
 
{
 
	_cursor.animate_list = table;
 
	_cursor.animate_cur = NULL;
 
	SwitchAnimatedCursor();
 
}
 

	
 
bool ChangeResInGame(int w, int h)
 
{
 
	return
 
		(_screen.width == w && _screen.height == h) ||
 
		_video_driver->change_resolution(w, h);
 
}
 

	
 
void ToggleFullScreen(bool fs)
 
{
 
	_video_driver->toggle_fullscreen(fs);
 
	if (_fullscreen != fs && _num_resolutions == 0) {
 
		DEBUG(driver, 0, "Could not find a suitable fullscreen resolution");
 
	}
 
}
 

	
 
static int CDECL compare_res(const void *pa, const void *pb)
 
{
 
	int x = ((const uint16*)pa)[0] - ((const uint16*)pb)[0];
 
	if (x != 0) return x;
 
	return ((const uint16*)pa)[1] - ((const uint16*)pb)[1];
 
}
 

	
 
void SortResolutions(int count)
 
{
 
	qsort(_resolutions, count, sizeof(_resolutions[0]), compare_res);
 
}
src/gfxinit.c
Show inline comments
 
deleted file
src/gfxinit.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "gfx.h"
 
#include "gfxinit.h"
 
#include "spritecache.h"
 
#include "table/sprites.h"
 
#include "fileio.h"
 
#include "string.h"
 
#include "newgrf.h"
 
#include "md5.h"
 
#include "variables.h"
 
#include "fontcache.h"
 
#include <string.h>
 

	
 
typedef struct MD5File {
 
	const char * const filename;     // filename
 
	const md5_byte_t hash[16]; // md5 sum of the file
 
} MD5File;
 

	
 
typedef struct FileList {
 
	const MD5File basic[4];     // grf files that always have to be loaded
 
	const MD5File landscape[3]; // landscape specific grf files
 
} FileList;
 

	
 
enum {
 
	SKIP = 0xFFFE,
 
	END  = 0xFFFF
 
};
 

	
 
#include "table/files.h"
 
#include "table/landscape_sprite.h"
 

	
 
static const SpriteID * const _landscape_spriteindexes[] = {
 
	_landscape_spriteindexes_1,
 
	_landscape_spriteindexes_2,
 
	_landscape_spriteindexes_3,
 
};
 

	
 
static const SpriteID * const _slopes_spriteindexes[] = {
 
	_slopes_spriteindexes_0,
 
	_slopes_spriteindexes_1,
 
	_slopes_spriteindexes_2,
 
	_slopes_spriteindexes_3,
 
};
 

	
 

	
 
static uint LoadGrfFile(const char* filename, uint load_index, int file_index)
 
{
 
	uint load_index_org = load_index;
 

	
 
	FioOpenFile(file_index, filename);
 

	
 
	DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
 

	
 
	while (LoadNextSprite(load_index, file_index)) {
 
		load_index++;
 
		if (load_index >= MAX_SPRITES) {
 
			error("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
 
		}
 
	}
 
	DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
 

	
 
	return load_index - load_index_org;
 
}
 

	
 

	
 
static void LoadGrfIndexed(const char* filename, const SpriteID* index_tbl, int file_index)
 
{
 
	uint start;
 

	
 
	FioOpenFile(file_index, filename);
 

	
 
	DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
 

	
 
	while ((start = *index_tbl++) != END) {
 
		uint end = *index_tbl++;
 

	
 
		if (start == SKIP) { // skip sprites (amount in second var)
 
			SkipSprites(end);
 
		} else { // load sprites and use indexes from start to end
 
			do {
 
			#ifdef NDEBUG
 
				LoadNextSprite(start, file_index);
 
			#else
 
				bool b = LoadNextSprite(start, file_index);
 
				assert(b);
 
			#endif
 
			} while (++start <= end);
 
		}
 
	}
 
}
 

	
 

	
 
/* Check that the supplied MD5 hash matches that stored for the supplied filename */
 
static bool CheckMD5Digest(const MD5File file, md5_byte_t *digest, bool warn)
 
{
 
	if (memcmp(file.hash, digest, sizeof(file.hash)) == 0) return true;
 
	if (warn) fprintf(stderr, "MD5 of %s is ****INCORRECT**** - File Corrupt.\n", file.filename);
 
	return false;
 
}
 

	
 
/* Calculate and check the MD5 hash of the supplied filename.
 
 * returns true if the checksum is correct */
 
static bool FileMD5(const MD5File file, bool warn)
 
{
 
	FILE *f;
 
	char buf[MAX_PATH];
 

	
 
	// open file
 
	snprintf(buf, lengthof(buf), "%s%s", _paths.data_dir, file.filename);
 
	f = fopen(buf, "rb");
 

	
 
#if !defined(WIN32)
 
	if (f == NULL) {
 
		strtolower(buf + strlen(_paths.data_dir) - 1);
 
		f = fopen(buf, "rb");
 
	}
 
#endif
 

	
 
#if defined SECOND_DATA_DIR
 
	/* If we failed to find the file in the first data directory, we will try the other one */
 

	
 
	if (f == NULL) {
 
		snprintf(buf, lengthof(buf), "%s%s", _paths.second_data_dir, file.filename);
 
		f = fopen(buf, "rb");
 

	
 
		if (f == NULL) {
 
			strtolower(buf + strlen(_paths.second_data_dir) - 1);
 
			f = fopen(buf, "rb");
 
		}
 
	}
 
#endif
 

	
 
	if (f != NULL) {
 
		md5_state_t filemd5state;
 
		md5_byte_t buffer[1024];
 
		md5_byte_t digest[16];
 
		size_t len;
 

	
 
		md5_init(&filemd5state);
 
		while ((len = fread(buffer, 1, sizeof(buffer), f)) != 0)
 
			md5_append(&filemd5state, buffer, len);
 

	
 
		if (ferror(f) && warn) fprintf(stderr, "Error Reading from %s \n", buf);
 
		fclose(f);
 

	
 
		md5_finish(&filemd5state, digest);
 
		return CheckMD5Digest(file, digest, warn);
 
	} else { // file not found
 
		return false;
 
	}
 
}
 

	
 
/* Checks, if either the Windows files exist (TRG1R.GRF) or the DOS files (TRG1.GRF)
 
 * by comparing the MD5 checksums of the files. _use_dos_palette is set accordingly.
 
 * If neither are found, Windows palette is assumed.
 
 *
 
 * (Note: Also checks sample.cat for corruption) */
 
void CheckExternalFiles(void)
 
{
 
	uint i;
 
	// count of files from this version
 
	uint dos = 0;
 
	uint win = 0;
 

	
 
	for (i = 0; i < 2; i++) if (FileMD5(files_dos.basic[i], true)) dos++;
 
	for (i = 0; i < 3; i++) if (FileMD5(files_dos.landscape[i], true)) dos++;
 

	
 
	for (i = 0; i < 2; i++) if (FileMD5(files_win.basic[i], true)) win++;
 
	for (i = 0; i < 3; i++) if (FileMD5(files_win.landscape[i], true)) win++;
 

	
 
	if (!FileMD5(sample_cat_win, false) && !FileMD5(sample_cat_dos, false))
 
		ShowInfo("Your 'sample.cat' file is corrupted or missing!");
 

	
 
	for (i = 0; i < lengthof(files_openttd); i++) {
 
		if (!FileMD5(files_openttd[i], false)) {
 
			ShowInfoF("Your '%s' file is corrupted or missing!", files_openttd[i].filename);
 
		}
 
	}
 

	
 
	/*
 
	 * forced DOS palette via command line -> leave it that way
 
	 * all Windows files present -> Windows palette
 
	 * all DOS files present -> DOS palette
 
	 * no Windows files present and any DOS file present -> DOS palette
 
	 * otherwise -> Windows palette
 
	 */
 
	if (_use_dos_palette) {
 
		return;
 
	} else if (win == 5) {
 
		_use_dos_palette = false;
 
	} else if (dos == 5 || (win == 0 && dos > 0)) {
 
		_use_dos_palette = true;
 
	} else {
 
		_use_dos_palette = false;
 
	}
 
}
 

	
 

	
 
static const SpriteID trg1idx[] = {
 
	   0,    1, // Mouse cursor, ZZZ
 
/* Medium font */
 
	   2,   92, // ' ' till 'z'
 
	SKIP,   36,
 
	 160,  160, // Move ¾ to the correct position
 
	  98,   98, // Up arrow
 
	 131,  133,
 
	SKIP,    1, // skip currency sign
 
	 135,  135,
 
	SKIP,    1,
 
	 137,  137,
 
	SKIP,    1,
 
	 139,  139,
 
	 140,  140, // TODO Down arrow
 
	 141,  141,
 
	 142,  142, // TODO Check mark
 
	 143,  143, // TODO Cross
 
	 144,  144,
 
	 145,  145, // TODO Right arrow
 
	 146,  149,
 
	 118,  122, // Transport markers
 
	SKIP,    2,
 
	 157,  157,
 
	 114,  115, // Small up/down arrows
 
	SKIP,    1,
 
	 161,  225,
 
/* Small font */
 
	 226,  316, // ' ' till 'z'
 
	SKIP,   36,
 
	 384,  384, // Move ¾ to the correct position
 
	 322,  322, // Up arrow
 
	 355,  357,
 
	SKIP,    1, // skip currency sign
 
	 359,  359,
 
	SKIP,    1,
 
	 361,  361,
 
	SKIP,    1,
 
	 363,  363,
 
	 364,  364, // TODO Down arrow
 
	 365,  366,
 
	SKIP,    1,
 
	 368,  368,
 
	 369,  369, // TODO Right arrow
 
	 370,  373,
 
	SKIP,    7,
 
	 381,  381,
 
	SKIP,    3,
 
	 385,  449,
 
/* Big font */
 
	 450,  540, // ' ' till 'z'
 
	SKIP,   36,
 
	 608,  608, // Move ¾ to the correct position
 
	SKIP,    1,
 
	 579,  581,
 
	SKIP,    1,
 
	 583,  583,
 
	SKIP,    5,
 
	 589,  589,
 
	SKIP,   15,
 
	 605,  605,
 
	SKIP,    3,
 
	 609,  625,
 
	SKIP,    1,
 
	 627,  632,
 
	SKIP,    1,
 
	 634,  639,
 
	SKIP,    1,
 
	 641,  657,
 
	SKIP,    1,
 
	 659,  664,
 
	SKIP,    2,
 
	 667,  671,
 
	SKIP,    1,
 
	 673,  673,
 
/* Graphics */
 
	 674, 4792,
 
	END
 
};
 

	
 
/* NOTE: When adding a normal sprite, increase OPENTTD_SPRITES_COUNT with the
 
 * amount of sprites and add them to the end of the list, with the index of
 
 * the old sprite-count offset from SPR_OPENTTD_BASE. With this there is no
 
 * correspondence of any kind with the ID's in the grf file, but results in
 
 * a maximum use of sprite slots. */
 
static const SpriteID _openttd_grf_indexes[] = {
 
	SPR_IMG_AUTORAIL, SPR_CURSOR_WAYPOINT, // icons etc
 
	134, 134,  // euro symbol medium size
 
	582, 582,  // euro symbol large size
 
	358, 358,  // euro symbol tiny
 
	SPR_CURSOR_CANAL, SPR_IMG_FASTFORWARD, // more icons
 
	648, 648, // nordic char: æ
 
	616, 616, // nordic char: Æ
 
	666, 666, // nordic char: ø
 
	634, 634, // nordic char: Ø
 
	SPR_PIN_UP, SPR_CURSOR_CLONE_TRAIN, // more icons
 
	382, 383, // ¼ ½ tiny
 
	158, 159, // ¼ ½ medium
 
	606, 607, // ¼ ½ large
 
	360, 360, // ¦ tiny
 
	362, 362, // ¨ tiny
 
	136, 136, // ¦ medium
 
	138, 138, // ¨ medium
 
	584, 584, // ¦ large
 
	586, 586, // ¨ large
 
	626, 626, // Ð large
 
	658, 658, // ð large
 
	374, 374, // ´ tiny
 
	378, 378, // ¸ tiny
 
	150, 150, // ´ medium
 
	154, 154, // ¸ medium
 
	598, 598, // ´ large
 
	602, 602, // ¸ large
 
	640, 640, // Þ large
 
	672, 672, // þ large
 
	380, 380, // º tiny
 
	156, 156, // º medium
 
	604, 604, // º large
 
	317, 320, // { | } ~ tiny
 
	 93,  96, // { | } ~ medium
 
	541, 544, // { | } ~ large
 
	SPR_HOUSE_ICON, SPR_HOUSE_ICON,
 
	585, 585, // § large
 
	587, 587, // © large
 
	592, 592, // ® large
 
	594, 597, // ° ± ² ³ large
 
	633, 633, // × large
 
	665, 665, // ÷ large
 
	SPR_SELL_TRAIN, SPR_SHARED_ORDERS_ICON,
 
	377, 377, // · small
 
	153, 153, // · medium
 
	601, 601, // · large
 
	END
 
};
 

	
 

	
 
static void LoadSpriteTables(void)
 
{
 
	const FileList* files = _use_dos_palette ? &files_dos : &files_win;
 
	uint load_index;
 
	uint i;
 

	
 
	LoadGrfIndexed(files->basic[0].filename, trg1idx, 0);
 
	DupSprite(  2, 130); // non-breaking space medium
 
	DupSprite(226, 354); // non-breaking space tiny
 
	DupSprite(450, 578); // non-breaking space large
 
	load_index = 4793;
 

	
 
	for (i = 1; files->basic[i].filename != NULL; i++) {
 
		load_index += LoadGrfFile(files->basic[i].filename, load_index, i);
 
	}
 

	
 
	/* Load additional sprites for climates other than temperate */
 
	if (_opt.landscape != LT_NORMAL) {
 
		LoadGrfIndexed(
 
			files->landscape[_opt.landscape - 1].filename,
 
			_landscape_spriteindexes[_opt.landscape - 1],
 
			i++
 
		);
 
	}
 

	
 
	assert(load_index == SPR_SIGNALS_BASE);
 
	load_index += LoadGrfFile("nsignalsw.grf", load_index, i++);
 

	
 
	assert(load_index == SPR_CANALS_BASE);
 
	load_index += LoadGrfFile("canalsw.grf", load_index, i++);
 

	
 
	assert(load_index == SPR_SLOPES_BASE);
 
	LoadGrfIndexed("trkfoundw.grf", _slopes_spriteindexes[_opt.landscape], i++);
 

	
 
	load_index = SPR_AUTORAIL_BASE;
 
	load_index += LoadGrfFile("autorail.grf", load_index, i++);
 

	
 
	assert(load_index == SPR_ELRAIL_BASE);
 
	load_index += LoadGrfFile("elrailsw.grf", load_index, i++);
 

	
 
	assert(load_index == SPR_2CCMAP_BASE);
 
	load_index += LoadGrfFile("2ccmap.grf", load_index, i++);
 

	
 
	assert(load_index == SPR_OPENTTD_BASE);
 
	LoadGrfIndexed("openttd.grf", _openttd_grf_indexes, i++);
 
	load_index = SPR_OPENTTD_BASE + OPENTTD_SPRITES_COUNT;
 

	
 
	assert(load_index == SPR_AIRPORTX_BASE);
 
	load_index += LoadGrfFile("airports.grf", load_index, i++);
 

	
 
	/* Initialize the unicode to sprite mapping table */
 
	InitializeUnicodeGlyphMap();
 

	
 
	LoadNewGRF(load_index, i);
 
}
 

	
 

	
 
void GfxLoadSprites(void)
 
{
 
	DEBUG(sprite, 2, "Loading sprite set %d", _opt.landscape);
 

	
 
	GfxInitSpriteMem();
 
	LoadSpriteTables();
 
	GfxInitPalettes();
 
}
src/graph_gui.c
Show inline comments
 
deleted file
src/graph_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "functions.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "player.h"
 
#include "economy.h"
 
#include "signs.h"
 
#include "strings.h"
 
#include "debug.h"
 
#include "variables.h"
 
#include "date.h"
 

	
 
const byte _cargo_colours[NUM_CARGO] = {152, 32, 15, 174, 208, 194, 191, 84, 184, 10, 202, 48};
 

	
 
static uint _legend_excludebits;
 
static uint _legend_cargobits;
 

	
 
/************************/
 
/* GENERIC GRAPH DRAWER */
 
/************************/
 

	
 
enum {GRAPH_NUM = 16};
 

	
 
typedef struct GraphDrawer {
 
	uint sel; // bitmask of the players *excluded* (e.g. 11111111 means that no players are shown)
 
	byte num_dataset;
 
	byte num_on_x_axis;
 
	byte month;
 
	Year year;
 
	bool include_neg;
 
	byte num_vert_lines;
 
	uint16 unk61A;
 
	uint16 unk61C;
 
	int left, top;
 
	uint height;
 
	StringID format_str_y_axis;
 
	byte color_3, color_2, bg_line_color;
 
	byte colors[GRAPH_NUM];
 
	uint64 cost[GRAPH_NUM][24]; // last 2 years
 
} GraphDrawer;
 

	
 
#define INVALID_VALUE 0x80000000
 

	
 
static void DrawGraph(const GraphDrawer *gw)
 
{
 

	
 
	int i,j,k;
 
	uint x,y,old_x,old_y;
 
	int color;
 
	int right, bottom;
 
	int num_x, num_dataset;
 
	const uint64 *row_ptr, *col_ptr;
 
	int64 mx;
 
	int adj_height;
 
	uint64 y_scaling, tmp;
 
	int64 value;
 
	int64 cur_val;
 
	uint sel;
 

	
 
	/* the colors and cost array of GraphDrawer must accomodate
 
	 * both values for cargo and players. So if any are higher, quit */
 
	assert(GRAPH_NUM >= NUM_CARGO && GRAPH_NUM >= MAX_PLAYERS);
 

	
 
	color = _colour_gradient[gw->bg_line_color][4];
 

	
 
	/* draw the vertical lines */
 
	i = gw->num_vert_lines; assert(i > 0);
 
	x = gw->left + 66;
 
	bottom = gw->top + gw->height - 1;
 
	do {
 
		GfxFillRect(x, gw->top, x, bottom, color);
 
		x += 22;
 
	} while (--i);
 

	
 
	/* draw the horizontal lines */
 
	i = 9;
 
	x = gw->left + 44;
 
	y = gw->height + gw->top;
 
	right = gw->left + 44 + gw->num_vert_lines*22-1;
 

	
 
	do {
 
		GfxFillRect(x, y, right, y, color);
 
		y -= gw->height >> 3;
 
	} while (--i);
 

	
 
	/* draw vertical edge line */
 
	GfxFillRect(x, gw->top, x, bottom, gw->color_2);
 

	
 
	adj_height = gw->height;
 
	if (gw->include_neg) adj_height >>= 1;
 

	
 
	/* draw horiz edge line */
 
	y = adj_height + gw->top;
 
	GfxFillRect(x, y, right, y, gw->color_2);
 

	
 
	/* find the max element */
 
	if (gw->num_on_x_axis == 0)
 
		return;
 

	
 
	num_dataset = gw->num_dataset;
 
	assert(num_dataset > 0);
 

	
 
	row_ptr = gw->cost[0];
 
	mx = 0;
 
		/* bit selection for the showing of various players, base max element
 
		 * on to-be shown player-information. This way the graph can scale */
 
	sel = gw->sel;
 
	do {
 
		if (!(sel&1)) {
 
			num_x = gw->num_on_x_axis;
 
			assert(num_x > 0);
 
			col_ptr = row_ptr;
 
			do {
 
				if (*col_ptr != INVALID_VALUE) {
 
					mx = max64(mx, myabs64(*col_ptr));
 
				}
 
			} while (col_ptr++, --num_x);
 
		}
 
	} while (sel>>=1, row_ptr+=24, --num_dataset);
 

	
 
	/* setup scaling */
 
	y_scaling = INVALID_VALUE;
 
	value = adj_height * 2;
 

	
 
	if (mx > value) {
 
		mx = (mx + 7) & ~7;
 
		y_scaling = (((uint64) (value>>1) << 32) / mx);
 
		value = mx;
 
	}
 

	
 
	/* draw text strings on the y axis */
 
	tmp = value;
 
	if (gw->include_neg) tmp >>= 1;
 
	x = gw->left + 45;
 
	y = gw->top - 3;
 
	i = 9;
 
	do {
 
		SetDParam(0, gw->format_str_y_axis);
 
		SetDParam64(1, (int64)tmp);
 
		tmp -= (value >> 3);
 
		DrawStringRightAligned(x, y, STR_0170, gw->color_3);
 
		y += gw->height >> 3;
 
	} while (--i);
 

	
 
	/* draw strings on the x axis */
 
	if (gw->month != 0xFF) {
 
		x = gw->left + 44;
 
		y = gw->top + gw->height + 1;
 
		j = gw->month;
 
		k = gw->year;
 
		i = gw->num_on_x_axis;assert(i>0);
 
		do {
 
			SetDParam(2, k);
 
			SetDParam(0, j + STR_0162_JAN);
 
			SetDParam(1, j + STR_0162_JAN + 2);
 
			DrawString(x, y, j == 0 ? STR_016F : STR_016E, gw->color_3);
 

	
 
			j += 3;
 
			if (j >= 12) {
 
				j = 0;
 
				k++;
 
			}
 
			x += 22;
 
		} while (--i);
 
	} else {
 
		x = gw->left + 52;
 
		y = gw->top + gw->height + 1;
 
		j = gw->unk61A;
 
		i = gw->num_on_x_axis;assert(i>0);
 
		do {
 
			SetDParam(0, j);
 
			DrawString(x, y, STR_01CB, gw->color_3);
 
			j += gw->unk61C;
 
			x += 22;
 
		} while (--i);
 
	}
 

	
 
	/* draw lines and dots */
 
	i = 0;
 
	row_ptr = gw->cost[0];
 
	sel = gw->sel; // show only selected lines. GraphDrawer qw->sel set in Graph-Legend (_legend_excludebits)
 
	do {
 
		if (!(sel & 1)) {
 
			x = gw->left + 55;
 
			j = gw->num_on_x_axis;assert(j>0);
 
			col_ptr = row_ptr;
 
			color = gw->colors[i];
 
			old_y = old_x = INVALID_VALUE;
 
			do {
 
				cur_val = *col_ptr++;
 
				if (cur_val != INVALID_VALUE) {
 
					y = adj_height - BIGMULSS64(cur_val, y_scaling >> 1, 31) + gw->top;
 

	
 
					GfxFillRect(x-1, y-1, x+1, y+1, color);
 
					if (old_x != INVALID_VALUE)
 
						GfxDrawLine(old_x, old_y, x, y, color);
 

	
 
					old_x = x;
 
					old_y = y;
 
				} else {
 
					old_x = INVALID_VALUE;
 
				}
 
			} while (x+=22,--j);
 
		}
 
	} while (sel>>=1,row_ptr+=24, ++i < gw->num_dataset);
 
}
 

	
 
/****************/
 
/* GRAPH LEGEND */
 
/****************/
 

	
 
void DrawPlayerIcon(PlayerID p, int x, int y)
 
{
 
	DrawSprite(SPRITE_PALETTE(PLAYER_SPRITE_COLOR(p) + 0x2EB), x, y);
 
}
 

	
 
static void GraphLegendWndProc(Window *w, WindowEvent *e)
 
{
 
	const Player* p;
 

	
 
	switch (e->event) {
 
	case WE_CREATE: {
 
		uint i;
 
		for (i = 3; i < w->widget_count; i++) {
 
			if (!HASBIT(_legend_excludebits, i - 3)) LowerWindowWidget(w, i);
 
		}
 
		break;
 
	}
 

	
 
	case WE_PAINT:
 
		FOR_ALL_PLAYERS(p) {
 
			if (!p->is_active) {
 
				SETBIT(_legend_excludebits, p->index);
 
				RaiseWindowWidget(w, p->index + 3);
 
			}
 
		}
 
		DrawWindowWidgets(w);
 

	
 
		FOR_ALL_PLAYERS(p) {
 
			if (!p->is_active) continue;
 

	
 
			DrawPlayerIcon(p->index, 4, 18+p->index*12);
 

	
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			SetDParam(2, GetPlayerNameString(p->index, 3));
 
			DrawString(21,17+p->index*12,STR_7021,HASBIT(_legend_excludebits, p->index) ? 0x10 : 0xC);
 
		}
 
		break;
 

	
 
	case WE_CLICK:
 
		if (IS_INT_INSIDE(e->we.click.widget, 3, 11)) {
 
			_legend_excludebits ^= (1 << (e->we.click.widget - 3));
 
			ToggleWidgetLoweredState(w, e->we.click.widget);
 
			SetWindowDirty(w);
 
			InvalidateWindow(WC_INCOME_GRAPH, 0);
 
			InvalidateWindow(WC_OPERATING_PROFIT, 0);
 
			InvalidateWindow(WC_DELIVERED_CARGO, 0);
 
			InvalidateWindow(WC_PERFORMANCE_HISTORY, 0);
 
			InvalidateWindow(WC_COMPANY_VALUE, 0);
 
		}
 
		break;
 
	}
 
}
 

	
 
static const Widget _graph_legend_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   249,     0,    13, STR_704E_KEY_TO_COMPANY_GRAPHS, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   249,    14,   113, 0x0,                            STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    16,    27, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    28,    39, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    40,    51, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    52,    63, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    64,    75, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    76,    87, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,    88,    99, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,   247,   100,   111, 0x0,                            STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _graph_legend_desc = {
 
	WDP_AUTO, WDP_AUTO, 250, 114,
 
	WC_GRAPH_LEGEND,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_graph_legend_widgets,
 
	GraphLegendWndProc
 
};
 

	
 
static void ShowGraphLegend(void)
 
{
 
	AllocateWindowDescFront(&_graph_legend_desc, 0);
 
}
 

	
 
/********************/
 
/* OPERATING PROFIT */
 
/********************/
 

	
 
static void SetupGraphDrawerForPlayers(GraphDrawer *gd)
 
{
 
	const Player* p;
 
	uint excludebits = _legend_excludebits;
 
	int nums;
 
	int mo,yr;
 

	
 
	// Exclude the players which aren't valid
 
	FOR_ALL_PLAYERS(p) {
 
		if (!p->is_active) SETBIT(excludebits,p->index);
 
	}
 
	gd->sel = excludebits;
 
	gd->num_vert_lines = 24;
 

	
 
	nums = 0;
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active) nums = max(nums,p->num_valid_stat_ent);
 
	}
 
	gd->num_on_x_axis = min(nums,24);
 

	
 
	mo = (_cur_month/3-nums)*3;
 
	yr = _cur_year;
 
	while (mo < 0) {
 
		yr--;
 
		mo += 12;
 
	}
 

	
 
	gd->year = yr;
 
	gd->month = mo;
 
}
 

	
 
static void OperatingProfitWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		GraphDrawer gd;
 
		const Player* p;
 
		int i,j;
 
		int numd;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		gd.left = 2;
 
		gd.top = 18;
 
		gd.height = 136;
 
		gd.include_neg = true;
 
		gd.format_str_y_axis = STR_CURRCOMPACT;
 
		gd.color_3 = 0x10;
 
		gd.color_2 = 0xD7;
 
		gd.bg_line_color = 0xE;
 

	
 
		SetupGraphDrawerForPlayers(&gd);
 

	
 
		numd = 0;
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active) {
 
				gd.colors[numd] = _colour_gradient[p->player_color][6];
 
				for (j = gd.num_on_x_axis, i = 0; --j >= 0;) {
 
					gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_VALUE : (uint64)(p->old_economy[j].income + p->old_economy[j].expenses);
 
					i++;
 
				}
 
			}
 
			numd++;
 
		}
 

	
 
		gd.num_dataset = numd;
 

	
 
		DrawGraph(&gd);
 
	}	break;
 
	case WE_CLICK:
 
		if (e->we.click.widget == 2) /* Clicked on Legend */
 
			ShowGraphLegend();
 
		break;
 
	}
 
}
 

	
 
static const Widget _operating_profit_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   525,     0,    13, STR_7025_OPERATING_PROFIT_GRAPH, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   526,   575,     0,    13, STR_704C_KEY,                    STR_704D_SHOW_KEY_TO_GRAPHS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   575,    14,   173, 0x0,                             STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _operating_profit_desc = {
 
	WDP_AUTO, WDP_AUTO, 576, 174,
 
	WC_OPERATING_PROFIT,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_operating_profit_widgets,
 
	OperatingProfitWndProc
 
};
 

	
 

	
 
void ShowOperatingProfitGraph(void)
 
{
 
	if (AllocateWindowDescFront(&_operating_profit_desc, 0)) {
 
		InvalidateWindow(WC_GRAPH_LEGEND, 0);
 
	}
 
}
 

	
 

	
 
/****************/
 
/* INCOME GRAPH */
 
/****************/
 

	
 
static void IncomeGraphWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		GraphDrawer gd;
 
		const Player* p;
 
		int i,j;
 
		int numd;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		gd.left = 2;
 
		gd.top = 18;
 
		gd.height = 104;
 
		gd.include_neg = false;
 
		gd.format_str_y_axis = STR_CURRCOMPACT;
 
		gd.color_3 = 0x10;
 
		gd.color_2 = 0xD7;
 
		gd.bg_line_color = 0xE;
 
		SetupGraphDrawerForPlayers(&gd);
 

	
 
		numd = 0;
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active) {
 
				gd.colors[numd] = _colour_gradient[p->player_color][6];
 
				for (j = gd.num_on_x_axis, i = 0; --j >= 0;) {
 
					gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_VALUE : (uint64)p->old_economy[j].income;
 
					i++;
 
				}
 
			}
 
			numd++;
 
		}
 

	
 
		gd.num_dataset = numd;
 

	
 
		DrawGraph(&gd);
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		if (e->we.click.widget == 2)
 
			ShowGraphLegend();
 
		break;
 
	}
 
}
 

	
 
static const Widget _income_graph_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   525,     0,    13, STR_7022_INCOME_GRAPH, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   526,   575,     0,    13, STR_704C_KEY,          STR_704D_SHOW_KEY_TO_GRAPHS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   575,    14,   141, 0x0,                   STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _income_graph_desc = {
 
	WDP_AUTO, WDP_AUTO, 576, 142,
 
	WC_INCOME_GRAPH,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_income_graph_widgets,
 
	IncomeGraphWndProc
 
};
 

	
 
void ShowIncomeGraph(void)
 
{
 
	if (AllocateWindowDescFront(&_income_graph_desc, 0)) {
 
		InvalidateWindow(WC_GRAPH_LEGEND, 0);
 
	}
 
}
 

	
 
/*******************/
 
/* DELIVERED CARGO */
 
/*******************/
 

	
 
static void DeliveredCargoGraphWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		GraphDrawer gd;
 
		const Player* p;
 
		int i,j;
 
		int numd;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		gd.left = 2;
 
		gd.top = 18;
 
		gd.height = 104;
 
		gd.include_neg = false;
 
		gd.format_str_y_axis = STR_7024;
 
		gd.color_3 = 0x10;
 
		gd.color_2 = 0xD7;
 
		gd.bg_line_color = 0xE;
 
		SetupGraphDrawerForPlayers(&gd);
 

	
 
		numd = 0;
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active) {
 
				gd.colors[numd] = _colour_gradient[p->player_color][6];
 
				for (j = gd.num_on_x_axis, i = 0; --j >= 0;) {
 
					gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_VALUE : (uint64)p->old_economy[j].delivered_cargo;
 
					i++;
 
				}
 
			}
 
			numd++;
 
		}
 

	
 
		gd.num_dataset = numd;
 

	
 
		DrawGraph(&gd);
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		if (e->we.click.widget == 2)
 
			ShowGraphLegend();
 
		break;
 
	}
 
}
 

	
 
static const Widget _delivered_cargo_graph_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                          STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   525,     0,    13, STR_7050_UNITS_OF_CARGO_DELIVERED, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   526,   575,     0,    13, STR_704C_KEY,                      STR_704D_SHOW_KEY_TO_GRAPHS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   575,    14,   141, 0x0,                               STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _delivered_cargo_graph_desc = {
 
	WDP_AUTO, WDP_AUTO, 576, 142,
 
	WC_DELIVERED_CARGO,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_delivered_cargo_graph_widgets,
 
	DeliveredCargoGraphWndProc
 
};
 

	
 
void ShowDeliveredCargoGraph(void)
 
{
 
	if (AllocateWindowDescFront(&_delivered_cargo_graph_desc, 0)) {
 
		InvalidateWindow(WC_GRAPH_LEGEND, 0);
 
	}
 
}
 

	
 
/***********************/
 
/* PERFORMANCE HISTORY */
 
/***********************/
 

	
 
static void PerformanceHistoryWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		GraphDrawer gd;
 
		const Player* p;
 
		int i,j;
 
		int numd;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		gd.left = 2;
 
		gd.top = 18;
 
		gd.height = 200;
 
		gd.include_neg = false;
 
		gd.format_str_y_axis = STR_7024;
 
		gd.color_3 = 0x10;
 
		gd.color_2 = 0xD7;
 
		gd.bg_line_color = 0xE;
 
		SetupGraphDrawerForPlayers(&gd);
 

	
 
		numd = 0;
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active) {
 
				gd.colors[numd] = _colour_gradient[p->player_color][6];
 
				for (j = gd.num_on_x_axis, i = 0; --j >= 0;) {
 
					gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_VALUE : (uint64)p->old_economy[j].performance_history;
 
					i++;
 
				}
 
			}
 
			numd++;
 
		}
 

	
 
		gd.num_dataset = numd;
 

	
 
		DrawGraph(&gd);
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		if (e->we.click.widget == 2)
 
			ShowGraphLegend();
 
		if (e->we.click.widget == 3)
 
			ShowPerformanceRatingDetail();
 
		break;
 
	}
 
}
 

	
 
static const Widget _performance_history_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                             STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   475,     0,    13, STR_7051_COMPANY_PERFORMANCE_RATINGS, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   526,   575,     0,    13, STR_704C_KEY,                         STR_704D_SHOW_KEY_TO_GRAPHS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   476,   525,     0,    13, STR_PERFORMANCE_DETAIL_KEY,           STR_704D_SHOW_KEY_TO_GRAPHS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   575,    14,   237, 0x0,                                  STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _performance_history_desc = {
 
	WDP_AUTO, WDP_AUTO, 576, 238,
 
	WC_PERFORMANCE_HISTORY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_performance_history_widgets,
 
	PerformanceHistoryWndProc
 
};
 

	
 
void ShowPerformanceHistoryGraph(void)
 
{
 
	if (AllocateWindowDescFront(&_performance_history_desc, 0)) {
 
		InvalidateWindow(WC_GRAPH_LEGEND, 0);
 
	}
 
}
 

	
 
/*****************/
 
/* COMPANY VALUE */
 
/*****************/
 

	
 
static void CompanyValueGraphWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		GraphDrawer gd;
 
		const Player* p;
 
		int i,j;
 
		int numd;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		gd.left = 2;
 
		gd.top = 18;
 
		gd.height = 200;
 
		gd.include_neg = false;
 
		gd.format_str_y_axis = STR_CURRCOMPACT;
 
		gd.color_3 = 0x10;
 
		gd.color_2 = 0xD7;
 
		gd.bg_line_color = 0xE;
 
		SetupGraphDrawerForPlayers(&gd);
 

	
 
		numd = 0;
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active) {
 
				gd.colors[numd] = _colour_gradient[p->player_color][6];
 
				for (j = gd.num_on_x_axis, i = 0; --j >= 0;) {
 
					gd.cost[numd][i] = (j >= p->num_valid_stat_ent) ? INVALID_VALUE : (uint64)p->old_economy[j].company_value;
 
					i++;
 
				}
 
			}
 
			numd++;
 
		}
 

	
 
		gd.num_dataset = numd;
 

	
 
		DrawGraph(&gd);
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		if (e->we.click.widget == 2)
 
			ShowGraphLegend();
 
		break;
 
	}
 
}
 

	
 
static const Widget _company_value_graph_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   525,     0,    13, STR_7052_COMPANY_VALUES, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   526,   575,     0,    13, STR_704C_KEY,            STR_704D_SHOW_KEY_TO_GRAPHS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   575,    14,   237, 0x0,                     STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _company_value_graph_desc = {
 
	WDP_AUTO, WDP_AUTO, 576, 238,
 
	WC_COMPANY_VALUE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_company_value_graph_widgets,
 
	CompanyValueGraphWndProc
 
};
 

	
 
void ShowCompanyValueGraph(void)
 
{
 
	if (AllocateWindowDescFront(&_company_value_graph_desc, 0)) {
 
		InvalidateWindow(WC_GRAPH_LEGEND, 0);
 
	}
 
}
 

	
 
/*****************/
 
/* PAYMENT RATES */
 
/*****************/
 

	
 
static void CargoPaymentRatesWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: {
 
		uint i;
 
		for (i = 3; i < w->widget_count; i++) {
 
			if (!HASBIT(_legend_cargobits, i - 3)) LowerWindowWidget(w, i);
 
		}
 
		break;
 
	}
 

	
 
	case WE_PAINT: {
 
		int j, x, y;
 
		CargoID i;
 
		GraphDrawer gd;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		x = 495;
 
		y = 24;
 

	
 
		gd.sel = _legend_cargobits;
 
		gd.left = 2;
 
		gd.top = 24;
 
		gd.height = 104;
 
		gd.include_neg = false;
 
		gd.format_str_y_axis = STR_CURRCOMPACT;
 
		gd.color_3 = 16;
 
		gd.color_2 = 215;
 
		gd.bg_line_color = 14;
 
		gd.num_dataset = NUM_CARGO;
 
		gd.num_on_x_axis = 20;
 
		gd.num_vert_lines = 20;
 
		gd.month = 0xFF;
 
		gd.unk61A = 10;
 
		gd.unk61C = 10;
 

	
 
		for (i = 0; i != NUM_CARGO; i++) {
 
			/* Since the buttons have no text, no images,
 
			 * both the text and the colored box have to be manually painted.
 
			 * clk_dif will move one pixel down and one pixel to the right
 
			 * when the button is clicked */
 
			byte clk_dif = IsWindowWidgetLowered(w, i + 3) ? 1 : 0;
 

	
 
			GfxFillRect(x + clk_dif, y + clk_dif, x + 8 + clk_dif, y + 5 + clk_dif, 0);
 
			GfxFillRect(x + 1 + clk_dif, y + 1 + clk_dif, x + 7 + clk_dif, y + 4 + clk_dif, _cargo_colours[i]);
 
			SetDParam(0, _cargoc.names_s[i]);
 
			DrawString(x + 14 + clk_dif, y + clk_dif, STR_7065, 0);
 
			y += 8;
 
			gd.colors[i] = _cargo_colours[i];
 
			for (j = 0; j != 20; j++) {
 
				gd.cost[i][j] = (uint64)GetTransportedGoodsIncome(10, 20, j * 6 + 6, i);
 
			}
 
		}
 

	
 
		DrawGraph(&gd);
 

	
 
		DrawString(2 + 46, 24 + gd.height + 7, STR_7062_DAYS_IN_TRANSIT, 0);
 
		DrawString(2 + 84, 24 - 9, STR_7063_PAYMENT_FOR_DELIVERING, 0);
 
	} break;
 

	
 
	case WE_CLICK: {
 
		switch (e->we.click.widget) {
 
		case 3: case 4: case 5: case 6:
 
		case 7: case 8: case 9: case 10:
 
		case 11: case 12: case 13: case 14:
 
			TOGGLEBIT(_legend_cargobits, e->we.click.widget - 3);
 
			ToggleWidgetLoweredState(w, e->we.click.widget);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
	} break;
 
	}
 
}
 

	
 
static const Widget _cargo_payment_rates_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                     STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   567,     0,    13, STR_7061_CARGO_PAYMENT_RATES, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   567,    14,   141, 0x0,                          STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,    24,    31, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,    32,    39, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,    40,    47, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,    48,    55, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,    56,    63, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,    64,    71, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,    72,    79, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,    80,    87, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,    88,    95, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,    96,   103, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,   104,   111, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{      WWT_PANEL,   RESIZE_NONE,    12,   493,   562,   112,   119, 0x0,                          STR_7064_TOGGLE_GRAPH_FOR_CARGO},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _cargo_payment_rates_desc = {
 
	WDP_AUTO, WDP_AUTO, 568, 142,
 
	WC_PAYMENT_RATES,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_cargo_payment_rates_widgets,
 
	CargoPaymentRatesWndProc
 
};
 

	
 

	
 
void ShowCargoPaymentRates(void)
 
{
 
	AllocateWindowDescFront(&_cargo_payment_rates_desc, 0);
 
}
 

	
 
/************************/
 
/* COMPANY LEAGUE TABLE */
 
/************************/
 

	
 
static const StringID _performance_titles[] = {
 
	STR_7066_ENGINEER,
 
	STR_7066_ENGINEER,
 
	STR_7067_TRAFFIC_MANAGER,
 
	STR_7067_TRAFFIC_MANAGER,
 
	STR_7068_TRANSPORT_COORDINATOR,
 
	STR_7068_TRANSPORT_COORDINATOR,
 
	STR_7069_ROUTE_SUPERVISOR,
 
	STR_7069_ROUTE_SUPERVISOR,
 
	STR_706A_DIRECTOR,
 
	STR_706A_DIRECTOR,
 
	STR_706B_CHIEF_EXECUTIVE,
 
	STR_706B_CHIEF_EXECUTIVE,
 
	STR_706C_CHAIRMAN,
 
	STR_706C_CHAIRMAN,
 
	STR_706D_PRESIDENT,
 
	STR_706E_TYCOON,
 
};
 

	
 
static inline StringID GetPerformanceTitleFromValue(uint value)
 
{
 
	return _performance_titles[minu(value, 1000) >> 6];
 
}
 

	
 
static int CDECL PerfHistComp(const void* elem1, const void* elem2)
 
{
 
	const Player* p1 = *(const Player* const*)elem1;
 
	const Player* p2 = *(const Player* const*)elem2;
 

	
 
	return p2->old_economy[1].performance_history - p1->old_economy[1].performance_history;
 
}
 

	
 
static void CompanyLeagueWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			const Player* plist[MAX_PLAYERS];
 
			const Player* p;
 
			uint pl_num;
 
			uint i;
 

	
 
			DrawWindowWidgets(w);
 

	
 
			pl_num = 0;
 
			FOR_ALL_PLAYERS(p) if (p->is_active) plist[pl_num++] = p;
 

	
 
			qsort((void*)plist, pl_num, sizeof(*plist), PerfHistComp);
 

	
 
			for (i = 0; i != pl_num; i++) {
 
				p = plist[i];
 
				SetDParam(0, i + STR_01AC_1ST);
 
				SetDParam(1, p->name_1);
 
				SetDParam(2, p->name_2);
 
				SetDParam(3, GetPlayerNameString(p->index, 4));
 
				SetDParam(5, GetPerformanceTitleFromValue(p->old_economy[1].performance_history));
 

	
 
				DrawString(2, 15 + i * 10, i == 0 ? STR_7054 : STR_7055, 0);
 
				DrawPlayerIcon(p->index, 27, 16 + i * 10);
 
			}
 

	
 
			break;
 
		}
 
	}
 
}
 

	
 

	
 
static const Widget _company_league_widgets[] = {
 
{   WWT_CLOSEBOX, RESIZE_NONE, 14,   0,  10,  0, 13, STR_00C5,                      STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION, RESIZE_NONE, 14,  11, 387,  0, 13, STR_7053_COMPANY_LEAGUE_TABLE, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX, RESIZE_NONE, 14, 388, 399,  0, 13, STR_NULL,                      STR_STICKY_BUTTON},
 
{      WWT_PANEL, RESIZE_NONE, 14,   0, 399, 14, 96, 0x0,                           STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _company_league_desc = {
 
	WDP_AUTO, WDP_AUTO, 400, 97,
 
	WC_COMPANY_LEAGUE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
 
	_company_league_widgets,
 
	CompanyLeagueWndProc
 
};
 

	
 
void ShowCompanyLeagueTable(void)
 
{
 
	AllocateWindowDescFront(&_company_league_desc,0);
 
}
 

	
 
/*****************************/
 
/* PERFORMANCE RATING DETAIL */
 
/*****************************/
 

	
 
static void PerformanceRatingDetailWndProc(Window *w, WindowEvent *e)
 
{
 
	static PlayerID _performance_rating_detail_player = 0;
 

	
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			int i;
 
			byte x;
 
			uint16 y = 14;
 
			int total_score = 0;
 
			int color_done, color_notdone;
 

	
 
			// Draw standard stuff
 
			DrawWindowWidgets(w);
 

	
 
			// Paint the player icons
 
			for (i = 0; i < MAX_PLAYERS; i++) {
 
				if (!GetPlayer(i)->is_active) {
 
					// Check if we have the player as an active player
 
					if (!IsWindowWidgetDisabled(w, i + 13)) {
 
						// Bah, player gone :(
 
						DisableWindowWidget(w, i + 13);
 
						// Is this player selected? If so, select first player (always save? :s)
 
						if (IsWindowWidgetLowered(w, i + 13)) {
 
							RaiseWindowWidget(w, i + 13);
 
							LowerWindowWidget(w, 13);
 
							_performance_rating_detail_player = 0;
 
						}
 
						// We need a repaint
 
						SetWindowDirty(w);
 
					}
 
				continue;
 
				}
 

	
 
				// Check if we have the player marked as inactive
 
				if (IsWindowWidgetDisabled(w, i + 13)) {
 
					// New player! Yippie :p
 
					EnableWindowWidget(w, i + 13);
 
					// We need a repaint
 
					SetWindowDirty(w);
 
				}
 

	
 
				x = (i == _performance_rating_detail_player) ? 1 : 0;
 
				DrawPlayerIcon(i, i * 37 + 13 + x, 16 + x);
 
			}
 

	
 
			// The colors used to show how the progress is going
 
			color_done = _colour_gradient[COLOUR_GREEN][4];
 
			color_notdone = _colour_gradient[COLOUR_RED][4];
 

	
 
			// Draw all the score parts
 
			for (i = 0; i < NUM_SCORE; i++) {
 
				int val    = _score_part[_performance_rating_detail_player][i];
 
				int needed = _score_info[i].needed;
 
				int score  = _score_info[i].score;
 

	
 
				y += 20;
 
				// SCORE_TOTAL has his own rulez ;)
 
				if (i == SCORE_TOTAL) {
 
					needed = total_score;
 
					score = SCORE_MAX;
 
				} else {
 
					total_score += score;
 
				}
 

	
 
				DrawString(7, y, STR_PERFORMANCE_DETAIL_VEHICLES + i, 0);
 

	
 
				// Draw the score
 
				SetDParam(0, score);
 
				DrawStringRightAligned(107, y, SET_PERFORMANCE_DETAIL_INT, 0);
 

	
 
				// Calculate the %-bar
 
				if (val > needed) {
 
					x = 50;
 
				} else if (val == 0) {
 
					x = 0;
 
				} else {
 
					x = val * 50 / needed;
 
				}
 

	
 
				// SCORE_LOAN is inversed
 
				if (val < 0 && i == SCORE_LOAN) x = 0;
 

	
 
				// Draw the bar
 
				if (x !=  0) GfxFillRect(112,     y - 2, 112 + x,  y + 10, color_done);
 
				if (x != 50) GfxFillRect(112 + x, y - 2, 112 + 50, y + 10, color_notdone);
 

	
 
				// Calculate the %
 
				x = (val <= needed) ? val * 100 / needed : 100;
 

	
 
				// SCORE_LOAN is inversed
 
				if (val < 0 && i == SCORE_LOAN) x = 0;
 

	
 
				// Draw it
 
				SetDParam(0, x);
 
				DrawStringCentered(137, y, STR_PERFORMANCE_DETAIL_PERCENT, 0);
 

	
 
				// SCORE_LOAN is inversed
 
				if (i == SCORE_LOAN) val = needed - val;
 

	
 
				// Draw the amount we have against what is needed
 
				//  For some of them it is in currency format
 
				SetDParam(0, val);
 
				SetDParam(1, needed);
 
				switch (i) {
 
					case SCORE_MIN_PROFIT:
 
					case SCORE_MIN_INCOME:
 
					case SCORE_MAX_INCOME:
 
					case SCORE_MONEY:
 
					case SCORE_LOAN:
 
						DrawString(167, y, STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, 0);
 
						break;
 
					default:
 
						DrawString(167, y, STR_PERFORMANCE_DETAIL_AMOUNT_INT, 0);
 
				}
 
			}
 

	
 
			break;
 
		}
 

	
 
		case WE_CLICK:
 
			// Check which button is clicked
 
			if (IS_INT_INSIDE(e->we.click.widget, 13, 21)) {
 
				// Is it no on disable?
 
				if (!IsWindowWidgetDisabled(w, e->we.click.widget)) {
 
					RaiseWindowWidget(w, _performance_rating_detail_player + 13);
 
					_performance_rating_detail_player = e->we.click.widget - 13;
 
					LowerWindowWidget(w, _performance_rating_detail_player + 13);
 
					SetWindowDirty(w);
 
				}
 
			}
 
			break;
 

	
 
		case WE_CREATE: {
 
			int i;
 
			Player *p2;
 

	
 
			/* Disable the players who are not active */
 
			for (i = 0; i < MAX_PLAYERS; i++) {
 
				SetWindowWidgetDisabledState(w, i + 13, !GetPlayer(i)->is_active);
 
			}
 
			/* Update all player stats with the current data
 
			 * (this is because _score_info is not saved to a savegame) */
 
			FOR_ALL_PLAYERS(p2) {
 
				if (p2->is_active) UpdateCompanyRatingAndValue(p2, false);
 
			}
 

	
 
			w->custom[0] = DAY_TICKS;
 
			w->custom[1] = 5;
 

	
 
			_performance_rating_detail_player = 0;
 
			LowerWindowWidget(w, _performance_rating_detail_player + 13);
 
			SetWindowDirty(w);
 

	
 
			break;
 
		}
 

	
 
		case WE_TICK: {
 
			// Update the player score every 5 days
 
			if (--w->custom[0] == 0) {
 
				w->custom[0] = DAY_TICKS;
 
				if (--w->custom[1] == 0) {
 
					Player *p2;
 

	
 
					w->custom[1] = 5;
 
					FOR_ALL_PLAYERS(p2) {
 
						// Skip if player is not active
 
						if (p2->is_active) UpdateCompanyRatingAndValue(p2, false);
 
					}
 
					SetWindowDirty(w);
 
				}
 
			}
 

	
 
			break;
 
		}
 
	}
 
}
 

	
 
static const Widget _performance_rating_detail_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,               STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   298,     0,    13, STR_PERFORMANCE_DETAIL, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,    14,    27, 0x0,                    STR_NULL},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,    28,    47, 0x0,                    STR_PERFORMANCE_DETAIL_VEHICLES_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,    48,    67, 0x0,                    STR_PERFORMANCE_DETAIL_STATIONS_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,    68,    87, 0x0,                    STR_PERFORMANCE_DETAIL_MIN_PROFIT_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,    88,   107, 0x0,                    STR_PERFORMANCE_DETAIL_MIN_INCOME_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   108,   127, 0x0,                    STR_PERFORMANCE_DETAIL_MAX_INCOME_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   128,   147, 0x0,                    STR_PERFORMANCE_DETAIL_DELIVERED_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   148,   167, 0x0,                    STR_PERFORMANCE_DETAIL_CARGO_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   168,   187, 0x0,                    STR_PERFORMANCE_DETAIL_MONEY_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   188,   207, 0x0,                    STR_PERFORMANCE_DETAIL_LOAN_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   298,   208,   227, 0x0,                    STR_PERFORMANCE_DETAIL_TOTAL_TIP},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,    38,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    39,    75,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    76,   112,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   113,   149,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   150,   186,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   187,   223,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   224,   260,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   261,   297,    14,    26, 0x0,                    STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _performance_rating_detail_desc = {
 
	WDP_AUTO, WDP_AUTO, 299, 228,
 
	WC_PERFORMANCE_DETAIL,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_performance_rating_detail_widgets,
 
	PerformanceRatingDetailWndProc
 
};
 

	
 
void ShowPerformanceRatingDetail(void)
 
{
 
	AllocateWindowDescFront(&_performance_rating_detail_desc, 0);
 
}
 

	
 

	
 
static const Sign **_sign_sort;
 
static uint _num_sign_sort;
 

	
 
static char _bufcache[64];
 
static const Sign *_last_sign;
 

	
 
static int CDECL SignNameSorter(const void *a, const void *b)
 
{
 
	const Sign *sign0 = *(const Sign**)a;
 
	const Sign *sign1 = *(const Sign**)b;
 
	char buf1[64];
 

	
 
	GetString(buf1, sign0->str, lastof(buf1));
 

	
 
	if (sign1 != _last_sign) {
 
		_last_sign = sign1;
 
		GetString(_bufcache, sign1->str, lastof(_bufcache));
 
	}
 

	
 
	return strcmp(buf1, _bufcache); // sort by name
 
}
 

	
 
static void GlobalSortSignList(void)
 
{
 
	const Sign *si;
 
	uint n = 0;
 

	
 
	/* Create array for sorting */
 
	_sign_sort = realloc((void *)_sign_sort, (GetMaxSignIndex() + 1)* sizeof(_sign_sort[0]));
 
	if (_sign_sort == NULL) error("Could not allocate memory for the sign-sorting-list");
 

	
 
	FOR_ALL_SIGNS(si) _sign_sort[n++] = si;
 
	_num_sign_sort = n;
 

	
 
	qsort((void*)_sign_sort, n, sizeof(_sign_sort[0]), SignNameSorter);
 

	
 
	_sign_sort_dirty = false;
 

	
 
	DEBUG(misc, 3, "Resorting global signs list");
 
}
 

	
 
static void SignListWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		int y = 16; // offset from top of widget
 

	
 
		if (_sign_sort_dirty)
 
			GlobalSortSignList();
 

	
 
		SetVScrollCount(w, _num_sign_sort);
 

	
 
		SetDParam(0, w->vscroll.count);
 
		DrawWindowWidgets(w);
 

	
 
		/* No signs? */
 
		if (w->vscroll.count == 0) {
 
			DrawString(2, y, STR_304A_NONE, 0);
 
			return;
 
		}
 

	
 
		{
 
			uint16 i;
 

	
 
			/* Start drawing the signs */
 
			for (i = w->vscroll.pos; i < w->vscroll.cap + w->vscroll.pos && i < w->vscroll.count; i++) {
 
				const Sign *si = _sign_sort[i];
 

	
 
				if (si->owner != OWNER_NONE)
 
					DrawPlayerIcon(si->owner, 4, y + 1);
 

	
 
				DrawString(22, y, si->str, 8);
 
				y += 10;
 
			}
 
		}
 
	} break;
 

	
 
	case WE_CLICK: {
 
		switch (e->we.click.widget) {
 
		case 3: {
 
			uint32 id_v = (e->we.click.pt.y - 15) / 10;
 
			const Sign *si;
 

	
 
			if (id_v >= w->vscroll.cap)
 
				return;
 

	
 
			id_v += w->vscroll.pos;
 

	
 
			if (id_v >= w->vscroll.count)
 
				return;
 

	
 
			si = _sign_sort[id_v];
 
			ScrollMainWindowToTile(TileVirtXY(si->x, si->y));
 
		} break;
 
		}
 
	} break;
 

	
 
	case WE_RESIZE:
 
		w->vscroll.cap += e->we.sizing.diff.y / 10;
 
		break;
 
	}
 
}
 

	
 
static const Widget _sign_list_widget[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   345,     0,    13, STR_SIGN_LIST_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,     RESIZE_LR,    14,   346,   357,     0,    13, 0x0,                   STR_STICKY_BUTTON},
 
{      WWT_PANEL,     RESIZE_RB,    14,     0,   345,    14,   137, 0x0,                   STR_NULL},
 
{  WWT_SCROLLBAR,    RESIZE_LRB,    14,   346,   357,    14,   125, 0x0,                   STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   346,   357,   126,   137, 0x0,                   STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _sign_list_desc = {
 
	WDP_AUTO, WDP_AUTO, 358, 138,
 
	WC_SIGN_LIST,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_sign_list_widget,
 
	SignListWndProc
 
};
 

	
 

	
 
void ShowSignList(void)
 
{
 
	Window *w;
 

	
 
	w = AllocateWindowDescFront(&_sign_list_desc, 0);
 
	if (w != NULL) {
 
		w->vscroll.cap = 12;
 
		w->resize.step_height = 10;
 
		w->resize.height = w->height - 10 * 7; // minimum if 5 in the list
 
	}
 
}
src/heightmap.c
Show inline comments
 
deleted file
src/heightmap.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "variables.h"
 
#include "functions.h"
 
#include "heightmap.h"
 
#include "clear_map.h"
 
#include "table/strings.h"
 
#include "void_map.h"
 
#include "debug.h"
 
#include "gfx.h"
 
#include "gui.h"
 
#include "saveload.h"
 
#include "bmp.h"
 

	
 
/**
 
 * Convert RGB colors to Grayscale using 29.9% Red, 58.7% Green, 11.4% Blue
 
 *  (average luminosity formula) -- Dalestan
 
 * This in fact is the NTSC Color Space -- TrueLight
 
 */
 
static inline byte RGBToGrayscale(byte red, byte green, byte blue)
 
{
 
	/* To avoid doubles and stuff, multiple it with a total of 65536 (16bits), then
 
	 *  divide by it to normalize the value to a byte again. */
 
	return ((red * 19595) + (green * 38470) + (blue * 7471)) / 65536;
 
}
 

	
 

	
 
#ifdef WITH_PNG
 

	
 
#include "png.h"
 

	
 
/**
 
 * The PNG Heightmap loader.
 
 */
 
static void ReadHeightmapPNGImageData(byte *map, png_structp png_ptr, png_infop info_ptr)
 
{
 
	uint x, y;
 
	byte gray_palette[256];
 
	png_bytep *row_pointers = NULL;
 

	
 
	/* Get palette and convert it to grayscale */
 
	if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
 
		int i;
 
		int palette_size;
 
		png_color *palette;
 
		bool all_gray = true;
 

	
 
		png_get_PLTE(png_ptr, info_ptr, &palette, &palette_size);
 
		for (i = 0; i < palette_size && (palette_size != 16 || all_gray); i++) {
 
			all_gray &= palette[i].red == palette[i].green && palette[i].red == palette[i].blue;
 
			gray_palette[i] = RGBToGrayscale(palette[i].red, palette[i].green, palette[i].blue);
 
		}
 

	
 
		/**
 
		 * For a non-gray palette of size 16 we assume that
 
		 * the order of the palette determines the height;
 
		 * the first entry is the sea (level 0), the second one
 
		 * level 1, etc.
 
		 */
 
		if (palette_size == 16 && !all_gray) {
 
			for (i = 0; i < palette_size; i++) {
 
				gray_palette[i] = 256 * i / palette_size;
 
			}
 
		}
 
	}
 

	
 
	row_pointers = png_get_rows(png_ptr, info_ptr);
 

	
 
	/* Read the raw image data and convert in 8-bit grayscale */
 
	for (x = 0; x < info_ptr->width; x++) {
 
		for (y = 0; y < info_ptr->height; y++) {
 
			byte *pixel = &map[y * info_ptr->width + x];
 
			uint x_offset = x * info_ptr->channels;
 

	
 
			if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
 
				*pixel = gray_palette[row_pointers[y][x_offset]];
 
			} else if (info_ptr->channels == 3) {
 
				*pixel = RGBToGrayscale(row_pointers[y][x_offset + 0],
 
						row_pointers[y][x_offset + 1], row_pointers[y][x_offset + 2]);
 
			} else {
 
				*pixel = row_pointers[y][x_offset];
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * Reads the heightmap and/or size of the heightmap from a PNG file.
 
 * If map == NULL only the size of the PNG is read, otherwise a map
 
 * with grayscale pixels is allocated and assigned to *map.
 
 */
 
static bool ReadHeightmapPNG(char *filename, uint *x, uint *y, byte **map)
 
{
 
	FILE *fp;
 
	png_structp png_ptr = NULL;
 
	png_infop info_ptr  = NULL;
 

	
 
	fp = fopen(filename, "rb");
 
	if (fp == NULL) {
 
		ShowErrorMessage(STR_PNGMAP_ERR_FILE_NOT_FOUND, STR_PNGMAP_ERROR, 0, 0);
 
		return false;
 
	}
 

	
 
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 
	if (png_ptr == NULL) {
 
		ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
 
		fclose(fp);
 
		return false;
 
	}
 

	
 
	info_ptr = png_create_info_struct(png_ptr);
 
	if (info_ptr == NULL || setjmp(png_jmpbuf(png_ptr))) {
 
		ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
 
		fclose(fp);
 
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
		return false;
 
	}
 

	
 
	png_init_io(png_ptr, fp);
 

	
 
	/* Allocate memory and read image, without alpha or 16-bit samples
 
	 * (result is either 8-bit indexed/grayscale or 24-bit RGB) */
 
	png_set_packing(png_ptr);
 
	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_STRIP_16, NULL);
 

	
 
	/* Maps of wrong color-depth are not used.
 
	 * (this should have been taken care of by stripping alpha and 16-bit samples on load) */
 
	if ((info_ptr->channels != 1) && (info_ptr->channels != 3) && (info_ptr->bit_depth != 8)) {
 
		ShowErrorMessage(STR_PNGMAP_ERR_IMAGE_TYPE, STR_PNGMAP_ERROR, 0, 0);
 
		fclose(fp);
 
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
		return false;
 
	}
 

	
 
	if (map != NULL) {
 
		*map = malloc(info_ptr->width * info_ptr->height * sizeof(byte));
 

	
 
		if (*map == NULL) {
 
			ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
 
			fclose(fp);
 
			png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
			return false;
 
		}
 

	
 
		ReadHeightmapPNGImageData(*map, png_ptr, info_ptr);
 
	}
 

	
 
	*x = info_ptr->width;
 
	*y = info_ptr->height;
 

	
 
	fclose(fp);
 
	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
	return true;
 
}
 

	
 
#endif /* WITH_PNG */
 

	
 

	
 
/**
 
 * The BMP Heightmap loader.
 
 */
 
static void ReadHeightmapBMPImageData(byte *map, BmpInfo *info, BmpData *data)
 
{
 
	uint x, y;
 
	byte gray_palette[256];
 

	
 
	if (data->palette != NULL) {
 
		uint i;
 
		bool all_gray = true;
 

	
 
		if (info->palette_size != 2) {
 
			for (i = 0; i < info->palette_size && (info->palette_size != 16 || all_gray); i++) {
 
				all_gray &= data->palette[i].r == data->palette[i].g && data->palette[i].r == data->palette[i].b;
 
				gray_palette[i] = RGBToGrayscale(data->palette[i].r, data->palette[i].g, data->palette[i].b);
 
			}
 

	
 
			/**
 
			 * For a non-gray palette of size 16 we assume that
 
			 * the order of the palette determines the height;
 
			 * the first entry is the sea (level 0), the second one
 
			 * level 1, etc.
 
			 */
 
			if (info->palette_size == 16 && !all_gray) {
 
				for (i = 0; i < info->palette_size; i++) {
 
					gray_palette[i] = 256 * i / info->palette_size;
 
				}
 
			}
 
		} else {
 
			/**
 
			 * For a palette of size 2 we assume that the order of the palette determines the height;
 
			 * the first entry is the sea (level 0), the second one is the land (level 1)
 
			 */
 
			gray_palette[0] = 0;
 
			gray_palette[1] = 16;
 
		}
 
	}
 

	
 
	/* Read the raw image data and convert in 8-bit grayscale */
 
	for (y = 0; y < info->height; y++) {
 
		byte *pixel = &map[y * info->width];
 
		byte *bitmap = &data->bitmap[y * info->width * (info->bpp == 24 ? 3 : 1)];
 

	
 
		for (x = 0; x < info->width; x++) {
 
			if (info->bpp != 24) {
 
				*pixel++ = gray_palette[*bitmap++];
 
			} else {
 
				*pixel++ = RGBToGrayscale(*bitmap, *(bitmap + 1), *(bitmap + 2));
 
				bitmap += 3;
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * Reads the heightmap and/or size of the heightmap from a BMP file.
 
 * If map == NULL only the size of the BMP is read, otherwise a map
 
 * with grayscale pixels is allocated and assigned to *map.
 
 */
 
static bool ReadHeightmapBMP(char *filename, uint *x, uint *y, byte **map)
 
{
 
	FILE *f;
 
	BmpInfo info;
 
	BmpData data;
 
	BmpBuffer buffer;
 

	
 
	f = fopen(filename, "rb");
 
	if (f == NULL) {
 
		ShowErrorMessage(STR_PNGMAP_ERR_FILE_NOT_FOUND, STR_BMPMAP_ERROR, 0, 0);
 
		return false;
 
	}
 

	
 
	BmpInitializeBuffer(&buffer, f);
 

	
 
	if (!BmpReadHeader(&buffer, &info, &data)) {
 
		ShowErrorMessage(STR_BMPMAP_ERR_IMAGE_TYPE, STR_BMPMAP_ERROR, 0, 0);
 
		fclose(f);
 
		BmpDestroyData(&data);
 
		return false;
 
	}
 

	
 
	if (map != NULL) {
 
		if (!BmpReadBitmap(&buffer, &info, &data)) {
 
			ShowErrorMessage(STR_BMPMAP_ERR_IMAGE_TYPE, STR_BMPMAP_ERROR, 0, 0);
 
			fclose(f);
 
			BmpDestroyData(&data);
 
			return false;
 
		}
 

	
 
		*map = malloc(info.width * info.height * sizeof(byte));
 
		if (*map == NULL) {
 
			ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_BMPMAP_ERROR, 0, 0);
 
			fclose(f);
 
			BmpDestroyData(&data);
 
			return false;
 
		}
 

	
 
		ReadHeightmapBMPImageData(*map, &info, &data);
 

	
 
	}
 

	
 
	BmpDestroyData(&data);
 

	
 
	*x = info.width;
 
	*y = info.height;
 

	
 
	fclose(f);
 
	return true;
 
}
 

	
 
static void GrayscaleToMapHeights(uint img_width, uint img_height, byte *map)
 
{
 
	/* Defines the detail of the aspect ratio (to avoid doubles) */
 
	const uint num_div = 16384;
 

	
 
	uint width, height;
 
	uint row, col;
 
	uint row_pad = 0, col_pad = 0;
 
	uint img_scale;
 
	uint img_row, img_col;
 
	TileIndex tile;
 

	
 
	/* Get map size and calculate scale and padding values */
 
	switch (_patches.heightmap_rotation) {
 
	case HM_COUNTER_CLOCKWISE:
 
		width   = MapSizeX();
 
		height  = MapSizeY();
 
		break;
 
	case HM_CLOCKWISE:
 
		width   = MapSizeY();
 
		height  = MapSizeX();
 
		break;
 
	default:
 
		NOT_REACHED();
 
		/* Avoids compiler warnings */
 
		return;
 
	}
 

	
 
	if ((img_width * num_div) / img_height > ((width * num_div) / height)) {
 
		/* Image is wider than map - center vertically */
 
		img_scale = (width * num_div) / img_width;
 
		row_pad = (height - ((img_height * img_scale) / num_div)) / 2;
 
	} else {
 
		/* Image is taller than map - center horizontally */
 
		img_scale = (height * num_div) / img_height;
 
		col_pad = (width - ((img_width * img_scale) / num_div)) / 2;
 
	}
 

	
 
	/* Form the landscape */
 
	for (row = 0; row < height - 1; row++) {
 
		for (col = 0; col < width - 1; col++) {
 
			switch (_patches.heightmap_rotation) {
 
			case HM_COUNTER_CLOCKWISE: tile = TileXY(col, row); break;
 
			case HM_CLOCKWISE:         tile = TileXY(row, col); break;
 
			default:                   NOT_REACHED(); return;
 
			}
 

	
 
			/* Check if current tile is within the 1-pixel map edge or padding regions */
 
			if ((DistanceFromEdge(tile) <= 1) ||
 
					(row < row_pad) || (row >= (img_height + row_pad)) ||
 
					(col < col_pad) || (col >= (img_width  + col_pad))) {
 
				SetTileHeight(tile, 0);
 
			} else {
 
				/* Use nearest neighbor resizing to scale map data.
 
				 *  We rotate the map 45 degrees (counter)clockwise */
 
				img_row = (((row - row_pad) * num_div) / img_scale);
 
				switch (_patches.heightmap_rotation) {
 
				case HM_COUNTER_CLOCKWISE:
 
					img_col = (((width - 1 - col - col_pad) * num_div) / img_scale);
 
					break;
 
				case HM_CLOCKWISE:
 
					img_col = (((col - col_pad) * num_div) / img_scale);
 
					break;
 
				default:
 
					NOT_REACHED();
 
					/* Avoids compiler warnings */
 
					return;
 
				}
 

	
 
				assert(img_row < img_height);
 
				assert(img_col < img_width);
 

	
 
				/* Color scales from 0 to 255, OpenTTD height scales from 0 to 15 */
 
				SetTileHeight(tile, map[img_row * img_width + img_col] / 16);
 
			}
 
			MakeClear(tile, CLEAR_GRASS, 3);
 
		}
 
	}
 
}
 

	
 
/**
 
 * This function takes care of the fact that land in OpenTTD can never differ
 
 * more than 1 in height
 
 */
 
static void FixSlopes(void)
 
{
 
	uint width, height;
 
	uint row, col;
 
	byte current_tile;
 

	
 
	/* Adjust height difference to maximum one horizontal/vertical change. */
 
	width   = MapSizeX();
 
	height  = MapSizeY();
 

	
 
	/* Top and left edge */
 
	for (row = 1; row < height - 2; row++) {
 
		for (col = 1; col < width - 2; col++) {
 
			/* Find lowest tile; either the top or left one */
 
			current_tile = TileHeight(TileXY(col - 1, row)); // top edge
 
			if (TileHeight(TileXY(col, row - 1)) < current_tile) {
 
				current_tile = TileHeight(TileXY(col, row - 1)); // left edge
 
			}
 

	
 
			/* Does the height differ more than one? */
 
			if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
 
				/* Then change the height to be no more than one */
 
				SetTileHeight(TileXY(col, row), current_tile + 1);
 
			}
 
		}
 
	}
 

	
 
	/* Bottom and right edge */
 
	for (row = height - 2; row > 0; row--) {
 
		for (col = width - 2; col > 0; col--) {
 
			/* Find lowest tile; either the bottom and right one */
 
			current_tile = TileHeight(TileXY(col + 1, row)); // bottom edge
 
			if (TileHeight(TileXY(col, row + 1)) < current_tile) {
 
				current_tile = TileHeight(TileXY(col, row + 1)); // right edge
 
			}
 

	
 
			/* Does the height differ more than one? */
 
			if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
 
				/* Then change the height to be no more than one */
 
				SetTileHeight(TileXY(col, row), current_tile + 1);
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * Reads the heightmap with the correct file reader
 
 */
 
static bool ReadHeightMap(char *filename, uint *x, uint *y, byte **map)
 
{
 
	switch (_file_to_saveload.mode) {
 
#ifdef WITH_PNG
 
		case SL_PNG:
 
			return ReadHeightmapPNG(filename, x, y, map);
 
#endif /* WITH_PNG */
 
		case SL_BMP:
 
			return ReadHeightmapBMP(filename, x, y, map);
 

	
 
		default:
 
			NOT_REACHED();
 
			/* Avoids compiler warnings */
 
			return false;
 
	}
 
}
 

	
 
bool GetHeightmapDimensions(char *filename, uint *x, uint *y)
 
{
 
	return ReadHeightMap(filename, x, y, NULL);
 
}
 

	
 
void LoadHeightmap(char *filename)
 
{
 
	uint x, y;
 
	byte *map = NULL;
 

	
 
	if (!ReadHeightMap(filename, &x, &y, &map)) {
 
		free(map);
 
		return;
 
	}
 

	
 
	GrayscaleToMapHeights(x, y, map);
 
	free(map);
 

	
 
	FixSlopes();
 
	MarkWholeScreenDirty();
 
}
 

	
 
void FlatEmptyWorld(byte tile_height)
 
{
 
	uint width, height;
 
	uint row, col;
 

	
 
	width  = MapSizeX();
 
	height = MapSizeY();
 

	
 
	for (row = 2; row < height - 2; row++) {
 
		for (col = 2; col < width - 2; col++) {
 
			SetTileHeight(TileXY(col, row), tile_height);
 
		}
 
	}
 

	
 
	FixSlopes();
 
	MarkWholeScreenDirty();
 
}
src/industry_cmd.c
Show inline comments
 
deleted file
src/industry_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "clear_map.h"
 
#include "functions.h"
 
#include "industry_map.h"
 
#include "station_map.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "industry.h"
 
#include "town.h"
 
#include "vehicle.h"
 
#include "news.h"
 
#include "saveload.h"
 
#include "economy.h"
 
#include "sound.h"
 
#include "variables.h"
 
#include "table/industry_land.h"
 
#include "table/build_industry.h"
 
#include "genworld.h"
 
#include "date.h"
 
#include "water_map.h"
 

	
 
void ShowIndustryViewWindow(int industry);
 
void BuildOilRig(TileIndex tile);
 
void DeleteOilRig(TileIndex tile);
 

	
 
static byte _industry_sound_ctr;
 
static TileIndex _industry_sound_tile;
 

	
 
/**
 
 * Called if a new block is added to the industry-pool
 
 */
 
static void IndustryPoolNewBlock(uint start_item)
 
{
 
	Industry *i;
 

	
 
	/* 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 (i = GetIndustry(start_item); i != NULL; i = (i->index + 1U < GetIndustryPoolSize()) ? GetIndustry(i->index + 1U) : NULL) i->index = start_item++;
 
}
 

	
 
DEFINE_OLD_POOL(Industry, Industry, IndustryPoolNewBlock, NULL)
 

	
 
/**
 
 * Retrieve the type for this industry.  Although it is accessed by a tile,
 
 * it will return the general type of industry, and not the sprite index
 
 * as would do GetIndustryGfx.
 
 * The same information can be accessed by looking at Industry->type
 
 * @param tile that is queried
 
 * @pre IsTileType(tile, MP_INDUSTRY)
 
 * @return general type for this industry, as defined in industry.h
 
 **/
 
IndustryType GetIndustryType(TileIndex tile)
 
{
 
	IndustryGfx this_type = GetIndustryGfx(tile);
 
	IndustryType iloop;
 

	
 
	assert(IsTileType(tile, MP_INDUSTRY));
 

	
 
	for (iloop = IT_COAL_MINE; iloop < IT_END; iloop += 1) {
 
		if (IS_BYTE_INSIDE(this_type, industry_gfx_Solver[iloop].MinGfx,
 
				industry_gfx_Solver[iloop].MaxGfx+1)) {
 
			return iloop;
 
		}
 
	}
 

	
 
	return IT_INVALID;  //we have not found equivalent, whatever the reason
 
}
 

	
 
/**
 
 * Accessor for array _industry_specs.
 
 * This will ensure at once : proper access and
 
 * not allowing modifications of it.
 
 * @param thistype of industry (which is the index in _industry_specs)
 
 * @pre thistype < IT_END
 
 **/
 
const IndustrySpec *GetIndustrySpec(IndustryType thistype)
 
{
 
	assert(thistype < IT_END);
 
	return &_industry_specs[thistype];
 
}
 

	
 
void DestroyIndustry(Industry *i)
 
{
 
	BEGIN_TILE_LOOP(tile_cur, i->width, i->height, i->xy);
 
		if (IsTileType(tile_cur, MP_INDUSTRY)) {
 
			if (GetIndustryIndex(tile_cur) == i->index) {
 
				DoClearSquare(tile_cur);
 
			}
 
		} else if (IsTileType(tile_cur, MP_STATION) && IsOilRig(tile_cur)) {
 
			DeleteOilRig(tile_cur);
 
		}
 
	END_TILE_LOOP(tile_cur, i->width, i->height, i->xy);
 

	
 
	if (i->type == IT_FARM || i->type == IT_FARM_2) {
 
		/* Remove the farmland and convert it to regular tiles over time. */
 
		BEGIN_TILE_LOOP(tile_cur, 42, 42, i->xy - TileDiffXY(21, 21)) {
 
			tile_cur = TILE_MASK(tile_cur);
 
			if (IsTileType(tile_cur, MP_CLEAR) && IsClearGround(tile_cur, CLEAR_FIELDS) &&
 
					GetIndustryIndexOfField(tile_cur) == i->index) {
 
				SetIndustryIndexOfField(tile_cur, INVALID_INDUSTRY);
 
			}
 
		} END_TILE_LOOP(tile_cur, 42, 42, i->xy - TileDiff(21, 21))
 
	}
 

	
 
	_industry_sort_dirty = true;
 
	_total_industries--;
 
	DeleteSubsidyWithIndustry(i->index);
 
	DeleteWindowById(WC_INDUSTRY_VIEW, i->index);
 
	InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0);
 
}
 

	
 
static void IndustryDrawSugarMine(const TileInfo *ti)
 
{
 
	const DrawIndustrySpec1Struct *d;
 
	uint32 image;
 

	
 
	if (!IsIndustryCompleted(ti->tile)) return;
 

	
 
	d = &_draw_industry_spec1[GetIndustryAnimationState(ti->tile)];
 

	
 
	AddChildSpriteScreen(SPR_IT_SUGAR_MINE_SIEVE + d->image_1, d->x, 0);
 

	
 
	image = d->image_2;
 
	if (image != 0) AddChildSpriteScreen(SPR_IT_SUGAR_MINE_CLOUDS + image - 1, 8, 41);
 

	
 
	image = d->image_3;
 
	if (image != 0) {
 
		AddChildSpriteScreen(SPR_IT_SUGAR_MINE_PILE + image - 1,
 
			_drawtile_proc1_x[image - 1], _drawtile_proc1_y[image - 1]);
 
	}
 
}
 

	
 
static void IndustryDrawToffeeQuarry(const TileInfo *ti)
 
{
 
	int x = 0;
 

	
 
	if (IsIndustryCompleted(ti->tile)) {
 
		x = _industry_anim_offs[GetIndustryAnimationState(ti->tile)];
 
		if ( (byte)x == 0xFF)
 
			x = 0;
 
	}
 

	
 
	AddChildSpriteScreen(SPR_IT_TOFFEE_QUARRY_SHOVEL, 22 - x, 24 + x);
 
	AddChildSpriteScreen(SPR_IT_TOFFEE_QUARRY_TOFFEE, 6, 14);
 
}
 

	
 
static void IndustryDrawBubbleGenerator( const TileInfo *ti)
 
{
 
	if (IsIndustryCompleted(ti->tile)) {
 
		AddChildSpriteScreen(SPR_IT_BUBBLE_GENERATOR_BUBBLE, 5, _industry_anim_offs_2[GetIndustryAnimationState(ti->tile)]);
 
	} else {
 
		AddChildSpriteScreen(SPR_IT_BUBBLE_GENERATOR_SPRING, 3, 67);
 
	}
 
}
 

	
 
static void IndustryDrawToyFactory(const TileInfo *ti)
 
{
 
	const DrawIndustrySpec4Struct *d;
 

	
 
	d = &_industry_anim_offs_3[GetIndustryAnimationState(ti->tile)];
 

	
 
	if (d->image_1 != 0xFF) {
 
		AddChildSpriteScreen(SPR_IT_TOY_FACTORY_CLAY, 50 - d->image_1 * 2, 96 + d->image_1);
 
	}
 

	
 
	if (d->image_2 != 0xFF) {
 
		AddChildSpriteScreen(SPR_IT_TOY_FACTORY_ROBOT, 16 - d->image_2 * 2, 100 + d->image_2);
 
	}
 

	
 
	AddChildSpriteScreen(SPR_IT_TOY_FACTORY_STAMP, 7, d->image_3);
 
	AddChildSpriteScreen(SPR_IT_TOY_FACTORY_STAMP_HOLDER, 0, 42);
 
}
 

	
 
static void IndustryDrawCoalPlantSparks(const TileInfo *ti)
 
{
 
	if (IsIndustryCompleted(ti->tile)) {
 
		uint image = GetIndustryAnimationState(ti->tile);
 

	
 
		if (image != 0 && image < 7) {
 
			AddChildSpriteScreen(image + SPR_IT_POWER_PLANT_TRANSFORMERS,
 
				_coal_plant_sparks_x[image - 1],
 
				_coal_plant_sparks_y[image - 1]
 
			);
 
		}
 
	}
 
}
 

	
 
typedef void IndustryDrawTileProc(const TileInfo *ti);
 
static IndustryDrawTileProc * const _industry_draw_tile_procs[5] = {
 
	IndustryDrawSugarMine,
 
	IndustryDrawToffeeQuarry,
 
	IndustryDrawBubbleGenerator,
 
	IndustryDrawToyFactory,
 
	IndustryDrawCoalPlantSparks,
 
};
 

	
 
static void DrawTile_Industry(TileInfo *ti)
 
{
 
	const IndustryGfx gfx = GetIndustryGfx(ti->tile);
 
	const Industry *ind;
 
	const DrawBuildingsTileStruct *dits;
 
	byte z;
 
	uint32 image, ormod;
 

	
 
	/* Pointer to industry */
 
	ind = GetIndustryByTile(ti->tile);
 
	ormod = GENERAL_SPRITE_COLOR(ind->random_color);
 

	
 
	/* Retrieve pointer to the draw industry tile struct */
 
	dits = &_industry_draw_tile_data[gfx << 2 | (_industry_section_draw_animation_state[gfx] ?
 
			GetIndustryAnimationState(ti->tile) & 3 :
 
			GetIndustryConstructionStage(ti->tile))];
 

	
 
	image = dits->ground;
 
	if (image & PALETTE_MODIFIER_COLOR && (image & PALETTE_SPRITE_MASK) == 0)
 
		image |= ormod;
 

	
 
	z = ti->z;
 
	/* Add bricks below the industry? */
 
	if (ti->tileh != SLOPE_FLAT) {
 
		AddSortableSpriteToDraw(SPR_FOUNDATION_BASE + ti->tileh, ti->x, ti->y, 16, 16, 7, z);
 
		AddChildSpriteScreen(image, 31, 1);
 
		z += TILE_HEIGHT;
 
	} else {
 
		/* Else draw regular ground */
 
		DrawGroundSprite(image);
 
	}
 

	
 
	/* Add industry on top of the ground? */
 
	image = dits->building;
 
	if (image != 0) {
 
		if (image & PALETTE_MODIFIER_COLOR && (image & PALETTE_SPRITE_MASK) == 0)
 
			image |= ormod;
 

	
 
		if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 

	
 
		AddSortableSpriteToDraw(image,
 
			ti->x + dits->subtile_x,
 
			ti->y + dits->subtile_y,
 
			dits->width  + 1,
 
			dits->height + 1,
 
			dits->dz,
 
			z);
 

	
 
		if (_display_opt & DO_TRANS_BUILDINGS) return;
 
	}
 

	
 
	{
 
		int proc = dits->draw_proc - 1;
 
		if (proc >= 0) _industry_draw_tile_procs[proc](ti);
 
	}
 
}
 

	
 
static uint GetSlopeZ_Industry(TileIndex tile, uint x, uint y)
 
{
 
	return GetTileMaxZ(tile);
 
}
 

	
 
static Slope GetSlopeTileh_Industry(TileIndex tile, Slope tileh)
 
{
 
	return SLOPE_FLAT;
 
}
 

	
 
static void GetAcceptedCargo_Industry(TileIndex tile, AcceptedCargo ac)
 
{
 
	IndustryGfx gfx = GetIndustryGfx(tile);
 
	CargoID a;
 

	
 
	a = _industry_section_accepts_1[gfx];
 
	if (a != CT_INVALID) ac[a] = (a == 0) ? 1 : 8;
 

	
 
	a = _industry_section_accepts_2[gfx];
 
	if (a != CT_INVALID) ac[a] = 8;
 

	
 
	a = _industry_section_accepts_3[gfx];
 
	if (a != CT_INVALID) ac[a] = 8;
 
}
 

	
 
static void GetTileDesc_Industry(TileIndex tile, TileDesc *td)
 
{
 
	const Industry *i = GetIndustryByTile(tile);
 

	
 
	td->owner = i->owner;
 
	td->str = GetIndustrySpec(i->type)->name;
 
	if (!IsIndustryCompleted(tile)) {
 
		SetDParamX(td->dparam, 0, td->str);
 
		td->str = STR_2058_UNDER_CONSTRUCTION;
 
	}
 
}
 

	
 
static int32 ClearTile_Industry(TileIndex tile, byte flags)
 
{
 
	Industry *i = GetIndustryByTile(tile);
 

	
 
	/* water can destroy industries
 
	 * in editor you can bulldoze industries
 
	 * with magic_bulldozer cheat you can destroy industries
 
	 * (area around OILRIG is water, so water shouldn't flood it
 
	 */
 
	if ((_current_player != OWNER_WATER && _game_mode != GM_EDITOR &&
 
			!_cheats.magic_bulldozer.value) ||
 
			(_current_player == OWNER_WATER && i->type == IT_OIL_RIG)) {
 
		SetDParam(0, GetIndustrySpec(i->type)->name);
 
		return_cmd_error(STR_4800_IN_THE_WAY);
 
	}
 

	
 
	if (flags & DC_EXEC) DeleteIndustry(i);
 
	return 0;
 
}
 

	
 
static void TransportIndustryGoods(TileIndex tile)
 
{
 
	Industry *i = GetIndustryByTile(tile);
 
	const IndustrySpec *indspec = GetIndustrySpec(i->type);
 
	uint cw, am;
 

	
 
	cw = min(i->cargo_waiting[0], 255);
 
	if (cw > indspec->minimal_cargo/* && i->produced_cargo[0] != 0xFF*/) {
 
		i->cargo_waiting[0] -= cw;
 

	
 
		/* fluctuating economy? */
 
		if (_economy.fluct <= 0) cw = (cw + 1) / 2;
 

	
 
		i->last_mo_production[0] += cw;
 

	
 
		am = MoveGoodsToStation(i->xy, i->width, i->height, i->produced_cargo[0], cw);
 
		i->last_mo_transported[0] += am;
 
		if (am != 0) {
 
			uint newgfx = _industry_produce_section[GetIndustryGfx(tile)];
 

	
 
			if (newgfx != 0xFF) {
 
				ResetIndustryConstructionStage(tile);
 
				SetIndustryCompleted(tile, true);
 
				SetIndustryGfx(tile, newgfx);
 
				MarkTileDirtyByTile(tile);
 
			}
 
		}
 
	}
 

	
 
	cw = min(i->cargo_waiting[1], 255);
 
	if (cw > indspec->minimal_cargo) {
 
		i->cargo_waiting[1] -= cw;
 

	
 
		if (_economy.fluct <= 0) cw = (cw + 1) / 2;
 

	
 
		i->last_mo_production[1] += cw;
 

	
 
		am = MoveGoodsToStation(i->xy, i->width, i->height, i->produced_cargo[1], cw);
 
		i->last_mo_transported[1] += am;
 
	}
 
}
 

	
 

	
 
static void AnimateTile_Industry(TileIndex tile)
 
{
 
	byte m;
 

	
 
	switch (GetIndustryGfx(tile)) {
 
	case GFX_SUGAR_MINE_SIEVE:
 
		if ((_tick_counter & 1) == 0) {
 
			m = GetIndustryAnimationState(tile) + 1;
 

	
 
			switch (m & 7) {
 
			case 2: SndPlayTileFx(SND_2D_RIP_2, tile); break;
 
			case 6: SndPlayTileFx(SND_29_RIP, tile); break;
 
			}
 

	
 
			if (m >= 96) {
 
				m = 0;
 
				DeleteAnimatedTile(tile);
 
			}
 
			SetIndustryAnimationState(tile, m);
 

	
 
			MarkTileDirtyByTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_TOFFEE_QUARY:
 
		if ((_tick_counter & 3) == 0) {
 
			m = GetIndustryAnimationState(tile);
 

	
 
			if (_industry_anim_offs[m] == 0xFF) {
 
				SndPlayTileFx(SND_30_CARTOON_SOUND, tile);
 
			}
 

	
 
			if (++m >= 70) {
 
				m = 0;
 
				DeleteAnimatedTile(tile);
 
			}
 
			SetIndustryAnimationState(tile, m);
 

	
 
			MarkTileDirtyByTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_BUBBLE_CATCHER:
 
		if ((_tick_counter & 1) == 0) {
 
			m = GetIndustryAnimationState(tile);
 

	
 
			if (++m >= 40) {
 
				m = 0;
 
				DeleteAnimatedTile(tile);
 
			}
 
			SetIndustryAnimationState(tile, m);
 

	
 
			MarkTileDirtyByTile(tile);
 
		}
 
		break;
 

	
 
	// Sparks on a coal plant
 
	case GFX_POWERPLANT_SPARKS:
 
		if ((_tick_counter & 3) == 0) {
 
			m = GetIndustryAnimationState(tile);
 
			if (m == 6) {
 
				SetIndustryAnimationState(tile, 0);
 
				DeleteAnimatedTile(tile);
 
			} else {
 
				SetIndustryAnimationState(tile, m + 1);
 
				MarkTileDirtyByTile(tile);
 
			}
 
		}
 
		break;
 

	
 
	case GFX_TOY_FACTORY:
 
		if ((_tick_counter & 1) == 0) {
 
			m = GetIndustryAnimationState(tile) + 1;
 

	
 
			if (m == 1) {
 
				SndPlayTileFx(SND_2C_MACHINERY, tile);
 
			} else if (m == 23) {
 
				SndPlayTileFx(SND_2B_COMEDY_HIT, tile);
 
			} else if (m == 28) {
 
				SndPlayTileFx(SND_2A_EXTRACT_AND_POP, tile);
 
			}
 

	
 
			if (m >= 50) {
 
				int n = GetIndustryAnimationLoop(tile) + 1;
 
				m = 0;
 
				if (n >= 8) {
 
					n = 0;
 
					DeleteAnimatedTile(tile);
 
				}
 
				SetIndustryAnimationLoop(tile, n);
 
			}
 
			SetIndustryAnimationState(tile, m);
 
			MarkTileDirtyByTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_7: case GFX_PLASTIC_FOUNTAIN_ANIMATED_8:
 
		if ((_tick_counter & 3) == 0) {
 
			IndustryGfx gfx = GetIndustryGfx(tile);
 

	
 
			gfx = (gfx < 155) ? gfx + 1 : 148;
 
			SetIndustryGfx(tile, gfx);
 
			MarkTileDirtyByTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_OILWELL_ANIMATED_1:
 
	case GFX_OILWELL_ANIMATED_2:
 
	case GFX_OILWELL_ANIMATED_3:
 
		if ((_tick_counter & 7) == 0) {
 
			bool b = CHANCE16(1,7);
 
			IndustryGfx gfx = GetIndustryGfx(tile);
 

	
 
			m = GetIndustryAnimationState(tile) + 1;
 
			if (m == 4 && (m = 0, ++gfx) == GFX_OILWELL_ANIMATED_3 + 1 && (gfx = GFX_OILWELL_ANIMATED_1, b)) {
 
				SetIndustryGfx(tile, GFX_OILWELL_NOT_ANIMATED);
 
				SetIndustryConstructionStage(tile, 3);
 
				DeleteAnimatedTile(tile);
 
			} else {
 
				SetIndustryAnimationState(tile, m);
 
				SetIndustryGfx(tile, gfx);
 
				MarkTileDirtyByTile(tile);
 
			}
 
		}
 
		break;
 

	
 
	case GFX_COAL_MINE_TOWER_ANIMATED:
 
	case GFX_COPPER_MINE_TOWER_ANIMATED:
 
	case GFX_GOLD_MINE_TOWER_ANIMATED: {
 
			int state = _tick_counter & 0x7FF;
 

	
 
			if ((state -= 0x400) < 0)
 
				return;
 

	
 
			if (state < 0x1A0) {
 
				if (state < 0x20 || state >= 0x180) {
 
					m = GetIndustryAnimationState(tile);
 
					if (!(m & 0x40)) {
 
						SetIndustryAnimationState(tile, m | 0x40);
 
						SndPlayTileFx(SND_0B_MINING_MACHINERY, tile);
 
					}
 
					if (state & 7)
 
						return;
 
				} else {
 
					if (state & 3)
 
						return;
 
				}
 
				m = (GetIndustryAnimationState(tile) + 1) | 0x40;
 
				if (m > 0xC2) m = 0xC0;
 
				SetIndustryAnimationState(tile, m);
 
				MarkTileDirtyByTile(tile);
 
			} else if (state >= 0x200 && state < 0x3A0) {
 
				int i;
 
				i = (state < 0x220 || state >= 0x380) ? 7 : 3;
 
				if (state & i)
 
					return;
 

	
 
				m = (GetIndustryAnimationState(tile) & 0xBF) - 1;
 
				if (m < 0x80) m = 0x82;
 
				SetIndustryAnimationState(tile, m);
 
				MarkTileDirtyByTile(tile);
 
			}
 
		} break;
 
	}
 
}
 

	
 
static void CreateIndustryEffectSmoke(TileIndex tile)
 
{
 
	uint x = TileX(tile) * TILE_SIZE;
 
	uint y = TileY(tile) * TILE_SIZE;
 
	uint z = GetTileMaxZ(tile);
 

	
 
	CreateEffectVehicle(x + 15, y + 14, z + 59, EV_CHIMNEY_SMOKE);
 
}
 

	
 
static void MakeIndustryTileBigger(TileIndex tile)
 
{
 
	byte cnt = GetIndustryConstructionCounter(tile) + 1;
 
	byte stage;
 

	
 
	if (cnt != 4) {
 
		SetIndustryConstructionCounter(tile, cnt);
 
		return;
 
	}
 

	
 
	stage = GetIndustryConstructionStage(tile) + 1;
 
	SetIndustryConstructionCounter(tile, 0);
 
	SetIndustryConstructionStage(tile, stage);
 
	if (stage == 3) {
 
		SetIndustryCompleted(tile, true);
 
	}
 

	
 
	MarkTileDirtyByTile(tile);
 

	
 
	if (!IsIndustryCompleted(tile)) return;
 

	
 
	switch (GetIndustryGfx(tile)) {
 
	case GFX_POWERPLANT_CHIMNEY:
 
		CreateIndustryEffectSmoke(tile);
 
		break;
 

	
 
	case GFX_OILRIG_1:
 
		if (GetIndustryGfx(tile + TileDiffXY(0, 1)) == GFX_OILRIG_1) BuildOilRig(tile);
 
		break;
 

	
 
	case GFX_TOY_FACTORY:
 
	case GFX_BUBBLE_CATCHER:
 
	case GFX_TOFFEE_QUARY:
 
		SetIndustryAnimationState(tile, 0);
 
		SetIndustryAnimationLoop(tile, 0);
 
		break;
 

	
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_7: case GFX_PLASTIC_FOUNTAIN_ANIMATED_8:
 
		AddAnimatedTile(tile);
 
		break;
 
	}
 
}
 

	
 

	
 
static void TileLoopIndustry_BubbleGenerator(TileIndex tile)
 
{
 
	int dir;
 
	Vehicle *v;
 
	static const int8 _tileloop_ind_case_161[12] = {
 
		11,   0, -4, -14,
 
		-4, -10, -4,   1,
 
		49,  59, 60,  65,
 
	};
 

	
 
	SndPlayTileFx(SND_2E_EXTRACT_AND_POP, tile);
 

	
 
	dir = Random() & 3;
 

	
 
	v = CreateEffectVehicleAbove(
 
		TileX(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 0],
 
		TileY(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 4],
 
		_tileloop_ind_case_161[dir + 8],
 
		EV_BUBBLE
 
	);
 

	
 
	if (v != NULL) v->u.special.unk2 = dir;
 
}
 

	
 
static void TileLoop_Industry(TileIndex tile)
 
{
 
	IndustryGfx newgfx;
 
	IndustryGfx gfx;
 

	
 
	if (!IsIndustryCompleted(tile)) {
 
		MakeIndustryTileBigger(tile);
 
		return;
 
	}
 

	
 
	if (_game_mode == GM_EDITOR) return;
 

	
 
	TransportIndustryGoods(tile);
 

	
 
	newgfx = _industry_section_animation_next[GetIndustryGfx(tile)];
 
	if (newgfx != 255) {
 
		ResetIndustryConstructionStage(tile);
 
		SetIndustryGfx(tile, newgfx);
 
		MarkTileDirtyByTile(tile);
 
		return;
 
	}
 

	
 
	gfx = GetIndustryGfx(tile);
 

	
 
	switch (gfx) {
 
	case GFX_OILRIG_1: // coast line at oilrigs
 
	case GFX_OILRIG_2:
 
	case GFX_OILRIG_3:
 
	case GFX_OILRIG_4:
 
	case GFX_OILRIG_5:
 
		TileLoop_Water(tile);
 
		break;
 

	
 
	case GFX_COAL_MINE_TOWER_NOT_ANIMATED:
 
	case GFX_COPPER_MINE_TOWER_NOT_ANIMATED:
 
	case GFX_GOLD_MINE_TOWER_NOT_ANIMATED:
 
		if (!(_tick_counter & 0x400) && CHANCE16(1, 2)) {
 
			switch (gfx) {
 
				case GFX_COAL_MINE_TOWER_NOT_ANIMATED:   gfx = GFX_COAL_MINE_TOWER_ANIMATED;   break;
 
				case GFX_COPPER_MINE_TOWER_NOT_ANIMATED: gfx = GFX_COPPER_MINE_TOWER_ANIMATED; break;
 
				case GFX_GOLD_MINE_TOWER_NOT_ANIMATED:   gfx = GFX_GOLD_MINE_TOWER_ANIMATED;   break;
 
			}
 
			SetIndustryGfx(tile, gfx);
 
			SetIndustryAnimationState(tile, 0x80);
 
			AddAnimatedTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_OILWELL_NOT_ANIMATED:
 
		if (CHANCE16(1, 6)) {
 
			SetIndustryGfx(tile, GFX_OILWELL_ANIMATED_1);
 
			SetIndustryAnimationState(tile, 0);
 
			AddAnimatedTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_COAL_MINE_TOWER_ANIMATED:
 
	case GFX_COPPER_MINE_TOWER_ANIMATED:
 
	case GFX_GOLD_MINE_TOWER_ANIMATED:
 
		if (!(_tick_counter & 0x400)) {
 
			switch (gfx) {
 
				case GFX_COAL_MINE_TOWER_ANIMATED:   gfx = GFX_COAL_MINE_TOWER_NOT_ANIMATED;   break;
 
				case GFX_COPPER_MINE_TOWER_ANIMATED: gfx = GFX_COPPER_MINE_TOWER_NOT_ANIMATED; break;
 
				case GFX_GOLD_MINE_TOWER_ANIMATED:   gfx = GFX_GOLD_MINE_TOWER_NOT_ANIMATED;   break;
 
			}
 
			SetIndustryGfx(tile, gfx);
 
			SetIndustryCompleted(tile, true);
 
			SetIndustryConstructionStage(tile, 3);
 
			DeleteAnimatedTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_POWERPLANT_SPARKS:
 
		if (CHANCE16(1,3)) {
 
			SndPlayTileFx(SND_0C_ELECTRIC_SPARK, tile);
 
			AddAnimatedTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_COPPER_MINE_CHIMNEY:
 
		CreateEffectVehicleAbove(TileX(tile) * TILE_SIZE + 6, TileY(tile) * TILE_SIZE + 6, 43, EV_SMOKE);
 
		break;
 

	
 

	
 
	case GFX_TOY_FACTORY: {
 
			Industry *i = GetIndustryByTile(tile);
 
			if (i->was_cargo_delivered) {
 
				i->was_cargo_delivered = false;
 
				SetIndustryAnimationLoop(tile, 0);
 
				AddAnimatedTile(tile);
 
			}
 
		}
 
		break;
 

	
 
	case GFX_BUBBLE_GENERATOR:
 
		TileLoopIndustry_BubbleGenerator(tile);
 
		break;
 

	
 
	case GFX_TOFFEE_QUARY:
 
		AddAnimatedTile(tile);
 
		break;
 

	
 
	case GFX_SUGAR_MINE_SIEVE:
 
		if (CHANCE16(1, 3)) AddAnimatedTile(tile);
 
		break;
 
	}
 
}
 

	
 

	
 
static void ClickTile_Industry(TileIndex tile)
 
{
 
	ShowIndustryViewWindow(GetIndustryIndex(tile));
 
}
 

	
 
static uint32 GetTileTrackStatus_Industry(TileIndex tile, TransportType mode)
 
{
 
	return 0;
 
}
 

	
 
static void GetProducedCargo_Industry(TileIndex tile, CargoID *b)
 
{
 
	const Industry *i = GetIndustryByTile(tile);
 

	
 
	b[0] = i->produced_cargo[0];
 
	b[1] = i->produced_cargo[1];
 
}
 

	
 
static void ChangeTileOwner_Industry(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	/* not used */
 
}
 

	
 
static const byte _plantfarmfield_type[] = {1, 1, 1, 1, 1, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6};
 

	
 
static bool IsBadFarmFieldTile(TileIndex tile)
 
{
 
	switch (GetTileType(tile)) {
 
		case MP_CLEAR: return IsClearGround(tile, CLEAR_FIELDS) || IsClearGround(tile, CLEAR_SNOW);
 
		case MP_TREES: return false;
 
		default:       return true;
 
	}
 
}
 

	
 
static bool IsBadFarmFieldTile2(TileIndex tile)
 
{
 
	switch (GetTileType(tile)) {
 
		case MP_CLEAR: return IsClearGround(tile, CLEAR_SNOW);
 
		case MP_TREES: return false;
 
		default:       return true;
 
	}
 
}
 

	
 
static void SetupFarmFieldFence(TileIndex tile, int size, byte type, Axis direction)
 
{
 
	do {
 
		tile = TILE_MASK(tile);
 

	
 
		if (IsTileType(tile, MP_CLEAR) || IsTileType(tile, MP_TREES)) {
 
			byte or = type;
 

	
 
			if (or == 1 && CHANCE16(1, 7)) or = 2;
 

	
 
			if (direction == AXIS_X) {
 
				SetFenceSE(tile, or);
 
			} else {
 
				SetFenceSW(tile, or);
 
			}
 
		}
 

	
 
		tile += (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
 
	} while (--size);
 
}
 

	
 
static void PlantFarmField(TileIndex tile, IndustryID industry)
 
{
 
	uint size_x, size_y;
 
	uint32 r;
 
	uint count;
 
	uint counter;
 
	uint field_type;
 
	int type;
 

	
 
	if (_opt.landscape == LT_HILLY) {
 
		if (GetTileZ(tile) + TILE_HEIGHT * 2 >= _opt.snow_line)
 
			return;
 
	}
 

	
 
	/* determine field size */
 
	r = (Random() & 0x303) + 0x404;
 
	if (_opt.landscape == LT_HILLY) r += 0x404;
 
	size_x = GB(r, 0, 8);
 
	size_y = GB(r, 8, 8);
 

	
 
	/* offset tile to match size */
 
	tile -= TileDiffXY(size_x / 2, size_y / 2);
 

	
 
	/* check the amount of bad tiles */
 
	count = 0;
 
	BEGIN_TILE_LOOP(cur_tile, size_x, size_y, tile)
 
		cur_tile = TILE_MASK(cur_tile);
 
		count += IsBadFarmFieldTile(cur_tile);
 
	END_TILE_LOOP(cur_tile, size_x, size_y, tile)
 
	if (count * 2 >= size_x * size_y) return;
 

	
 
	/* determine type of field */
 
	r = Random();
 
	counter = GB(r, 5, 3);
 
	field_type = GB(r, 8, 8) * 9 >> 8;
 

	
 
	/* make field */
 
	BEGIN_TILE_LOOP(cur_tile, size_x, size_y, tile)
 
		cur_tile = TILE_MASK(cur_tile);
 
		if (!IsBadFarmFieldTile2(cur_tile)) {
 
			MakeField(cur_tile, field_type, industry);
 
			SetClearCounter(cur_tile, counter);
 
			MarkTileDirtyByTile(cur_tile);
 
		}
 
	END_TILE_LOOP(cur_tile, size_x, size_y, tile)
 

	
 
	type = 3;
 
	if (_opt.landscape != LT_HILLY && _opt.landscape != LT_DESERT) {
 
		type = _plantfarmfield_type[Random() & 0xF];
 
	}
 

	
 
	SetupFarmFieldFence(tile - TileDiffXY(1, 0), size_y, type, AXIS_Y);
 
	SetupFarmFieldFence(tile - TileDiffXY(0, 1), size_x, type, AXIS_X);
 
	SetupFarmFieldFence(tile + TileDiffXY(size_x - 1, 0), size_y, type, AXIS_Y);
 
	SetupFarmFieldFence(tile + TileDiffXY(0, size_y - 1), size_x, type, AXIS_X);
 
}
 

	
 
void PlantRandomFarmField(const Industry *i)
 
{
 
	int x = i->width  / 2 + Random() % 31 - 16;
 
	int y = i->height / 2 + Random() % 31 - 16;
 

	
 
	TileIndex tile = TileAddWrap(i->xy, x, y);
 

	
 
	if (tile != INVALID_TILE) PlantFarmField(tile, i->index);
 
}
 

	
 
static void MaybePlantFarmField(const Industry *i)
 
{
 
	if (CHANCE16(1, 8)) PlantRandomFarmField(i);
 
}
 

	
 
/**
 
 * Search callback function for ChopLumberMillTrees
 
 * @param tile to test
 
 * @param data that is passed by the caller.  In this case, nothing
 
 * @result of the test
 
 */
 
static bool SearchLumberMillTrees(TileIndex tile, uint32 data)
 
{
 
	if (IsTileType(tile, MP_TREES)) {
 
		PlayerID old_player = _current_player;
 
		/* found a tree */
 

	
 
		_current_player = OWNER_NONE;
 
		_industry_sound_ctr = 1;
 
		_industry_sound_tile = tile;
 
		SndPlayTileFx(SND_38_CHAINSAW, tile);
 

	
 
		DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
		SetTropicZone(tile, TROPICZONE_INVALID);
 

	
 
		_current_player = old_player;
 
		return true;
 
	}
 
	return false;
 
}
 

	
 
/**
 
 * Perform a circular search around the Lumber Mill in order to find trees to cut
 
 * @param i industry
 
 */
 
static void ChopLumberMillTrees(Industry *i)
 
{
 
	TileIndex tile = i->xy;
 

	
 
	if (!IsIndustryCompleted(tile)) return;  ///< Can't proceed if not completed
 

	
 
	if (CircularTileSearch(tile, 40, SearchLumberMillTrees, 0)) ///< 40x40 tiles  to search
 
		i->cargo_waiting[0] = min(0xffff, i->cargo_waiting[0] + 45); ///< Found a tree, add according value to waiting cargo
 
}
 

	
 
static const byte _industry_sounds[37][2] = {
 
	{0},
 
	{0},
 
	{1, SND_28_SAWMILL},
 
	{0},
 
	{0},
 
	{0},
 
	{1, SND_03_FACTORY_WHISTLE},
 
	{1, SND_03_FACTORY_WHISTLE},
 
	{0},
 
	{3, SND_24_SHEEP},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{1, SND_28_SAWMILL},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{1, SND_03_FACTORY_WHISTLE},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
	{1, SND_33_PLASTIC_MINE},
 
	{0},
 
	{0},
 
	{0},
 
	{0},
 
};
 

	
 

	
 
static void ProduceIndustryGoods(Industry *i)
 
{
 
	uint32 r;
 
	uint num;
 

	
 
	/* play a sound? */
 
	if ((i->counter & 0x3F) == 0) {
 
		if (CHANCE16R(1,14,r) && (num=_industry_sounds[i->type][0]) != 0) {
 
			SndPlayTileFx(
 
				_industry_sounds[i->type][1] + (((r >> 16) * num) >> 16),
 
				i->xy);
 
		}
 
	}
 

	
 
	i->counter--;
 

	
 
	/* produce some cargo */
 
	if ((i->counter & 0xFF) == 0) {
 
		i->cargo_waiting[0] = min(0xffff, i->cargo_waiting[0] + i->production_rate[0]);
 
		i->cargo_waiting[1] = min(0xffff, i->cargo_waiting[1] + i->production_rate[1]);
 

	
 
		if (i->type == IT_FARM) {
 
			MaybePlantFarmField(i);
 
		} else if (i->type == IT_LUMBER_MILL && (i->counter & 0x1FF) == 0) {
 
			ChopLumberMillTrees(i);
 
		}
 
	}
 
}
 

	
 
void OnTick_Industry(void)
 
{
 
	Industry *i;
 

	
 
	if (_industry_sound_ctr != 0) {
 
		_industry_sound_ctr++;
 

	
 
		if (_industry_sound_ctr == 75) {
 
			SndPlayTileFx(SND_37_BALLOON_SQUEAK, _industry_sound_tile);
 
		} else if (_industry_sound_ctr == 160) {
 
			_industry_sound_ctr = 0;
 
			SndPlayTileFx(SND_36_CARTOON_CRASH, _industry_sound_tile);
 
		}
 
	}
 

	
 
	if (_game_mode == GM_EDITOR) return;
 

	
 
	FOR_ALL_INDUSTRIES(i) {
 
		ProduceIndustryGoods(i);
 
	}
 
}
 

	
 

	
 
static bool CheckNewIndustry_NULL(TileIndex tile)
 
{
 
	return true;
 
}
 

	
 
static bool CheckNewIndustry_Forest(TileIndex tile)
 
{
 
	if (_opt.landscape == LT_HILLY) {
 
		if (GetTileZ(tile) < _opt.snow_line + TILE_HEIGHT * 2U) {
 
			_error_message = STR_4831_FOREST_CAN_ONLY_BE_PLANTED;
 
			return false;
 
		}
 
	}
 
	return true;
 
}
 

	
 
static bool CheckNewIndustry_OilRefinery(TileIndex tile)
 
{
 
	if (_game_mode == GM_EDITOR) return true;
 
	if (DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true;
 

	
 
	_error_message = STR_483B_CAN_ONLY_BE_POSITIONED;
 
	return false;
 
}
 

	
 
extern bool _ignore_restrictions;
 

	
 
static bool CheckNewIndustry_OilRig(TileIndex tile)
 
{
 
	if (_game_mode == GM_EDITOR && _ignore_restrictions) return true;
 
	if (TileHeight(tile) == 0 &&
 
			DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true;
 

	
 
	_error_message = STR_483B_CAN_ONLY_BE_POSITIONED;
 
	return false;
 
}
 

	
 
static bool CheckNewIndustry_Farm(TileIndex tile)
 
{
 
	if (_opt.landscape == LT_HILLY) {
 
		if (GetTileZ(tile) + TILE_HEIGHT * 2 >= _opt.snow_line) {
 
			_error_message = STR_0239_SITE_UNSUITABLE;
 
			return false;
 
		}
 
	}
 
	return true;
 
}
 

	
 
static bool CheckNewIndustry_Plantation(TileIndex tile)
 
{
 
	if (GetTropicZone(tile) == TROPICZONE_DESERT) {
 
		_error_message = STR_0239_SITE_UNSUITABLE;
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
static bool CheckNewIndustry_Water(TileIndex tile)
 
{
 
	if (GetTropicZone(tile) != TROPICZONE_DESERT) {
 
		_error_message = STR_0318_CAN_ONLY_BE_BUILT_IN_DESERT;
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
static bool CheckNewIndustry_Lumbermill(TileIndex tile)
 
{
 
	if (GetTropicZone(tile) != TROPICZONE_RAINFOREST) {
 
		_error_message = STR_0317_CAN_ONLY_BE_BUILT_IN_RAINFOREST;
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
static bool CheckNewIndustry_BubbleGen(TileIndex tile)
 
{
 
	return GetTileZ(tile) <= TILE_HEIGHT * 4;
 
}
 

	
 
typedef bool CheckNewIndustryProc(TileIndex tile);
 
static CheckNewIndustryProc * const _check_new_industry_procs[CHECK_END] = {
 
	CheckNewIndustry_NULL,
 
	CheckNewIndustry_Forest,
 
	CheckNewIndustry_OilRefinery,
 
	CheckNewIndustry_Farm,
 
	CheckNewIndustry_Plantation,
 
	CheckNewIndustry_Water,
 
	CheckNewIndustry_Lumbermill,
 
	CheckNewIndustry_BubbleGen,
 
	CheckNewIndustry_OilRig
 
};
 

	
 
static bool CheckSuitableIndustryPos(TileIndex tile)
 
{
 
	uint x = TileX(tile);
 
	uint y = TileY(tile);
 

	
 
	if (x < 2 || y < 2 || x > MapMaxX() - 3 || y > MapMaxY() - 3) {
 
		_error_message = STR_0239_SITE_UNSUITABLE;
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
static const Town *CheckMultipleIndustryInTown(TileIndex tile, int type)
 
{
 
	const Town *t;
 
	const Industry *i;
 

	
 
	t = ClosestTownFromTile(tile, (uint)-1);
 

	
 
	if (_patches.multiple_industry_per_town) return t;
 

	
 
	FOR_ALL_INDUSTRIES(i) {
 
		if (i->type == (byte)type &&
 
				i->town == t) {
 
			_error_message = STR_0287_ONLY_ONE_ALLOWED_PER_TOWN;
 
			return NULL;
 
		}
 
	}
 

	
 
	return t;
 
}
 

	
 
static bool CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileTable *it, int type)
 
{
 
	_error_message = STR_0239_SITE_UNSUITABLE;
 

	
 
	do {
 
		TileIndex cur_tile = tile + ToTileIndexDiff(it->ti);
 

	
 
		if (!IsValidTile(cur_tile)) {
 
			if (it->gfx == 0xff) continue;
 
			return false;
 
		}
 

	
 
		if (it->gfx == 0xFF) {
 
			if (!IsTileType(cur_tile, MP_WATER) ||
 
					GetTileSlope(cur_tile, NULL) != SLOPE_FLAT) {
 
				return false;
 
			}
 
		} else {
 
			if (!EnsureNoVehicle(cur_tile)) return false;
 

	
 
			if (type == IT_OIL_RIG)  {
 
				if (!IsClearWaterTile(cur_tile)) return false;
 
			} else {
 
				Slope tileh;
 

	
 
				if (IsClearWaterTile(cur_tile)) return false;
 

	
 
				tileh = GetTileSlope(cur_tile, NULL);
 
				if (IsSteepSlope(tileh)) return false;
 

	
 
				if (_patches.land_generator != LG_TERRAGENESIS || !_generating_world) {
 
					/* It is almost impossible to have a fully flat land in TG, so what we
 
					 *  do is that we check if we can make the land flat later on. See
 
					 *  CheckIfCanLevelIndustryPlatform(). */
 
					if (tileh != SLOPE_FLAT) {
 
						Slope t;
 
						byte bits = _industry_section_bits[it->gfx];
 

	
 
						if (bits & 0x10) return false;
 

	
 
						t = ComplementSlope(tileh);
 

	
 
						if (bits & 1 && (t & SLOPE_NW)) return false;
 
						if (bits & 2 && (t & SLOPE_NE)) return false;
 
						if (bits & 4 && (t & SLOPE_SW)) return false;
 
						if (bits & 8 && (t & SLOPE_SE)) return false;
 
					}
 
				}
 

	
 
				if (type == IT_BANK_TEMP) {
 
					if (!IsTileType(cur_tile, MP_HOUSE)) {
 
						_error_message = STR_029D_CAN_ONLY_BE_BUILT_IN_TOWNS;
 
						return false;
 
					}
 
				} else if (type == IT_BANK_TROPIC_ARCTIC) {
 
					if (!IsTileType(cur_tile, MP_HOUSE)) {
 
						_error_message = STR_030D_CAN_ONLY_BE_BUILT_IN_TOWNS;
 
						return false;
 
					}
 
				} else if (type == IT_TOY_SHOP) {
 
					if (!IsTileType(cur_tile, MP_HOUSE)) goto do_clear;
 
				} else if (type == IT_WATER_TOWER) {
 
					if (!IsTileType(cur_tile, MP_HOUSE)) {
 
						_error_message = STR_0316_CAN_ONLY_BE_BUILT_IN_TOWNS;
 
						return false;
 
					}
 
				} else {
 
do_clear:
 
					if (CmdFailed(DoCommand(cur_tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR)))
 
						return false;
 
				}
 
			}
 
		}
 
	} while ((++it)->ti.x != -0x80);
 

	
 
	return true;
 
}
 

	
 
static bool CheckIfIndustryIsAllowed(TileIndex tile, int type, const Town *t)
 
{
 
	if (type == IT_BANK_TEMP && t->population < 1200) {
 
		_error_message = STR_029D_CAN_ONLY_BE_BUILT_IN_TOWNS;
 
		return false;
 
	}
 

	
 
	if (type == IT_TOY_SHOP && DistanceMax(t->xy, tile) > 9) {
 
		_error_message = STR_0239_SITE_UNSUITABLE;
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
static bool CheckCanTerraformSurroundingTiles(TileIndex tile, uint height, int internal)
 
{
 
	int size_x, size_y;
 
	uint curh;
 

	
 
	size_x = 2;
 
	size_y = 2;
 

	
 
	/* Check if we don't leave the map */
 
	if (TileX(tile) == 0 || TileY(tile) == 0 || GetTileType(tile) == MP_VOID) return false;
 

	
 
	tile += TileDiffXY(-1, -1);
 
	BEGIN_TILE_LOOP(tile_walk, size_x, size_y, tile) {
 
		curh = TileHeight(tile_walk);
 
		/* Is the tile clear? */
 
		if ((GetTileType(tile_walk) != MP_CLEAR) && (GetTileType(tile_walk) != MP_TREES))
 
			return false;
 

	
 
		/* Don't allow too big of a change if this is the sub-tile check */
 
		if (internal != 0 && myabs(curh - height) > 1) return false;
 

	
 
		/* Different height, so the surrounding tiles of this tile
 
		 *  has to be correct too (in level, or almost in level)
 
		 *  else you get a chain-reaction of terraforming. */
 
		if (internal == 0 && curh != height) {
 
			if (!CheckCanTerraformSurroundingTiles(tile_walk + TileDiffXY(-1, -1), height, internal + 1))
 
				return false;
 
		}
 
	} END_TILE_LOOP(tile_walk, size_x, size_y, tile);
 

	
 
	return true;
 
}
 

	
 
/**
 
 * This function tries to flatten out the land below an industry, without
 
 *  damaging the surroundings too much.
 
 */
 
static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, uint32 flags, const IndustryTileTable* it, int type)
 
{
 
	const int MKEND = -0x80;   // used for last element in an IndustryTileTable (see build_industry.h)
 
	int max_x = 0;
 
	int max_y = 0;
 
	TileIndex cur_tile;
 
	uint size_x, size_y;
 
	uint h, curh;
 

	
 
	/* Finds dimensions of largest variant of this industry */
 
	do {
 
		if (it->ti.x > max_x) max_x = it->ti.x;
 
		if (it->ti.y > max_y) max_y = it->ti.y;
 
	} while ((++it)->ti.x != MKEND);
 

	
 
	/* Remember level height */
 
	h = TileHeight(tile);
 

	
 
	/* Check that all tiles in area and surrounding are clear
 
	 * this determines that there are no obstructing items */
 
	cur_tile = tile + TileDiffXY(-1, -1);
 
	size_x = max_x + 4;
 
	size_y = max_y + 4;
 

	
 
	/* Check if we don't leave the map */
 
	if (TileX(cur_tile) == 0 || TileY(cur_tile) == 0 || TileX(cur_tile) + size_x >= MapMaxX() || TileY(cur_tile) + size_y >= MapMaxY()) return false;
 

	
 
	BEGIN_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) {
 
		curh = TileHeight(tile_walk);
 
		if (curh != h) {
 
			/* This tile needs terraforming. Check if we can do that without
 
			 *  damaging the surroundings too much. */
 
			if (!CheckCanTerraformSurroundingTiles(tile_walk, h, 0)) return false;
 
			/* This is not 100% correct check, but the best we can do without modifying the map.
 
			 *  What is missing, is if the difference in height is more than 1.. */
 
			if (CmdFailed(DoCommand(tile_walk, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND))) return false;
 
		}
 
	} END_TILE_LOOP(tile_walk, size_x, size_y, cur_tile)
 

	
 
	if (flags & DC_EXEC) {
 
		/* Terraform the land under the industry */
 
		BEGIN_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) {
 
			curh = TileHeight(tile_walk);
 
			while (curh != h) {
 
				/* We give the terraforming for free here, because we can't calculate
 
				 *  exact cost in the test-round, and as we all know, that will cause
 
				 *  a nice assert if they don't match ;) */
 
				DoCommand(tile_walk, 8, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND);
 
				curh += (curh > h) ? -1 : 1;
 
			}
 
		} END_TILE_LOOP(tile_walk, size_x, size_y, cur_tile)
 
	}
 

	
 
	return true;
 
}
 

	
 

	
 
static bool CheckIfTooCloseToIndustry(TileIndex tile, int type)
 
{
 
	const IndustrySpec *indspec = GetIndustrySpec(type);
 
	const Industry *i;
 

	
 
	// accepting industries won't be close, not even with patch
 
	if (_patches.same_industry_close && indspec->accepts_cargo[0] == CT_INVALID)
 
		return true;
 

	
 
	FOR_ALL_INDUSTRIES(i) {
 
		// check if an industry that accepts the same goods is nearby
 
		if (DistanceMax(tile, i->xy) <= 14 &&
 
				indspec->accepts_cargo[0] != CT_INVALID &&
 
				indspec->accepts_cargo[0] == i->accepts_cargo[0] && (
 
					_game_mode != GM_EDITOR ||
 
					!_patches.same_industry_close ||
 
					!_patches.multiple_industry_per_town
 
				)) {
 
			_error_message = STR_INDUSTRY_TOO_CLOSE;
 
			return false;
 
		}
 

	
 
		// check "not close to" field.
 
		if ((i->type == indspec->conflicting[0] || i->type == indspec->conflicting[1] || i->type == indspec->conflicting[2]) &&
 
				DistanceMax(tile, i->xy) <= 14) {
 
			_error_message = STR_INDUSTRY_TOO_CLOSE;
 
			return false;
 
		}
 
	}
 
	return true;
 
}
 

	
 
static Industry *AllocateIndustry(void)
 
{
 
	Industry *i;
 

	
 
	/* 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 (i = GetIndustry(0); i != NULL; i = (i->index + 1U < GetIndustryPoolSize()) ? GetIndustry(i->index + 1U) : NULL) {
 
		IndustryID index = i->index;
 

	
 
		if (IsValidIndustry(i)) continue;
 

	
 
		memset(i, 0, sizeof(*i));
 
		i->index = index;
 

	
 
		return i;
 
	}
 

	
 
	/* Check if we can add a block to the pool */
 
	return AddBlockToPool(&_Industry_pool) ? AllocateIndustry() : NULL;
 
}
 

	
 
static void DoCreateNewIndustry(Industry *i, TileIndex tile, int type, const IndustryTileTable *it, const Town *t, byte owner)
 
{
 
	const IndustrySpec *indspec = GetIndustrySpec(type);
 
	uint32 r;
 
	int j;
 

	
 
	_total_industries++;
 
	i->xy = tile;
 
	i->width = i->height = 0;
 
	i->type = type;
 

	
 
	i->produced_cargo[0] = indspec->produced_cargo[0];
 
	i->produced_cargo[1] = indspec->produced_cargo[1];
 
	i->accepts_cargo[0] = indspec->accepts_cargo[0];
 
	i->accepts_cargo[1] = indspec->accepts_cargo[1];
 
	i->accepts_cargo[2] = indspec->accepts_cargo[2];
 
	i->production_rate[0] = indspec->production_rate[0];
 
	i->production_rate[1] = indspec->production_rate[1];
 

	
 
	if (_patches.smooth_economy) {
 
		i->production_rate[0] = min((RandomRange(256) + 128) * i->production_rate[0] >> 8 , 255);
 
		i->production_rate[1] = min((RandomRange(256) + 128) * i->production_rate[1] >> 8 , 255);
 
	}
 

	
 
	i->town = t;
 
	i->owner = owner;
 

	
 
	r = Random();
 
	i->random_color = GB(r, 8, 4);
 
	i->counter = GB(r, 0, 12);
 
	i->cargo_waiting[0] = 0;
 
	i->cargo_waiting[1] = 0;
 
	i->last_mo_production[0] = 0;
 
	i->last_mo_production[1] = 0;
 
	i->last_mo_transported[0] = 0;
 
	i->last_mo_transported[1] = 0;
 
	i->pct_transported[0] = 0;
 
	i->pct_transported[1] = 0;
 
	i->total_transported[0] = 0;
 
	i->total_transported[1] = 0;
 
	i->was_cargo_delivered = false;
 
	i->last_prod_year = _cur_year;
 
	i->total_production[0] = i->production_rate[0] * 8;
 
	i->total_production[1] = i->production_rate[1] * 8;
 

	
 
	if (!_generating_world) i->total_production[0] = i->total_production[1] = 0;
 

	
 
	i->prod_level = 0x10;
 

	
 
	do {
 
		TileIndex cur_tile = tile + ToTileIndexDiff(it->ti);
 

	
 
		if (it->gfx != 0xFF) {
 
			byte size;
 

	
 
			size = it->ti.x;
 
			if (size > i->width) i->width = size;
 
			size = it->ti.y;
 
			if (size > i->height)i->height = size;
 

	
 
			DoCommand(cur_tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 

	
 
			MakeIndustry(cur_tile, i->index, it->gfx);
 
			if (_generating_world) {
 
				SetIndustryConstructionCounter(cur_tile, 3);
 
				SetIndustryConstructionStage(cur_tile, 2);
 
			}
 
		}
 
	} while ((++it)->ti.x != -0x80);
 

	
 
	i->width++;
 
	i->height++;
 

	
 
	if (i->type == IT_FARM || i->type == IT_FARM_2) {
 
		for (j = 0; j != 50; j++) PlantRandomFarmField(i);
 
	}
 
	_industry_sort_dirty = true;
 
	InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0);
 
}
 

	
 
static Industry *CreateNewIndustryHelper(TileIndex tile, IndustryType type, uint32 flags, const IndustrySpec *indspec, const IndustryTileTable *it)
 
{
 
	const Town *t;
 
	Industry *i;
 

	
 
	if (!CheckIfIndustryTilesAreFree(tile, it, type)) return NULL;
 
	if (_patches.land_generator == LG_TERRAGENESIS && _generating_world && !CheckIfCanLevelIndustryPlatform(tile, 0, it, type)) return NULL;
 
	if (!_check_new_industry_procs[indspec->check_proc](tile)) return NULL;
 
	if (!CheckIfTooCloseToIndustry(tile, type)) return NULL;
 

	
 
	t = CheckMultipleIndustryInTown(tile, type);
 
	if (t == NULL) return NULL;
 

	
 
	if (!CheckIfIndustryIsAllowed(tile, type, t)) return NULL;
 
	if (!CheckSuitableIndustryPos(tile)) return NULL;
 

	
 
	i = AllocateIndustry();
 
	if (i == NULL) return NULL;
 

	
 
	if (flags & DC_EXEC) {
 
		CheckIfCanLevelIndustryPlatform(tile, DC_EXEC, it, type);
 
		DoCreateNewIndustry(i, tile, type, it, t, OWNER_NONE);
 
	}
 

	
 
	return i;
 
}
 

	
 
/** Build/Fund an industry
 
 * @param tile tile where industry is built
 
 * @param p1 industry type @see build_industry.h and @see industry.h
 
 * @param p2 unused
 
 */
 
int32 CmdBuildIndustry(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int num;
 
	const IndustryTileTable * const *itt;
 
	const IndustryTileTable *it;
 
	const IndustrySpec *indspec;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_OTHER);
 

	
 
	indspec = GetIndustrySpec(p1);
 

	
 
	/* Check if the to-be built/founded industry is available for this climate. */
 
	if (!HASBIT(indspec->climate_availability, _opt_ptr->landscape)) return CMD_ERROR;
 

	
 
	/* If the patch for raw-material industries is not on, you cannot build raw-material industries.
 
	 * Raw material industries are industries that do not accept cargo (at least for now)
 
	 * Exclude the lumber mill (only "raw" industry that can be built) */
 
	if (!_patches.build_rawmaterial_ind &&
 
			indspec->accepts_cargo[0] == CT_INVALID &&
 
			indspec->accepts_cargo[1] == CT_INVALID &&
 
			indspec->accepts_cargo[2] == CT_INVALID &&
 
			p1 != IT_LUMBER_MILL) {
 
		return CMD_ERROR;
 
	}
 

	
 
	num = indspec->num_table;
 
	itt = indspec->table;
 

	
 
	do {
 
		if (--num < 0) return_cmd_error(STR_0239_SITE_UNSUITABLE);
 
	} while (!CheckIfIndustryTilesAreFree(tile, it = itt[num], p1));
 

	
 
	if (CreateNewIndustryHelper(tile, p1, flags, indspec, it) == NULL) return CMD_ERROR;
 

	
 
	return (_price.build_industry >> 5) * indspec->cost_multiplier;
 
}
 

	
 

	
 
Industry *CreateNewIndustry(TileIndex tile, IndustryType type)
 
{
 
	const IndustrySpec *indspec = GetIndustrySpec(type);
 
	const IndustryTileTable *it = indspec->table[RandomRange(indspec->num_table)];
 

	
 
	return CreateNewIndustryHelper(tile, type, DC_EXEC, indspec, it);
 
}
 

	
 
static const byte _numof_industry_table[4][12] = {
 
	// difficulty settings for number of industries
 
	{0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0},   //none
 
	{0, 1, 1, 1, 2, 2, 3, 3,  4,  4,  5},   //low
 
	{0, 1, 2, 3, 4, 5, 6, 7,  8,  9, 10},   //normal
 
	{0, 2, 3, 4, 6, 7, 8, 9, 10, 10, 10},   //high
 
};
 

	
 
static void PlaceInitialIndustry(IndustryType type, int amount)
 
{
 
	int num = _numof_industry_table[_opt.diff.number_industries][amount];
 

	
 
	if (type == IT_OIL_REFINERY || type == IT_OIL_RIG) {
 
		// These are always placed next to the coastline, so we scale by the perimeter instead.
 
		num = ScaleByMapSize1D(num);
 
	} else {
 
		num = ScaleByMapSize(num);
 
	}
 

	
 
	if (_opt.diff.number_industries != 0) {
 
		PlayerID old_player = _current_player;
 
		_current_player = OWNER_NONE;
 
		assert(num > 0);
 

	
 
		do {
 
			uint i;
 

	
 
			IncreaseGeneratingWorldProgress(GWP_INDUSTRY);
 

	
 
			for (i = 0; i < 2000; i++) {
 
				if (CreateNewIndustry(RandomTile(), type) != NULL) break;
 
			}
 
		} while (--num);
 

	
 
		_current_player = old_player;
 
	}
 
}
 

	
 
void GenerateIndustries(void)
 
{
 
	const byte *b;
 
	uint i = 0;
 

	
 
	/* Find the total amount of industries */
 
	b = _industry_create_table[_opt.landscape];
 
	do {
 
		int num = _numof_industry_table[_opt.diff.number_industries][b[0]];
 

	
 
		if (b[1] == IT_OIL_REFINERY || b[1] == IT_OIL_RIG) {
 
			/* These are always placed next to the coastline, so we scale by the perimeter instead. */
 
			num = ScaleByMapSize1D(num);
 
		} else {
 
			num = ScaleByMapSize(num);
 
		}
 

	
 
		i += num;
 
	} while ( (b+=2)[0] != 0);
 
	SetGeneratingWorldProgress(GWP_INDUSTRY, i);
 

	
 
	b = _industry_create_table[_opt.landscape];
 
	do {
 
		PlaceInitialIndustry(b[1], b[0]);
 
	} while ( (b+=2)[0] != 0);
 
}
 

	
 
/* Change industry production or do closure */
 
static void ExtChangeIndustryProduction(Industry *i)
 
{
 
	bool closeit = true;
 
	int j;
 
	const IndustrySpec *indspec = GetIndustrySpec(i->type);
 

	
 
	switch (indspec->life_type) {
 
		case INDUSTRYLIFE_NOT_CLOSABLE:
 
			return;
 

	
 
		case INDUSTRYLIFE_CLOSABLE:
 
			if ((byte)(_cur_year - i->last_prod_year) < 5 || !CHANCE16(1, 180))
 
				closeit = false;
 
			break;
 

	
 
		default: /* INDUSTRY_PRODUCTION */
 
			for (j = 0; j < 2 && i->produced_cargo[j] != CT_INVALID; j++){
 
				uint32 r = Random();
 
				int old, new, percent;
 
				int mag;
 

	
 
				new = old = i->production_rate[j];
 
				if (CHANCE16I(20, 1024, r))
 
					new -= ((RandomRange(50) + 10) * old) >> 8;
 
				if (CHANCE16I(20 + (i->pct_transported[j] * 20 >> 8), 1024, r >> 16))
 
					new += ((RandomRange(50) + 10) * old) >> 8;
 

	
 
				new = clamp(new, 0, 255);
 
				if (new == old) {
 
					closeit = false;
 
					continue;
 
				}
 

	
 
				percent = new * 100 / old - 100;
 
				i->production_rate[j] = new;
 

	
 
				if (new >= indspec->production_rate[j] / 4)
 
					closeit = false;
 

	
 
				mag = abs(percent);
 
				if (mag >= 10) {
 
					SetDParam(2, mag);
 
					SetDParam(0, _cargoc.names_s[i->produced_cargo[j]]);
 
					SetDParam(1, i->index);
 
					AddNewsItem(
 
						percent >= 0 ? STR_INDUSTRY_PROD_GOUP : STR_INDUSTRY_PROD_GODOWN,
 
						NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_TILE, NT_ECONOMY, 0),
 
						i->xy + TileDiffXY(1, 1), 0
 
					);
 
				}
 
			}
 
			break;
 
	}
 

	
 
	/* If industry will be closed down, show this */
 
	if (closeit) {
 
		i->prod_level = 0;
 
		SetDParam(0, i->index);
 
		AddNewsItem(
 
			indspec->closure_text,
 
			NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_TILE, NT_ECONOMY, 0),
 
			i->xy + TileDiffXY(1, 1), 0
 
		);
 
	}
 
}
 

	
 

	
 
static void UpdateIndustryStatistics(Industry *i)
 
{
 
	byte pct;
 

	
 
	if (i->produced_cargo[0] != CT_INVALID) {
 
		pct = 0;
 
		if (i->last_mo_production[0] != 0) {
 
			i->last_prod_year = _cur_year;
 
			pct = min(i->last_mo_transported[0] * 256 / i->last_mo_production[0],255);
 
		}
 
		i->pct_transported[0] = pct;
 

	
 
		i->total_production[0] = i->last_mo_production[0];
 
		i->last_mo_production[0] = 0;
 

	
 
		i->total_transported[0] = i->last_mo_transported[0];
 
		i->last_mo_transported[0] = 0;
 
	}
 

	
 
	if (i->produced_cargo[1] != CT_INVALID) {
 
		pct = 0;
 
		if (i->last_mo_production[1] != 0) {
 
			i->last_prod_year = _cur_year;
 
			pct = min(i->last_mo_transported[1] * 256 / i->last_mo_production[1],255);
 
		}
 
		i->pct_transported[1] = pct;
 

	
 
		i->total_production[1] = i->last_mo_production[1];
 
		i->last_mo_production[1] = 0;
 

	
 
		i->total_transported[1] = i->last_mo_transported[1];
 
		i->last_mo_transported[1] = 0;
 
	}
 

	
 

	
 
	if (i->produced_cargo[0] != CT_INVALID || i->produced_cargo[1] != CT_INVALID)
 
		InvalidateWindow(WC_INDUSTRY_VIEW, i->index);
 

	
 
	if (i->prod_level == 0) {
 
		DeleteIndustry(i);
 
	} else if (_patches.smooth_economy) {
 
		ExtChangeIndustryProduction(i);
 
	}
 
}
 

	
 
static const byte _new_industry_rand[4][32] = {
 
	{12, 12, 12, 12, 12, 12, 12,  0,  0,  6,  6,  9,  9,  3,  3,  3, 18, 18,  4,  4,  2,  2,  5,  5,  5,  5,  5,  5,  1,  1,  8,  8},
 
	{16, 16, 16,  0,  0,  0,  9,  9,  9,  9, 13, 13,  3,  3,  3,  3, 15, 15, 15,  4,  4, 11, 11, 11, 11, 11, 14, 14,  1,  1,  7,  7},
 
	{21, 21, 21, 24, 22, 22, 22, 22, 23, 23, 16, 16, 16,  4,  4, 19, 19, 19, 13, 13, 20, 20, 20, 11, 11, 11, 17, 17, 17, 10, 10, 10},
 
	{30, 30, 30, 36, 36, 31, 31, 31, 27, 27, 27, 28, 28, 28, 26, 26, 26, 34, 34, 34, 35, 35, 35, 29, 29, 29, 32, 32, 32, 33, 33, 33},
 
};
 

	
 
static void MaybeNewIndustry(uint32 r)
 
{
 
	int type;
 
	int j;
 
	Industry *i;
 

	
 
	type = _new_industry_rand[_opt.landscape][GB(r, 16, 5)];
 

	
 
	if (type == IT_OIL_WELL && _cur_year > 1950) return;
 
	if (type == IT_OIL_RIG  && _cur_year < 1960) return;
 

	
 
	j = 2000;
 
	for (;;) {
 
		i = CreateNewIndustry(RandomTile(), type);
 
		if (i != NULL) break;
 
		if (--j == 0) return;
 
	}
 

	
 
	SetDParam(0, GetIndustrySpec(type)->name);
 
	SetDParam(1, i->town->index);
 
	AddNewsItem(
 
		(type != IT_FOREST && type != IT_FRUIT_PLANTATION && type != IT_RUBBER_PLANTATION && type != IT_COTTON_CANDY) ?
 
			STR_482D_NEW_UNDER_CONSTRUCTION : STR_482E_NEW_BEING_PLANTED_NEAR,
 
		NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_TILE, NT_ECONOMY,0), i->xy, 0
 
	);
 
}
 

	
 
static void ChangeIndustryProduction(Industry *i)
 
{
 
	bool only_decrease = false;
 
	StringID str = STR_NULL;
 
	int type = i->type;
 
	const IndustrySpec *indspec = GetIndustrySpec(type);
 

	
 
	switch (indspec->life_type) {
 
		case INDUSTRYLIFE_NOT_CLOSABLE:
 
			return;
 

	
 
		case INDUSTRYLIFE_PRODUCTION:
 
			/* decrease or increase */
 
			if (type == IT_OIL_WELL && _opt.landscape == LT_NORMAL)
 
				only_decrease = true;
 

	
 
			if (only_decrease || CHANCE16(1,3)) {
 
				/* If you transport > 60%, 66% chance we increase, else 33% chance we increase */
 
				if (!only_decrease && (i->pct_transported[0] > 153) != CHANCE16(1,3)) {
 
					/* Increase production */
 
					if (i->prod_level != 0x80) {
 
						byte b;
 

	
 
						i->prod_level <<= 1;
 

	
 
						b = i->production_rate[0] * 2;
 
						if (i->production_rate[0] >= 128)
 
							b = 0xFF;
 
						i->production_rate[0] = b;
 

	
 
						b = i->production_rate[1] * 2;
 
						if (i->production_rate[1] >= 128)
 
							b = 0xFF;
 
						i->production_rate[1] = b;
 

	
 
						str = indspec->production_up_text;
 
					}
 
				} else {
 
					/* Decrease production */
 
					if (i->prod_level == 4) {
 
						i->prod_level = 0;
 
						str = indspec->closure_text;
 
					} else {
 
						i->prod_level >>= 1;
 
						i->production_rate[0] = (i->production_rate[0] + 1) >> 1;
 
						i->production_rate[1] = (i->production_rate[1] + 1) >> 1;
 

	
 
						str = indspec->production_down_text;
 
					}
 
				}
 
			}
 
			break;
 

	
 
		case INDUSTRYLIFE_CLOSABLE:
 
			/* maybe close */
 
			if ( (byte)(_cur_year - i->last_prod_year) >= 5 && CHANCE16(1,2)) {
 
				i->prod_level = 0;
 
				str = indspec->closure_text;
 
			}
 
			break;
 
	}
 

	
 
	if (str != STR_NULL) {
 
		SetDParam(0, i->index);
 
		AddNewsItem(str, NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_TILE, NT_ECONOMY, 0), i->xy + TileDiffXY(1, 1), 0);
 
	}
 
}
 

	
 
void IndustryMonthlyLoop(void)
 
{
 
	Industry *i;
 
	PlayerID old_player = _current_player;
 
	_current_player = OWNER_NONE;
 

	
 
	FOR_ALL_INDUSTRIES(i) {
 
		UpdateIndustryStatistics(i);
 
	}
 

	
 
	/* 3% chance that we start a new industry */
 
	if (CHANCE16(3, 100)) {
 
		MaybeNewIndustry(Random());
 
	} else if (!_patches.smooth_economy) {
 
		i = GetRandomIndustry();
 
		if (i != NULL) ChangeIndustryProduction(i);
 
	}
 

	
 
	_current_player = old_player;
 

	
 
	// production-change
 
	_industry_sort_dirty = true;
 
	InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0);
 
}
 

	
 

	
 
void InitializeIndustries(void)
 
{
 
	CleanPool(&_Industry_pool);
 
	AddBlockToPool(&_Industry_pool);
 

	
 
	_total_industries = 0;
 
	_industry_sort_dirty = true;
 
	_industry_sound_tile = 0;
 
}
 

	
 
const TileTypeProcs _tile_type_industry_procs = {
 
	DrawTile_Industry,           /* draw_tile_proc */
 
	GetSlopeZ_Industry,          /* get_slope_z_proc */
 
	ClearTile_Industry,          /* clear_tile_proc */
 
	GetAcceptedCargo_Industry,   /* get_accepted_cargo_proc */
 
	GetTileDesc_Industry,        /* get_tile_desc_proc */
 
	GetTileTrackStatus_Industry, /* get_tile_track_status_proc */
 
	ClickTile_Industry,          /* click_tile_proc */
 
	AnimateTile_Industry,        /* animate_tile_proc */
 
	TileLoop_Industry,           /* tile_loop_proc */
 
	ChangeTileOwner_Industry,    /* change_tile_owner_proc */
 
	GetProducedCargo_Industry,   /* get_produced_cargo_proc */
 
	NULL,                        /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_Industry,      /* get_slope_tileh_proc */
 
};
 

	
 
static const SaveLoad _industry_desc[] = {
 
	SLE_CONDVAR(Industry, xy,                  SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Industry, xy,                  SLE_UINT32,                  6, SL_MAX_VERSION),
 
	    SLE_VAR(Industry, width,               SLE_UINT8),
 
	    SLE_VAR(Industry, height,              SLE_UINT8),
 
	    SLE_REF(Industry, town,                REF_TOWN),
 
	    SLE_ARR(Industry, produced_cargo,      SLE_UINT8,  2),
 
	    SLE_ARR(Industry, cargo_waiting,       SLE_UINT16, 2),
 
	    SLE_ARR(Industry, production_rate,     SLE_UINT8,  2),
 
	    SLE_ARR(Industry, accepts_cargo,       SLE_UINT8,  3),
 
	    SLE_VAR(Industry, prod_level,          SLE_UINT8),
 
	    SLE_ARR(Industry, last_mo_production,  SLE_UINT16, 2),
 
	    SLE_ARR(Industry, last_mo_transported, SLE_UINT16, 2),
 
	    SLE_ARR(Industry, pct_transported,     SLE_UINT8,  2),
 
	    SLE_ARR(Industry, total_production,    SLE_UINT16, 2),
 
	    SLE_ARR(Industry, total_transported,   SLE_UINT16, 2),
 

	
 
	    SLE_VAR(Industry, counter,             SLE_UINT16),
 

	
 
	    SLE_VAR(Industry, type,                SLE_UINT8),
 
	    SLE_VAR(Industry, owner,               SLE_UINT8),
 
	    SLE_VAR(Industry, random_color,        SLE_UINT8),
 
	SLE_CONDVAR(Industry, last_prod_year,      SLE_FILE_U8 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Industry, last_prod_year,      SLE_INT32,                 31, SL_MAX_VERSION),
 
	    SLE_VAR(Industry, was_cargo_delivered, SLE_UINT8),
 

	
 
	// reserve extra space in savegame here. (currently 32 bytes)
 
	SLE_CONDNULL(32, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static void Save_INDY(void)
 
{
 
	Industry *ind;
 

	
 
	// Write the vehicles
 
	FOR_ALL_INDUSTRIES(ind) {
 
		SlSetArrayIndex(ind->index);
 
		SlObject(ind, _industry_desc);
 
	}
 
}
 

	
 
static void Load_INDY(void)
 
{
 
	int index;
 

	
 
	_total_industries = 0;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		Industry *i;
 

	
 
		if (!AddBlockIfNeeded(&_Industry_pool, index))
 
			error("Industries: failed loading savegame: too many industries");
 

	
 
		i = GetIndustry(index);
 
		SlObject(i, _industry_desc);
 

	
 
		_total_industries++;
 
	}
 
}
 

	
 
const ChunkHandler _industry_chunk_handlers[] = {
 
	{ 'INDY', Save_INDY, Load_INDY, CH_ARRAY | CH_LAST},
 
};
src/industry_gui.c
Show inline comments
 
deleted file
src/industry_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "strings.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "map.h"
 
#include "gui.h"
 
#include "window.h"
 
#include "gfx.h"
 
#include "command.h"
 
#include "viewport.h"
 
#include "industry.h"
 
#include "town.h"
 
#include "variables.h"
 

	
 
const byte _build_industry_types[4][12] = {
 
	{  1,  2,  4,  6,  8,  0,  3,  5,  9, 11, 18 },
 
	{  1, 14,  4, 13,  7,  0,  3,  9, 11, 15 },
 
	{ 25, 13,  4, 23, 22, 11, 17, 10, 24, 19, 20, 21 },
 
	{ 27, 30, 31, 33, 26, 28, 29, 32, 34, 35, 36 },
 
};
 

	
 
static void UpdateIndustryProduction(Industry *i);
 

	
 
static void BuildIndustryWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 
		if (_thd.place_mode == 1 && _thd.window_class == WC_BUILD_INDUSTRY) {
 
			int ind_type = _build_industry_types[_opt_ptr->landscape][WP(w,def_d).data_1];
 

	
 
			SetDParam(0, (_price.build_industry >> 5) * GetIndustrySpec(ind_type)->cost_multiplier);
 
			DrawStringCentered(85, w->height - 21, STR_482F_COST, 0);
 
		}
 
		break;
 

	
 
	case WE_CLICK: {
 
		int wid = e->we.click.widget;
 
		if (wid >= 3) {
 
			if (HandlePlacePushButton(w, wid, SPR_CURSOR_INDUSTRY, 1, NULL))
 
				WP(w,def_d).data_1 = wid - 3;
 
		}
 
	} break;
 

	
 
	case WE_PLACE_OBJ:
 
		if (DoCommandP(e->we.place.tile, _build_industry_types[_opt_ptr->landscape][WP(w,def_d).data_1], 0, NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY)))
 
			ResetObjectToPlace();
 
		break;
 

	
 
	case WE_ABORT_PLACE_OBJ:
 
		RaiseWindowButtons(w);
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_industry_land0_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   115, 0x0,                            STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_0241_POWER_STATION,         STR_0263_CONSTRUCT_POWER_STATION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    29,    40, STR_0242_SAWMILL,               STR_0264_CONSTRUCT_SAWMILL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_0244_OIL_REFINERY,          STR_0266_CONSTRUCT_OIL_REFINERY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_0246_FACTORY,               STR_0268_CONSTRUCT_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    68,    79, STR_0247_STEEL_MILL,            STR_0269_CONSTRUCT_STEEL_MILL},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _build_industry_land1_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   115, 0x0,                            STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_0241_POWER_STATION,         STR_0263_CONSTRUCT_POWER_STATION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    29,    40, STR_024C_PAPER_MILL,            STR_026E_CONSTRUCT_PAPER_MILL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_0244_OIL_REFINERY,          STR_0266_CONSTRUCT_OIL_REFINERY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_024D_FOOD_PROCESSING_PLANT, STR_026F_CONSTRUCT_FOOD_PROCESSING},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    68,    79, STR_024E_PRINTING_WORKS,        STR_0270_CONSTRUCT_PRINTING_WORKS},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _build_industry_land2_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   115, 0x0,                            STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_0250_LUMBER_MILL,           STR_0273_CONSTRUCT_LUMBER_MILL_TO},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    29,    40, STR_024D_FOOD_PROCESSING_PLANT, STR_026F_CONSTRUCT_FOOD_PROCESSING},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_0244_OIL_REFINERY,          STR_0266_CONSTRUCT_OIL_REFINERY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_0246_FACTORY,               STR_0268_CONSTRUCT_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    68,    79, STR_0254_WATER_TOWER,           STR_0277_CONSTRUCT_WATER_TOWER_CAN},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _build_industry_land3_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   115, 0x0,                            STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_0258_CANDY_FACTORY,         STR_027B_CONSTRUCT_CANDY_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    29,    40, STR_025B_TOY_SHOP,              STR_027E_CONSTRUCT_TOY_SHOP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_025C_TOY_FACTORY,           STR_027F_CONSTRUCT_TOY_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_025E_FIZZY_DRINK_FACTORY,   STR_0281_CONSTRUCT_FIZZY_DRINK_FACTORY},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _build_industry_land0_widgets_extra[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   187, 0x0,                            STR_NULL},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_0241_POWER_STATION,         STR_0263_CONSTRUCT_POWER_STATION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    29,    40, STR_0242_SAWMILL,               STR_0264_CONSTRUCT_SAWMILL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_0244_OIL_REFINERY,          STR_0266_CONSTRUCT_OIL_REFINERY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_0246_FACTORY,               STR_0268_CONSTRUCT_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    68,    79, STR_0247_STEEL_MILL,            STR_0269_CONSTRUCT_STEEL_MILL},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    84,    95, STR_0240_COAL_MINE,             STR_CONSTRUCT_COAL_MINE_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    97,   108, STR_0243_FOREST,                STR_CONSTRUCT_FOREST_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   110,   121, STR_0245_OIL_RIG,               STR_CONSTRUCT_OIL_RIG_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   123,   134, STR_0248_FARM,                  STR_CONSTRUCT_FARM_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   136,   147, STR_024A_OIL_WELLS,             STR_CONSTRUCT_OIL_WELLS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   149,   160, STR_0249_IRON_ORE_MINE,         STR_CONSTRUCT_IRON_ORE_MINE_TIP},
 

	
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _build_industry_land1_widgets_extra[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   174, 0x0,                            STR_NULL},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_0241_POWER_STATION,         STR_0263_CONSTRUCT_POWER_STATION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    29,    40, STR_024C_PAPER_MILL,            STR_026E_CONSTRUCT_PAPER_MILL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_0244_OIL_REFINERY,          STR_0266_CONSTRUCT_OIL_REFINERY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_024D_FOOD_PROCESSING_PLANT, STR_026F_CONSTRUCT_FOOD_PROCESSING},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    68,    79, STR_024E_PRINTING_WORKS,        STR_0270_CONSTRUCT_PRINTING_WORKS},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    84,    95, STR_0240_COAL_MINE,             STR_CONSTRUCT_COAL_MINE_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    97,   108, STR_0243_FOREST,                STR_CONSTRUCT_FOREST_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   110,   121, STR_0248_FARM,                  STR_CONSTRUCT_FARM_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   123,   134, STR_024A_OIL_WELLS,             STR_CONSTRUCT_OIL_WELLS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   136,   147, STR_024F_GOLD_MINE,             STR_CONSTRUCT_GOLD_MINE_TIP},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _build_industry_land2_widgets_extra[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   200, 0x0,                            STR_NULL},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_0250_LUMBER_MILL,           STR_0273_CONSTRUCT_LUMBER_MILL_TO},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    29,    40, STR_024D_FOOD_PROCESSING_PLANT, STR_026F_CONSTRUCT_FOOD_PROCESSING},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_0244_OIL_REFINERY,          STR_0266_CONSTRUCT_OIL_REFINERY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_0246_FACTORY,               STR_0268_CONSTRUCT_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    68,    79, STR_0254_WATER_TOWER,           STR_0277_CONSTRUCT_WATER_TOWER_CAN},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    84,    95, STR_024A_OIL_WELLS,             STR_CONSTRUCT_OIL_WELLS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    97,   108, STR_0255_DIAMOND_MINE,          STR_CONSTRUCT_DIAMOND_MINE_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   110,   121, STR_0256_COPPER_ORE_MINE,       STR_CONSTRUCT_COPPER_ORE_MINE_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   123,   134, STR_0248_FARM,                  STR_CONSTRUCT_FARM_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   136,   147, STR_0251_FRUIT_PLANTATION,      STR_CONSTRUCT_FRUIT_PLANTATION_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   149,   160, STR_0252_RUBBER_PLANTATION,     STR_CONSTRUCT_RUBBER_PLANTATION_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   162,   173, STR_0253_WATER_SUPPLY,          STR_CONSTRUCT_WATER_SUPPLY_TIP},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _build_industry_land3_widgets_extra[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   187, 0x0,                            STR_NULL},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_0258_CANDY_FACTORY,         STR_027B_CONSTRUCT_CANDY_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    29,    40, STR_025B_TOY_SHOP,              STR_027E_CONSTRUCT_TOY_SHOP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_025C_TOY_FACTORY,           STR_027F_CONSTRUCT_TOY_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_025E_FIZZY_DRINK_FACTORY,   STR_0281_CONSTRUCT_FIZZY_DRINK_FACTORY},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    71,    82, STR_0257_COTTON_CANDY_FOREST,   STR_CONSTRUCT_COTTON_CANDY_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    84,    95, STR_0259_BATTERY_FARM,          STR_CONSTRUCT_BATTERY_FARM_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    97,   108, STR_025A_COLA_WELLS,            STR_CONSTRUCT_COLA_WELLS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   110,   121, STR_025D_PLASTIC_FOUNTAINS,     STR_CONSTRUCT_PLASTIC_FOUNTAINS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   123,   134, STR_025F_BUBBLE_GENERATOR,      STR_CONSTRUCT_BUBBLE_GENERATOR_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   136,   147, STR_0260_TOFFEE_QUARRY,         STR_CONSTRUCT_TOFFEE_QUARRY_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   149,   160, STR_0261_SUGAR_MINE,            STR_CONSTRUCT_SUGAR_MINE_TIP},
 
{   WIDGETS_END},
 
};
 

	
 

	
 
static const WindowDesc _build_industry_land0_desc = {
 
	WDP_AUTO, WDP_AUTO, 170, 116,
 
	WC_BUILD_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_industry_land0_widgets,
 
	BuildIndustryWndProc
 
};
 

	
 
static const WindowDesc _build_industry_land1_desc = {
 
	WDP_AUTO, WDP_AUTO, 170, 116,
 
	WC_BUILD_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_industry_land1_widgets,
 
	BuildIndustryWndProc
 
};
 

	
 
static const WindowDesc _build_industry_land2_desc = {
 
	WDP_AUTO, WDP_AUTO, 170, 116,
 
	WC_BUILD_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_industry_land2_widgets,
 
	BuildIndustryWndProc
 
};
 

	
 
static const WindowDesc _build_industry_land3_desc = {
 
	WDP_AUTO, WDP_AUTO, 170, 116,
 
	WC_BUILD_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_industry_land3_widgets,
 
	BuildIndustryWndProc
 
};
 

	
 
static const WindowDesc _build_industry_land0_desc_extra = {
 
	WDP_AUTO, WDP_AUTO, 170, 188,
 
	WC_BUILD_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_industry_land0_widgets_extra,
 
	BuildIndustryWndProc
 
};
 

	
 
static const WindowDesc _build_industry_land1_desc_extra = {
 
	WDP_AUTO, WDP_AUTO, 170, 175,
 
	WC_BUILD_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_industry_land1_widgets_extra,
 
	BuildIndustryWndProc
 
};
 

	
 
static const WindowDesc _build_industry_land2_desc_extra = {
 
	WDP_AUTO, WDP_AUTO, 170, 201,
 
	WC_BUILD_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_industry_land2_widgets_extra,
 
	BuildIndustryWndProc
 
};
 

	
 
static const WindowDesc _build_industry_land3_desc_extra = {
 
	WDP_AUTO, WDP_AUTO, 170, 188,
 
	WC_BUILD_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_industry_land3_widgets_extra,
 
	BuildIndustryWndProc
 
};
 

	
 
static const WindowDesc * const _industry_window_desc[2][4] = {
 
	{
 
	&_build_industry_land0_desc,
 
	&_build_industry_land1_desc,
 
	&_build_industry_land2_desc,
 
	&_build_industry_land3_desc,
 
	},
 
	{
 
	&_build_industry_land0_desc_extra,
 
	&_build_industry_land1_desc_extra,
 
	&_build_industry_land2_desc_extra,
 
	&_build_industry_land3_desc_extra,
 
	},
 
};
 

	
 
void ShowBuildIndustryWindow(void)
 
{
 
	if (!IsValidPlayer(_current_player)) return;
 
	AllocateWindowDescFront(_industry_window_desc[_patches.build_rawmaterial_ind][_opt_ptr->landscape],0);
 
}
 

	
 
static inline bool isProductionMinimum(const Industry *i, int pt) {
 
	return i->production_rate[pt] == 1;
 
}
 

	
 
static inline bool isProductionMaximum(const Industry *i, int pt) {
 
	return i->production_rate[pt] == 255;
 
}
 

	
 
static inline bool IsProductionAlterable(const Industry *i)
 
{
 
	return ((_game_mode == GM_EDITOR || _cheats.setup_prod.value) &&
 
		     (i->accepts_cargo[0] == CT_INVALID || i->accepts_cargo[0] == CT_VALUABLES));
 
}
 

	
 
static void IndustryViewWndProc(Window *w, WindowEvent *e)
 
{
 
	// WP(w,vp2_d).data_1 is for the editbox line
 
	// WP(w,vp2_d).data_2 is for the clickline
 
	// WP(w,vp2_d).data_3 is for the click pos (left or right)
 

	
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const Industry *i = GetIndustry(w->window_number);
 

	
 
		SetDParam(0, w->window_number);
 
		DrawWindowWidgets(w);
 

	
 
		if (i->accepts_cargo[0] != CT_INVALID) {
 
			StringID str;
 

	
 
			SetDParam(0, _cargoc.names_s[i->accepts_cargo[0]]);
 
			str = STR_4827_REQUIRES;
 
			if (i->accepts_cargo[1] != CT_INVALID) {
 
				SetDParam(1, _cargoc.names_s[i->accepts_cargo[1]]);
 
				str = STR_4828_REQUIRES;
 
				if (i->accepts_cargo[2] != CT_INVALID) {
 
					SetDParam(2, _cargoc.names_s[i->accepts_cargo[2]]);
 
					str = STR_4829_REQUIRES;
 
				}
 
			}
 
			DrawString(2, 107, str, 0);
 
		}
 

	
 
		if (i->produced_cargo[0] != CT_INVALID) {
 
			DrawString(2, 117, STR_482A_PRODUCTION_LAST_MONTH, 0);
 

	
 
			SetDParam(0, i->produced_cargo[0]);
 
			SetDParam(1, i->total_production[0]);
 

	
 
			SetDParam(2, i->pct_transported[0] * 100 >> 8);
 
			DrawString(4 + (IsProductionAlterable(i) ? 30 : 0), 127, STR_482B_TRANSPORTED, 0);
 
			// Let's put out those buttons..
 
			if (IsProductionAlterable(i)) {
 
				DrawArrowButtons(5, 127, 3, (WP(w,vp2_d).data_2 == 1) ? WP(w,vp2_d).data_3 : 0,
 
						!isProductionMinimum(i, 0), !isProductionMaximum(i, 0));
 
			}
 

	
 
			if (i->produced_cargo[1] != CT_INVALID) {
 
				SetDParam(0, i->produced_cargo[1]);
 
				SetDParam(1, i->total_production[1]);
 
				SetDParam(2, i->pct_transported[1] * 100 >> 8);
 
				DrawString(4 + (IsProductionAlterable(i) ? 30 : 0), 137, STR_482B_TRANSPORTED, 0);
 
				// Let's put out those buttons..
 
				if (IsProductionAlterable(i)) {
 
					DrawArrowButtons(5, 137, 3, (WP(w,vp2_d).data_2 == 2) ? WP(w,vp2_d).data_3 : 0,
 
						!isProductionMinimum(i, 1), !isProductionMaximum(i, 1));
 
				}
 
			}
 
		}
 

	
 
		DrawWindowViewport(w);
 
		}
 
		break;
 

	
 
	case WE_CLICK: {
 
		Industry *i;
 

	
 
		switch (e->we.click.widget) {
 
		case 5: {
 
			int line, x;
 

	
 
			i = GetIndustry(w->window_number);
 

	
 
			// We should work if needed..
 
			if (!IsProductionAlterable(i)) return;
 

	
 
			x = e->we.click.pt.x;
 
			line = (e->we.click.pt.y - 127) / 10;
 
			if (e->we.click.pt.y >= 127 && IS_INT_INSIDE(line, 0, 2) && i->produced_cargo[line] != CT_INVALID) {
 
				if (IS_INT_INSIDE(x, 5, 25) ) {
 
					/* Clicked buttons, decrease or increase production */
 
					if (x < 15) {
 
						if (isProductionMinimum(i, line)) return;
 
						i->production_rate[line] = maxu(i->production_rate[line] / 2, 1);
 
					} else {
 
						if (isProductionMaximum(i, line)) return;
 
						i->production_rate[line] = minu(i->production_rate[line] * 2, 255);
 
					}
 

	
 
					UpdateIndustryProduction(i);
 
					SetWindowDirty(w);
 
					w->flags4 |= 5 << WF_TIMEOUT_SHL;
 
					WP(w,vp2_d).data_2 = line+1;
 
					WP(w,vp2_d).data_3 = (x < 15 ? 1 : 2);
 
				} else if (IS_INT_INSIDE(x, 34, 160)) {
 
					// clicked the text
 
					WP(w,vp2_d).data_1 = line;
 
					SetDParam(0, i->production_rate[line] * 8);
 
					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_GAME_PRODUCTION, 10, 100, w, CS_ALPHANUMERAL);
 
				}
 
			}
 
		} break;
 
		case 6:
 
			i = GetIndustry(w->window_number);
 
			ScrollMainWindowToTile(i->xy + TileDiffXY(1, 1));
 
		}	break;
 

	
 
		}
 
		break;
 
	case WE_TIMEOUT:
 
		WP(w,vp2_d).data_2 = 0;
 
		WP(w,vp2_d).data_3 = 0;
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT:
 
		if (e->we.edittext.str[0] != '\0') {
 
			Industry* i = GetIndustry(w->window_number);
 
			int line = WP(w,vp2_d).data_1;
 

	
 
			i->production_rate[line] = clampu(atoi(e->we.edittext.str), 0, 255);
 
			UpdateIndustryProduction(i);
 
			SetWindowDirty(w);
 
		}
 
	}
 
}
 

	
 
static void UpdateIndustryProduction(Industry *i)
 
{
 
	if (i->produced_cargo[0] != CT_INVALID)
 
		i->total_production[0] = 8 * i->production_rate[0];
 

	
 
	if (i->produced_cargo[1] != CT_INVALID)
 
		i->total_production[1] = 8 * i->production_rate[1];
 
}
 

	
 
static const Widget _industry_view_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     9,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     9,    11,   247,     0,    13, STR_4801,          STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,     9,   248,   259,     0,    13, 0x0,               STR_STICKY_BUTTON},
 
{      WWT_PANEL,   RESIZE_NONE,     9,     0,   259,    14,   105, 0x0,               STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,     9,     2,   257,    16,   103, 0x0,               STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     9,     0,   259,   106,   147, 0x0,               STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,     9,     0,   129,   148,   159, STR_00E4_LOCATION, STR_482C_CENTER_THE_MAIN_VIEW_ON},
 
{      WWT_PANEL,   RESIZE_NONE,     9,   130,   259,   148,   159, 0x0,               STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _industry_view_desc = {
 
	WDP_AUTO, WDP_AUTO, 260, 160,
 
	WC_INDUSTRY_VIEW,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
 
	_industry_view_widgets,
 
	IndustryViewWndProc
 
};
 

	
 
void ShowIndustryViewWindow(int industry)
 
{
 
	Window *w = AllocateWindowDescFront(&_industry_view_desc, industry);
 

	
 
	if (w != NULL) {
 
		w->flags4 |= WF_DISABLE_VP_SCROLL;
 
		WP(w,vp2_d).data_1 = 0;
 
		WP(w,vp2_d).data_2 = 0;
 
		WP(w,vp2_d).data_3 = 0;
 
		AssignWindowViewport(w, 3, 17, 0xFE, 0x56, GetIndustry(w->window_number)->xy + TileDiffXY(1, 1), 1);
 
	}
 
}
 

	
 
static const Widget _industry_directory_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    13,    11,   495,     0,    13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    13,   496,   507,     0,    13, 0x0,                     STR_STICKY_BUTTON},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,     0,   100,    14,    25, STR_SORT_BY_NAME,        STR_SORT_ORDER_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   101,   200,    14,    25, STR_SORT_BY_TYPE,        STR_SORT_ORDER_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   201,   300,    14,    25, STR_SORT_BY_PRODUCTION,  STR_SORT_ORDER_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   301,   400,    14,    25, STR_SORT_BY_TRANSPORTED, STR_SORT_ORDER_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    13,   401,   495,    14,    25, 0x0,                     STR_NULL},
 
{      WWT_PANEL, RESIZE_BOTTOM,    13,     0,   495,    26,   189, 0x0,                     STR_200A_TOWN_NAMES_CLICK_ON_NAME},
 
{  WWT_SCROLLBAR, RESIZE_BOTTOM,    13,   496,   507,    14,   177, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{  WWT_RESIZEBOX,     RESIZE_TB,    13,   496,   507,   178,   189, 0x0,                     STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static uint _num_industry_sort;
 

	
 
static char _bufcache[96];
 
static const Industry* _last_industry;
 

	
 
static byte _industry_sort_order;
 

	
 
static int CDECL GeneralIndustrySorter(const void *a, const void *b)
 
{
 
	const Industry* i = *(const Industry**)a;
 
	const Industry* j = *(const Industry**)b;
 
	int r;
 

	
 
	switch (_industry_sort_order >> 1) {
 
		default: NOT_REACHED();
 
		case 0: /* Sort by Name (handled later) */
 
			r = 0;
 
			break;
 

	
 
		case 1: /* Sort by Type */
 
			r = i->type - j->type;
 
			break;
 

	
 
		case 2: /* Sort by Production */
 
			if (i->produced_cargo[0] == CT_INVALID) {
 
				r = (j->produced_cargo[0] == CT_INVALID ? 0 : -1);
 
			} else {
 
				if (j->produced_cargo[0] == CT_INVALID) {
 
					r = 1;
 
				} else {
 
					r =
 
						(i->total_production[0] + i->total_production[1]) -
 
						(j->total_production[0] + j->total_production[1]);
 
				}
 
			}
 
			break;
 

	
 
		case 3: /* Sort by transported fraction */
 
			if (i->produced_cargo[0] == CT_INVALID) {
 
				r = (j->produced_cargo[0] == CT_INVALID ? 0 : -1);
 
			} else {
 
				if (j->produced_cargo[0] == CT_INVALID) {
 
					r = 1;
 
				} else {
 
					int pi;
 
					int pj;
 

	
 
					pi = i->pct_transported[0] * 100 >> 8;
 
					if (i->produced_cargo[1] != CT_INVALID) {
 
						int p = i->pct_transported[1] * 100 >> 8;
 
						if (p < pi) pi = p;
 
					}
 

	
 
					pj = j->pct_transported[0] * 100 >> 8;
 
					if (j->produced_cargo[1] != CT_INVALID) {
 
						int p = j->pct_transported[1] * 100 >> 8;
 
						if (p < pj) pj = p;
 
					}
 

	
 
					r = pi - pj;
 
				}
 
			}
 
			break;
 
	}
 

	
 
	// default to string sorting if they are otherwise equal
 
	if (r == 0) {
 
		char buf1[96];
 

	
 
		SetDParam(0, i->town->index);
 
		GetString(buf1, STR_TOWN, lastof(buf1));
 

	
 
		if (j != _last_industry) {
 
			_last_industry = j;
 
			SetDParam(0, j->town->index);
 
			GetString(_bufcache, STR_TOWN, lastof(_bufcache));
 
		}
 
		r = strcmp(buf1, _bufcache);
 
	}
 

	
 
	if (_industry_sort_order & 1) r = -r;
 
	return r;
 
}
 

	
 
static void MakeSortedIndustryList(void)
 
{
 
	const Industry* i;
 
	int n = 0;
 

	
 
	/* Don't attempt a sort if there are no industries */
 
	if (GetNumIndustries() == 0) return;
 

	
 
	/* Create array for sorting */
 
	_industry_sort = realloc((void *)_industry_sort, (GetMaxIndustryIndex() + 1) * sizeof(_industry_sort[0]));
 
	if (_industry_sort == NULL) error("Could not allocate memory for the industry-sorting-list");
 

	
 
	FOR_ALL_INDUSTRIES(i) _industry_sort[n++] = i;
 

	
 
	_num_industry_sort = n;
 
	_last_industry = NULL; // used for "cache"
 

	
 
	qsort((void*)_industry_sort, n, sizeof(_industry_sort[0]), GeneralIndustrySorter);
 

	
 
	DEBUG(misc, 3, "Resorting industries list");
 
}
 

	
 

	
 
static void IndustryDirectoryWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		int n;
 
		uint p;
 
		static const uint16 _indicator_positions[4] = {88, 187, 284, 387};
 

	
 
		if (_industry_sort_dirty) {
 
			_industry_sort_dirty = false;
 
			MakeSortedIndustryList();
 
		}
 

	
 
		SetVScrollCount(w, _num_industry_sort);
 

	
 
		DrawWindowWidgets(w);
 
		DoDrawString(_industry_sort_order & 1 ? DOWNARROW : UPARROW, _indicator_positions[_industry_sort_order>>1], 15, 0x10);
 

	
 
		p = w->vscroll.pos;
 
		n = 0;
 

	
 
		while (p < _num_industry_sort) {
 
			const Industry* i = _industry_sort[p];
 

	
 
			SetDParam(0, i->index);
 
			if (i->produced_cargo[0] != CT_INVALID) {
 
				SetDParam(1, i->produced_cargo[0]);
 
				SetDParam(2, i->total_production[0]);
 

	
 
				if (i->produced_cargo[1] != CT_INVALID) {
 
					SetDParam(3, i->produced_cargo[1]);
 
					SetDParam(4, i->total_production[1]);
 
					SetDParam(5, i->pct_transported[0] * 100 >> 8);
 
					SetDParam(6, i->pct_transported[1] * 100 >> 8);
 
					DrawString(4, 28+n*10, STR_INDUSTRYDIR_ITEM_TWO, 0);
 
				} else {
 
					SetDParam(3, i->pct_transported[0] * 100 >> 8);
 
					DrawString(4, 28+n*10, STR_INDUSTRYDIR_ITEM, 0);
 
				}
 
			} else {
 
				DrawString(4, 28+n*10, STR_INDUSTRYDIR_ITEM_NOPROD, 0);
 
			}
 
			p++;
 
			if (++n == w->vscroll.cap) break;
 
		}
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 3: {
 
			_industry_sort_order = _industry_sort_order==0 ? 1 : 0;
 
			_industry_sort_dirty = true;
 
			SetWindowDirty(w);
 
		} break;
 

	
 
		case 4: {
 
			_industry_sort_order = _industry_sort_order==2 ? 3 : 2;
 
			_industry_sort_dirty = true;
 
			SetWindowDirty(w);
 
		} break;
 

	
 
		case 5: {
 
			_industry_sort_order = _industry_sort_order==4 ? 5 : 4;
 
			_industry_sort_dirty = true;
 
			SetWindowDirty(w);
 
		} break;
 

	
 
		case 6: {
 
			_industry_sort_order = _industry_sort_order==6 ? 7 : 6;
 
			_industry_sort_dirty = true;
 
			SetWindowDirty(w);
 
		} break;
 

	
 
		case 8: {
 
			int y = (e->we.click.pt.y - 28) / 10;
 
			uint16 p;
 

	
 
			if (!IS_INT_INSIDE(y, 0, w->vscroll.cap)) return;
 
			p = y + w->vscroll.pos;
 
			if (p < _num_industry_sort) {
 
				ScrollMainWindowToTile(_industry_sort[p]->xy);
 
			}
 
		} break;
 
		}
 
		break;
 

	
 
	case WE_4:
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_RESIZE:
 
		w->vscroll.cap += e->we.sizing.diff.y / 10;
 
		break;
 
	}
 
}
 

	
 

	
 
/* Industry List */
 
static const WindowDesc _industry_directory_desc = {
 
	WDP_AUTO, WDP_AUTO, 508, 190,
 
	WC_INDUSTRY_DIRECTORY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_industry_directory_widgets,
 
	IndustryDirectoryWndProc
 
};
 

	
 

	
 
void ShowIndustryDirectory(void)
 
{
 
	Window *w = AllocateWindowDescFront(&_industry_directory_desc, 0);
 

	
 
	if (w != NULL) {
 
		w->vscroll.cap = 16;
 
		w->resize.height = w->height - 6 * 10; // minimum 10 items
 
		w->resize.step_height = 10;
 
		SetWindowDirty(w);
 
	}
 
}
src/intro_gui.c
Show inline comments
 
deleted file
src/intro_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "functions.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "player.h"
 
#include "network/network.h"
 
#include "variables.h"
 
#include "settings.h"
 
#include "heightmap.h"
 
#include "genworld.h"
 
#include "network/network_gui.h"
 
#include "newgrf.h"
 

	
 
static const Widget _select_game_widgets[] = {
 
{    WWT_CAPTION, RESIZE_NONE, 13,   0, 335,   0,  13, STR_0307_OPENTTD,         STR_NULL},
 
{      WWT_PANEL, RESIZE_NONE, 13,   0, 335,  14, 194, 0x0,                      STR_NULL},
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12,  10, 167,  22,  33, STR_0140_NEW_GAME,        STR_02FB_START_A_NEW_GAME},
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325,  22,  33, STR_0141_LOAD_GAME,       STR_02FC_LOAD_A_SAVED_GAME},
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12,  10, 167,  40,  51, STR_029A_PLAY_SCENARIO,   STR_0303_START_A_NEW_GAME_USING},
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325,  40,  51, STR_PLAY_HEIGHTMAP,       STR_PLAY_HEIGHTMAP_HINT},
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12,  10, 167,  58,  69, STR_0220_CREATE_SCENARIO, STR_02FE_CREATE_A_CUSTOMIZED_GAME},
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325,  58,  69, STR_MULTIPLAYER,          STR_0300_SELECT_MULTIPLAYER_GAME},
 

	
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12,  10,  86,  77, 131, SPR_SELECT_TEMPERATE,     STR_030E_SELECT_TEMPERATE_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12,  90, 166,  77, 131, SPR_SELECT_SUB_ARCTIC,    STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12, 170, 246,  77, 131, SPR_SELECT_SUB_TROPICAL,  STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
 
{   WWT_IMGBTN_2, RESIZE_NONE, 12, 250, 326,  77, 131, SPR_SELECT_TOYLAND,       STR_0311_SELECT_TOYLAND_LANDSCAPE},
 

	
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12,  10, 167, 139, 150, STR_0148_GAME_OPTIONS,    STR_0301_DISPLAY_GAME_OPTIONS},
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 139, 150, STR_01FE_DIFFICULTY,      STR_0302_DISPLAY_DIFFICULTY_OPTIONS},
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12,  10, 167, 157, 168, STR_CONFIG_PATCHES,       STR_CONFIG_PATCHES_TIP},
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 157, 168, STR_NEWGRF_SETTINGS_BUTTON, STR_NULL},
 

	
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 104, 231, 175, 186, STR_0304_QUIT,            STR_0305_QUIT_OPENTTD},
 
{   WIDGETS_END},
 
};
 

	
 
static inline void SetNewLandscapeType(byte landscape)
 
{
 
	_opt_newgame.landscape = landscape;
 
	InvalidateWindowClasses(WC_SELECT_GAME);
 
}
 

	
 
static void SelectGameWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: LowerWindowWidget(w, _opt_newgame.landscape + 8); break;
 

	
 
	case WE_PAINT:
 
		SetWindowWidgetLoweredState(w, 8,  _opt_newgame.landscape == LT_NORMAL);
 
		SetWindowWidgetLoweredState(w, 9,  _opt_newgame.landscape == LT_HILLY);
 
		SetWindowWidgetLoweredState(w, 10, _opt_newgame.landscape == LT_DESERT);
 
		SetWindowWidgetLoweredState(w, 11, _opt_newgame.landscape == LT_CANDY);
 
		SetDParam(0, STR_6801_EASY + _opt_newgame.diff_level);
 
		DrawWindowWidgets(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 2: ShowGenerateLandscape(); break;
 
		case 3: ShowSaveLoadDialog(SLD_LOAD_GAME); break;
 
		case 4: ShowSaveLoadDialog(SLD_LOAD_SCENARIO); break;
 
		case 5: ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP); break;
 
		case 6: ShowCreateScenario(); break;
 
		case 7:
 
			if (!_network_available) {
 
				ShowErrorMessage(INVALID_STRING_ID, STR_NETWORK_ERR_NOTAVAILABLE, 0, 0);
 
			} else {
 
				ShowNetworkGameWindow();
 
			}
 
			break;
 
		case 8: case 9: case 10: case 11:
 
			RaiseWindowWidget(w, _opt_newgame.landscape + 8);
 
			SetNewLandscapeType(e->we.click.widget - 8);
 
			break;
 
		case 12: ShowGameOptions(); break;
 
		case 13: ShowGameDifficulty(); break;
 
		case 14: ShowPatchesSelection(); break;
 
		case 15: ShowNewGRFSettings(true, true, false, &_grfconfig_newgame); break;
 
		case 16: HandleExitGameRequest(); break;
 
		}
 
		break;
 
	}
 
}
 

	
 
static const WindowDesc _select_game_desc = {
 
	WDP_CENTER, WDP_CENTER, 336, 195,
 
	WC_SELECT_GAME,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_select_game_widgets,
 
	SelectGameWndProc
 
};
 

	
 
void ShowSelectGameWindow(void)
 
{
 
	AllocateWindowDesc(&_select_game_desc);
 
}
 

	
 
static void AskExitGameCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) _exit_game = true;
 
}
 

	
 
void AskExitGame(void)
 
{
 
#if defined(_WIN32)
 
		SetDParam(0, STR_0133_WINDOWS);
 
#elif defined(__APPLE__)
 
		SetDParam(0, STR_0135_OSX);
 
#elif defined(__BEOS__)
 
		SetDParam(0, STR_OSNAME_BEOS);
 
#elif defined(__MORPHOS__)
 
		SetDParam(0, STR_OSNAME_MORPHOS);
 
#elif defined(__AMIGA__)
 
		SetDParam(0, STR_OSNAME_AMIGAOS);
 
#elif defined(__OS2__)
 
		SetDParam(0, STR_OSNAME_OS2);
 
#else
 
		SetDParam(0, STR_0134_UNIX);
 
#endif
 
	ShowQuery(
 
		STR_00C7_QUIT,
 
		STR_00CA_ARE_YOU_SURE_YOU_WANT_TO,
 
		NULL,
 
		AskExitGameCallback
 
	);
 
}
 

	
 

	
 
static void AskExitToGameMenuCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) _switch_mode = SM_MENU;
 
}
 

	
 
void AskExitToGameMenu(void)
 
{
 
	ShowQuery(
 
		STR_0161_QUIT_GAME,
 
		(_game_mode != GM_EDITOR) ? STR_ABANDON_GAME_QUERY : STR_QUIT_SCENARIO_QUERY,
 
		NULL,
 
		AskExitToGameMenuCallback
 
	);
 
}
src/landscape.c
Show inline comments
 
deleted file
src/landscape.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "heightmap.h"
 
#include "clear_map.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "player.h"
 
#include "spritecache.h"
 
#include "table/sprites.h"
 
#include "tile.h"
 
#include <stdarg.h>
 
#include "viewport.h"
 
#include "command.h"
 
#include "vehicle.h"
 
#include "variables.h"
 
#include "void_map.h"
 
#include "water_map.h"
 
#include "tgp.h"
 
#include "genworld.h"
 
#include "heightmap.h"
 

	
 
extern const TileTypeProcs
 
	_tile_type_clear_procs,
 
	_tile_type_rail_procs,
 
	_tile_type_road_procs,
 
	_tile_type_town_procs,
 
	_tile_type_trees_procs,
 
	_tile_type_station_procs,
 
	_tile_type_water_procs,
 
	_tile_type_dummy_procs,
 
	_tile_type_industry_procs,
 
	_tile_type_tunnelbridge_procs,
 
	_tile_type_unmovable_procs;
 

	
 
const TileTypeProcs * const _tile_type_procs[16] = {
 
	&_tile_type_clear_procs,
 
	&_tile_type_rail_procs,
 
	&_tile_type_road_procs,
 
	&_tile_type_town_procs,
 
	&_tile_type_trees_procs,
 
	&_tile_type_station_procs,
 
	&_tile_type_water_procs,
 
	&_tile_type_dummy_procs,
 
	&_tile_type_industry_procs,
 
	&_tile_type_tunnelbridge_procs,
 
	&_tile_type_unmovable_procs,
 
};
 

	
 
/* landscape slope => sprite */
 
const byte _tileh_to_sprite[32] = {
 
	0, 1, 2, 3, 4, 5, 6,  7, 8, 9, 10, 11, 12, 13, 14, 0,
 
	0, 0, 0, 0, 0, 0, 0, 16, 0, 0,  0, 17,  0, 15, 18, 0,
 
};
 

	
 
const byte _inclined_tileh[] = {
 
	SLOPE_SW,  SLOPE_NW,  SLOPE_SW,  SLOPE_SE, SLOPE_NE, SLOPE_SE, SLOPE_NE, SLOPE_NW,
 
	SLOPE_E,   SLOPE_N,   SLOPE_W,   SLOPE_S,
 
	SLOPE_NWS, SLOPE_WSE, SLOPE_SEN, SLOPE_ENW
 
};
 

	
 

	
 
uint GetPartialZ(int x, int y, Slope corners)
 
{
 
	int z = 0;
 

	
 
	switch (corners) {
 
	case SLOPE_W:
 
		if (x - y >= 0)
 
			z = (x - y) >> 1;
 
		break;
 

	
 
	case SLOPE_S:
 
		y^=0xF;
 
		if ( (x - y) >= 0)
 
			z = (x - y) >> 1;
 
		break;
 

	
 
	case SLOPE_SW:
 
		z = (x>>1) + 1;
 
		break;
 

	
 
	case SLOPE_E:
 
		if (y - x >= 0)
 
			z = (y - x) >> 1;
 
		break;
 

	
 
	case SLOPE_EW:
 
	case SLOPE_NS:
 
	case SLOPE_ELEVATED:
 
		z = 4;
 
		break;
 

	
 
	case SLOPE_SE:
 
		z = (y>>1) + 1;
 
		break;
 

	
 
	case SLOPE_WSE:
 
		z = 8;
 
		y^=0xF;
 
		if (x - y < 0)
 
			z += (x - y) >> 1;
 
		break;
 

	
 
	case SLOPE_N:
 
		y ^= 0xF;
 
		if (y - x >= 0)
 
			z = (y - x) >> 1;
 
		break;
 

	
 
	case SLOPE_NW:
 
		z = (y^0xF)>>1;
 
		break;
 

	
 
	case SLOPE_NWS:
 
		z = 8;
 
		if (x - y < 0)
 
			z += (x - y) >> 1;
 
		break;
 

	
 
	case SLOPE_NE:
 
		z = (x^0xF)>>1;
 
		break;
 

	
 
	case SLOPE_ENW:
 
		z = 8;
 
		y ^= 0xF;
 
		if (y - x < 0)
 
			z += (y - x) >> 1;
 
		break;
 

	
 
	case SLOPE_SEN:
 
		z = 8;
 
		if (y - x < 0)
 
			z += (y - x) >> 1;
 
		break;
 

	
 
	case SLOPE_STEEP_S:
 
		z = 1 + ((x+y)>>1);
 
		break;
 

	
 
	case SLOPE_STEEP_W:
 
		z = 1 + ((x+(y^0xF))>>1);
 
		break;
 

	
 
	case SLOPE_STEEP_N:
 
		z = 1 + (((x^0xF)+(y^0xF))>>1);
 
		break;
 

	
 
	case SLOPE_STEEP_E:
 
		z = 1 + (((x^0xF)+(y^0xF))>>1);
 
		break;
 

	
 
		default: break;
 
	}
 

	
 
	return z;
 
}
 

	
 
uint GetSlopeZ(int x, int y)
 
{
 
	TileIndex tile = TileVirtXY(x, y);
 

	
 
	return _tile_type_procs[GetTileType(tile)]->get_slope_z_proc(tile, x, y);
 
}
 

	
 

	
 
static Slope GetFoundationSlope(TileIndex tile, uint* z)
 
{
 
	Slope tileh = GetTileSlope(tile, z);
 
	Slope slope = _tile_type_procs[GetTileType(tile)]->get_slope_tileh_proc(tile, tileh);
 

	
 
	// Flatter slope -> higher base height
 
	if (slope < tileh) *z += TILE_HEIGHT;
 
	return slope;
 
}
 

	
 

	
 
static bool HasFoundationNW(TileIndex tile, Slope slope_here, uint z_here)
 
{
 
	uint z;
 
	Slope slope = GetFoundationSlope(TILE_ADDXY(tile, 0, -1), &z);
 

	
 
	return
 
		(
 
			z_here + (slope_here & SLOPE_N ? TILE_HEIGHT : 0) + (slope_here == SLOPE_STEEP_N ? TILE_HEIGHT : 0) >
 
			z      + (slope      & SLOPE_E ? TILE_HEIGHT : 0) + (slope      == SLOPE_STEEP_E ? TILE_HEIGHT : 0)
 
		) || (
 
			z_here + (slope_here & SLOPE_W ? TILE_HEIGHT : 0) + (slope_here == SLOPE_STEEP_W ? TILE_HEIGHT : 0) >
 
			z      + (slope      & SLOPE_S ? TILE_HEIGHT : 0) + (slope      == SLOPE_STEEP_S ? TILE_HEIGHT : 0)
 
		);
 
}
 

	
 

	
 
static bool HasFoundationNE(TileIndex tile, Slope slope_here, uint z_here)
 
{
 
	uint z;
 
	Slope slope = GetFoundationSlope(TILE_ADDXY(tile, -1, 0), &z);
 

	
 
	return
 
		(
 
			z_here + (slope_here & SLOPE_N ? TILE_HEIGHT : 0) + (slope_here == SLOPE_STEEP_N ? TILE_HEIGHT : 0) >
 
			z      + (slope      & SLOPE_W ? TILE_HEIGHT : 0) + (slope      == SLOPE_STEEP_W ? TILE_HEIGHT : 0)
 
		) || (
 
			z_here + (slope_here & SLOPE_E ? TILE_HEIGHT : 0) + (slope_here == SLOPE_STEEP_E ? TILE_HEIGHT : 0) >
 
			z      + (slope      & SLOPE_S ? TILE_HEIGHT : 0) + (slope      == SLOPE_STEEP_S ? TILE_HEIGHT : 0)
 
		);
 
}
 

	
 

	
 
void DrawFoundation(TileInfo *ti, uint f)
 
{
 
	uint32 sprite_base = SPR_SLOPES_BASE - 15;
 
	Slope slope;
 
	uint z;
 

	
 
	slope = GetFoundationSlope(ti->tile, &z);
 
	if (!HasFoundationNW(ti->tile, slope, z)) sprite_base += 22;
 
	if (!HasFoundationNE(ti->tile, slope, z)) sprite_base += 44;
 

	
 
	if (IsSteepSlope(ti->tileh)) {
 
		uint32 lower_base;
 

	
 
		// Lower part of foundation
 
		lower_base = sprite_base;
 
		if (lower_base == SPR_SLOPES_BASE - 15) lower_base = SPR_FOUNDATION_BASE;
 
		AddSortableSpriteToDraw(
 
			lower_base + (ti->tileh & ~SLOPE_STEEP), ti->x, ti->y, 16, 16, 7, ti->z
 
		);
 
		ti->z += TILE_HEIGHT;
 
		ti->tileh = _inclined_tileh[f - 15];
 
		if (f < 15 + 8) {
 
			// inclined
 
			AddSortableSpriteToDraw(sprite_base + f, ti->x, ti->y, 16, 16, 1, ti->z);
 
			OffsetGroundSprite(31, 9);
 
		} else if (f >= 15 + 8 + 4) {
 
			// three corners raised
 
			uint32 upper = sprite_base + 15 + (f - 15 - 8 - 4) * 2;
 

	
 
			AddSortableSpriteToDraw(upper, ti->x, ti->y, 16, 16, 1, ti->z);
 
			AddChildSpriteScreen(upper + 1, 31, 9);
 
			OffsetGroundSprite(31, 9);
 
		} else {
 
			// one corner raised
 
			OffsetGroundSprite(31, 1);
 
		}
 
	} else {
 
		if (f < 15) {
 
			// leveled foundation
 
			// Use the original slope sprites if NW and NE borders should be visible
 
			if (sprite_base  == SPR_SLOPES_BASE - 15) sprite_base = SPR_FOUNDATION_BASE;
 

	
 
			AddSortableSpriteToDraw(sprite_base + f, ti->x, ti->y, 16, 16, 7, ti->z);
 
			ti->z += TILE_HEIGHT;
 
			ti->tileh = SLOPE_FLAT;
 
			OffsetGroundSprite(31, 1);
 
		} else {
 
			// inclined foundation
 
			AddSortableSpriteToDraw(sprite_base + f, ti->x, ti->y, 16, 16, 1, ti->z);
 
			ti->tileh = _inclined_tileh[f - 15];
 
			OffsetGroundSprite(31, 9);
 
		}
 
	}
 
}
 

	
 
void DoClearSquare(TileIndex tile)
 
{
 
	MakeClear(tile, CLEAR_GRASS, _generating_world ? 3 : 0);
 
	MarkTileDirtyByTile(tile);
 
}
 

	
 
uint32 GetTileTrackStatus(TileIndex tile, TransportType mode)
 
{
 
	return _tile_type_procs[GetTileType(tile)]->get_tile_track_status_proc(tile, mode);
 
}
 

	
 
void ChangeTileOwner(TileIndex tile, byte old_player, byte new_player)
 
{
 
	_tile_type_procs[GetTileType(tile)]->change_tile_owner_proc(tile, old_player, new_player);
 
}
 

	
 
void GetAcceptedCargo(TileIndex tile, AcceptedCargo ac)
 
{
 
	memset(ac, 0, sizeof(AcceptedCargo));
 
	_tile_type_procs[GetTileType(tile)]->get_accepted_cargo_proc(tile, ac);
 
}
 

	
 
void AnimateTile(TileIndex tile)
 
{
 
	_tile_type_procs[GetTileType(tile)]->animate_tile_proc(tile);
 
}
 

	
 
void ClickTile(TileIndex tile)
 
{
 
	_tile_type_procs[GetTileType(tile)]->click_tile_proc(tile);
 
}
 

	
 
void GetTileDesc(TileIndex tile, TileDesc *td)
 
{
 
	_tile_type_procs[GetTileType(tile)]->get_tile_desc_proc(tile, td);
 
}
 

	
 
/** Clear a piece of landscape
 
 * @param tile tile to clear
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdLandscapeClear(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	return _tile_type_procs[GetTileType(tile)]->clear_tile_proc(tile, flags);
 
}
 

	
 
/** Clear a big piece of landscape
 
 * @param tile end tile of area dragging
 
 * @param p1 start tile of area dragging
 
 * @param p2 unused
 
 */
 
int32 CmdClearArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 cost, ret, money;
 
	int ex;
 
	int ey;
 
	int sx,sy;
 
	int x,y;
 
	bool success = false;
 

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

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	// make sure sx,sy are smaller than ex,ey
 
	ex = TileX(tile);
 
	ey = TileY(tile);
 
	sx = TileX(p1);
 
	sy = TileY(p1);
 
	if (ex < sx) intswap(ex, sx);
 
	if (ey < sy) intswap(ey, sy);
 

	
 
	money = GetAvailableMoneyForCommand();
 
	cost = 0;
 

	
 
	for (x = sx; x <= ex; ++x) {
 
		for (y = sy; y <= ey; ++y) {
 
			ret = DoCommand(TileXY(x, y), 0, 0, flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
			if (CmdFailed(ret)) continue;
 
			cost += ret;
 
			success = true;
 

	
 
			if (flags & DC_EXEC) {
 
				if (ret > 0 && (money -= ret) < 0) {
 
					_additional_cash_required = ret;
 
					return cost - ret;
 
				}
 
				DoCommand(TileXY(x, y), 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 

	
 
				// draw explosion animation...
 
				if ((x == sx || x == ex) && (y == sy || y == ey)) {
 
					// big explosion in each corner, or small explosion for single tiles
 
					CreateEffectVehicleAbove(x * TILE_SIZE + TILE_SIZE / 2, y * TILE_SIZE + TILE_SIZE / 2, 2,
 
						sy == ey && sx == ex ? EV_EXPLOSION_SMALL : EV_EXPLOSION_LARGE
 
					);
 
				}
 
			}
 
		}
 
	}
 

	
 
	return (success) ? cost : CMD_ERROR;
 
}
 

	
 

	
 
#define TILELOOP_BITS 4
 
#define TILELOOP_SIZE (1 << TILELOOP_BITS)
 
#define TILELOOP_ASSERTMASK ((TILELOOP_SIZE-1) + ((TILELOOP_SIZE-1) << MapLogX()))
 
#define TILELOOP_CHKMASK (((1 << (MapLogX() - TILELOOP_BITS))-1) << TILELOOP_BITS)
 

	
 
void RunTileLoop(void)
 
{
 
	TileIndex tile;
 
	uint count;
 

	
 
	tile = _cur_tileloop_tile;
 

	
 
	assert( (tile & ~TILELOOP_ASSERTMASK) == 0);
 
	count = (MapSizeX() / TILELOOP_SIZE) * (MapSizeY() / TILELOOP_SIZE);
 
	do {
 
		_tile_type_procs[GetTileType(tile)]->tile_loop_proc(tile);
 

	
 
		if (TileX(tile) < MapSizeX() - TILELOOP_SIZE) {
 
			tile += TILELOOP_SIZE; /* no overflow */
 
		} else {
 
			tile = TILE_MASK(tile - TILELOOP_SIZE * (MapSizeX() / TILELOOP_SIZE - 1) + TileDiffXY(0, TILELOOP_SIZE)); /* x would overflow, also increase y */
 
		}
 
	} while (--count);
 
	assert( (tile & ~TILELOOP_ASSERTMASK) == 0);
 

	
 
	tile += 9;
 
	if (tile & TILELOOP_CHKMASK)
 
		tile = (tile + MapSizeX()) & TILELOOP_ASSERTMASK;
 
	_cur_tileloop_tile = tile;
 
}
 

	
 
void InitializeLandscape(void)
 
{
 
	uint maxx = MapMaxX();
 
	uint maxy = MapMaxY();
 
	uint sizex = MapSizeX();
 
	uint x;
 
	uint y;
 

	
 
	for (y = 0; y < maxy; y++) {
 
		for (x = 0; x < maxx; x++) {
 
			MakeClear(sizex * y + x, CLEAR_GRASS, 3);
 
			SetTileHeight(sizex * y + x, 0);
 
			_m[sizex * y + x].extra = 0;
 
			ClearBridgeMiddle(sizex * y + x);
 
		}
 
		MakeVoid(sizex * y + x);
 
	}
 
	for (x = 0; x < sizex; x++) MakeVoid(sizex * y + x);
 
}
 

	
 
void ConvertGroundTilesIntoWaterTiles(void)
 
{
 
	TileIndex tile;
 
	uint z;
 
	Slope slope;
 

	
 
	for (tile = 0; tile < MapSize(); ++tile) {
 
		slope = GetTileSlope(tile, &z);
 
		if (IsTileType(tile, MP_CLEAR) && z == 0) {
 
			/* Make both water for tiles at level 0
 
			 * and make shore, as that looks much better
 
			 * during the generation. */
 
			switch (slope) {
 
				case SLOPE_FLAT:
 
					MakeWater(tile);
 
					break;
 

	
 
				case SLOPE_N:
 
				case SLOPE_E:
 
				case SLOPE_S:
 
				case SLOPE_W:
 
				case SLOPE_NW:
 
				case SLOPE_SW:
 
				case SLOPE_SE:
 
				case SLOPE_NE:
 
					MakeShore(tile);
 
					break;
 

	
 
				default:
 
					break;
 
			}
 
		}
 
	}
 
}
 

	
 
static const byte _genterrain_tbl_1[5] = { 10, 22, 33, 37, 4  };
 
static const byte _genterrain_tbl_2[5] = {  0,  0,  0,  0, 33 };
 

	
 
static void GenerateTerrain(int type, int flag)
 
{
 
	uint32 r;
 
	uint x;
 
	uint y;
 
	uint w;
 
	uint h;
 
	const Sprite* template;
 
	const byte *p;
 
	Tile* tile;
 
	byte direction;
 

	
 
	r = Random();
 
	template = GetSprite((((r >> 24) * _genterrain_tbl_1[type]) >> 8) + _genterrain_tbl_2[type] + 4845);
 

	
 
	x = r & MapMaxX();
 
	y = (r >> MapLogX()) & MapMaxY();
 

	
 

	
 
	if (x < 2 || y < 2) return;
 

	
 
	direction = GB(r, 22, 2);
 
	if (direction & 1) {
 
		w = template->height;
 
		h = template->width;
 
	} else {
 
		w = template->width;
 
		h = template->height;
 
	}
 
	p = template->data;
 

	
 
	if (flag & 4) {
 
		uint xw = x * MapSizeY();
 
		uint yw = y * MapSizeX();
 
		uint bias = (MapSizeX() + MapSizeY()) * 16;
 

	
 
		switch (flag & 3) {
 
			case 0:
 
				if (xw + yw > MapSize() - bias) return;
 
				break;
 

	
 
			case 1:
 
				if (yw < xw + bias) return;
 
				break;
 

	
 
			case 2:
 
				if (xw + yw < MapSize() + bias) return;
 
				break;
 

	
 
			case 3:
 
				if (xw < yw + bias) return;
 
				break;
 
		}
 
	}
 

	
 
	if (x + w >= MapMaxX() - 1) return;
 
	if (y + h >= MapMaxY() - 1) return;
 

	
 
	tile = &_m[TileXY(x, y)];
 

	
 
	switch (direction) {
 
		case 0:
 
			do {
 
				Tile* tile_cur = tile;
 
				uint w_cur;
 

	
 
				for (w_cur = w; w_cur != 0; --w_cur) {
 
					if (*p >= tile_cur->type_height) tile_cur->type_height = *p;
 
					p++;
 
					tile_cur++;
 
				}
 
				tile += TileDiffXY(0, 1);
 
			} while (--h != 0);
 
			break;
 

	
 
		case 1:
 
			do {
 
				Tile* tile_cur = tile;
 
				uint h_cur;
 

	
 
				for (h_cur = h; h_cur != 0; --h_cur) {
 
					if (*p >= tile_cur->type_height) tile_cur->type_height = *p;
 
					p++;
 
					tile_cur += TileDiffXY(0, 1);
 
				}
 
				tile++;
 
			} while (--w != 0);
 
			break;
 

	
 
		case 2:
 
			tile += TileDiffXY(w - 1, 0);
 
			do {
 
				Tile* tile_cur = tile;
 
				uint w_cur;
 

	
 
				for (w_cur = w; w_cur != 0; --w_cur) {
 
					if (*p >= tile_cur->type_height) tile_cur->type_height = *p;
 
					p++;
 
					tile_cur--;
 
				}
 
				tile += TileDiffXY(0, 1);
 
			} while (--h != 0);
 
			break;
 

	
 
		case 3:
 
			tile += TileDiffXY(0, h - 1);
 
			do {
 
				Tile* tile_cur = tile;
 
				uint h_cur;
 

	
 
				for (h_cur = h; h_cur != 0; --h_cur) {
 
					if (*p >= tile_cur->type_height) tile_cur->type_height = *p;
 
					p++;
 
					tile_cur -= TileDiffXY(0, 1);
 
				}
 
				tile++;
 
			} while (--w != 0);
 
			break;
 
	}
 
}
 

	
 

	
 
#include "table/genland.h"
 

	
 
static void CreateDesertOrRainForest(void)
 
{
 
	TileIndex tile;
 
	TileIndex update_freq = MapSize() / 4;
 
	const TileIndexDiffC *data;
 
	uint i;
 

	
 
	for (tile = 0; tile != MapSize(); ++tile) {
 
		if ((tile % update_freq) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 

	
 
		for (data = _make_desert_or_rainforest_data;
 
				data != endof(_make_desert_or_rainforest_data); ++data) {
 
			TileIndex t = TILE_MASK(tile + ToTileIndexDiff(*data));
 
			if (TileHeight(t) >= 4 || IsTileType(t, MP_WATER)) break;
 
		}
 
		if (data == endof(_make_desert_or_rainforest_data))
 
			SetTropicZone(tile, TROPICZONE_DESERT);
 
	}
 

	
 
	for (i = 0; i != 256; i++) {
 
		if ((i % 64) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 

	
 
		RunTileLoop();
 
	}
 

	
 
	for (tile = 0; tile != MapSize(); ++tile) {
 
		if ((tile % update_freq) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 

	
 
		for (data = _make_desert_or_rainforest_data;
 
				data != endof(_make_desert_or_rainforest_data); ++data) {
 
			TileIndex t = TILE_MASK(tile + ToTileIndexDiff(*data));
 
			if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_DESERT)) break;
 
		}
 
		if (data == endof(_make_desert_or_rainforest_data))
 
			SetTropicZone(tile, TROPICZONE_RAINFOREST);
 
	}
 
}
 

	
 
void GenerateLandscape(byte mode)
 
{
 
	const int gwp_desert_amount = 4 + 8;
 
	uint i;
 
	uint flag;
 
	uint32 r;
 

	
 
	if (mode == GW_HEIGHTMAP) {
 
		SetGeneratingWorldProgress(GWP_LANDSCAPE, (_opt.landscape == LT_DESERT) ? 1 + gwp_desert_amount : 1);
 
		LoadHeightmap(_file_to_saveload.name);
 
		IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 
	} else if (_patches.land_generator == LG_TERRAGENESIS) {
 
		SetGeneratingWorldProgress(GWP_LANDSCAPE, (_opt.landscape == LT_DESERT) ? 3 + gwp_desert_amount : 3);
 
		GenerateTerrainPerlin();
 
	} else {
 
		switch (_opt.landscape) {
 
			case LT_HILLY:
 
				SetGeneratingWorldProgress(GWP_LANDSCAPE, 2);
 

	
 
				for (i = ScaleByMapSize((Random() & 0x7F) + 950); i != 0; --i) {
 
					GenerateTerrain(2, 0);
 
				}
 
				IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 

	
 
				r = Random();
 
				flag = GB(r, 0, 2) | 4;
 
				for (i = ScaleByMapSize(GB(r, 16, 7) + 450); i != 0; --i) {
 
					GenerateTerrain(4, flag);
 
				}
 
				IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 
				break;
 

	
 
			case LT_DESERT:
 
				SetGeneratingWorldProgress(GWP_LANDSCAPE, 3 + gwp_desert_amount);
 

	
 
				for (i = ScaleByMapSize((Random() & 0x7F) + 170); i != 0; --i) {
 
					GenerateTerrain(0, 0);
 
				}
 
				IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 

	
 
				r = Random();
 
				flag = GB(r, 0, 2) | 4;
 
				for (i = ScaleByMapSize(GB(r, 16, 8) + 1700); i != 0; --i) {
 
					GenerateTerrain(0, flag);
 
				}
 
				IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 

	
 
				flag ^= 2;
 

	
 
				for (i = ScaleByMapSize((Random() & 0x7F) + 410); i != 0; --i) {
 
					GenerateTerrain(3, flag);
 
				}
 
				IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 
				break;
 

	
 
			default:
 
				SetGeneratingWorldProgress(GWP_LANDSCAPE, 1);
 

	
 
				i = ScaleByMapSize((Random() & 0x7F) + (3 - _opt.diff.quantity_sea_lakes) * 256 + 100);
 
				for (; i != 0; --i) {
 
					GenerateTerrain(_opt.diff.terrain_type, 0);
 
				}
 
				IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 
				break;
 
		}
 
	}
 

	
 
	ConvertGroundTilesIntoWaterTiles();
 

	
 
	if (_opt.landscape == LT_DESERT) CreateDesertOrRainForest();
 
}
 

	
 
void OnTick_Town(void);
 
void OnTick_Trees(void);
 
void OnTick_Station(void);
 
void OnTick_Industry(void);
 

	
 
void OnTick_Players(void);
 
void OnTick_Train(void);
 

	
 
void CallLandscapeTick(void)
 
{
 
	OnTick_Town();
 
	OnTick_Trees();
 
	OnTick_Station();
 
	OnTick_Industry();
 

	
 
	OnTick_Players();
 
	OnTick_Train();
 
}
 

	
 
TileIndex AdjustTileCoordRandomly(TileIndex a, byte rng)
 
{
 
	int rn = rng;
 
	uint32 r = Random();
 

	
 
	return TILE_MASK(TileXY(
 
		TileX(a) + (GB(r, 0, 8) * rn * 2 >> 8) - rn,
 
		TileY(a) + (GB(r, 8, 8) * rn * 2 >> 8) - rn
 
	));
 
}
 

	
 
bool IsValidTile(TileIndex tile)
 
{
 
	return (tile < MapSizeX() * MapMaxY() && TileX(tile) != MapMaxX());
 
}
src/main_gui.c
Show inline comments
 
deleted file
src/main_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "heightmap.h"
 
#include "currency.h"
 
#include "functions.h"
 
#include "spritecache.h"
 
#include "station.h"
 
#include "strings.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "player.h"
 
#include "command.h"
 
#include "news.h"
 
#include "town.h"
 
#include "vehicle.h"
 
#include "console.h"
 
#include "sound.h"
 
#include "network/network.h"
 
#include "signs.h"
 
#include "waypoint.h"
 
#include "variables.h"
 
#include "train.h"
 
#include "unmovable_map.h"
 
#include "string.h"
 
#include "screenshot.h"
 
#include "genworld.h"
 
#include "settings.h"
 
#include "date.h"
 
#include "vehicle_gui.h"
 
#include "newgrf_config.h"
 

	
 
#include "network/network_data.h"
 
#include "network/network_client.h"
 
#include "network/network_server.h"
 
#include "network/network_gui.h"
 
#include "industry.h"
 

	
 
static int _rename_id = 1;
 
static int _rename_what = -1;
 

	
 
static byte _terraform_size = 1;
 
RailType _last_built_railtype;
 
static int _scengen_town_size = 2; // depress medium-sized towns per default
 

	
 
extern void GenerateIndustries(void);
 
extern bool GenerateTowns(void);
 

	
 

	
 
void HandleOnEditText(const char *str)
 
{
 
	int id = _rename_id;
 
	_cmd_text = str;
 

	
 
	switch (_rename_what) {
 
	case 0: /* Rename a s sign, if string is empty, delete sign */
 
		DoCommandP(0, id, 0, NULL, CMD_RENAME_SIGN | CMD_MSG(STR_280C_CAN_T_CHANGE_SIGN_NAME));
 
		break;
 
	case 1: /* Rename a waypoint */
 
		if (*str == '\0') return;
 
		DoCommandP(0, id, 0, NULL, CMD_RENAME_WAYPOINT | CMD_MSG(STR_CANT_CHANGE_WAYPOINT_NAME));
 
		break;
 
#ifdef ENABLE_NETWORK
 
	case 3: { /* Give money, you can only give money in excess of loan */
 
		const Player *p = GetPlayer(_current_player);
 
		int32 money = min(p->money64 - p->current_loan, atoi(str) / _currency->rate);
 
		char msg[20];
 

	
 
		money = clamp(money, 0, 20000000); // Clamp between 20 million and 0
 

	
 
		// Give 'id' the money, and substract it from ourself
 
		if (!DoCommandP(0, money, id, NULL, CMD_GIVE_MONEY | CMD_MSG(STR_INSUFFICIENT_FUNDS))) break;
 

	
 
		// Inform the player of this action
 
		snprintf(msg, sizeof(msg), "%d", money);
 

	
 
		if (!_network_server) {
 
			SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_GIVE_MONEY, DESTTYPE_TEAM, id, msg);
 
		} else {
 
			NetworkServer_HandleChat(NETWORK_ACTION_GIVE_MONEY, DESTTYPE_TEAM, id, msg, NETWORK_SERVER_INDEX);
 
		}
 
	}	break;
 
#endif /* ENABLE_NETWORK */
 
		default: NOT_REACHED();
 
	}
 

	
 
	_rename_id = _rename_what = -1;
 
}
 

	
 
/**
 
 * This code is shared for the majority of the pushbuttons.
 
 * Handles e.g. the pressing of a button (to build things), playing of click sound and sets certain parameters
 
 *
 
 * @param w Window which called the function
 
 * @param widget ID of the widget (=button) that called this function
 
 * @param cursor How should the cursor image change? E.g. cursor with depot image in it
 
 * @param mode Tile highlighting mode, e.g. drawing a rectangle or a dot on the ground
 
 * @param placeproc Procedure which will be called when someone clicks on the map
 

	
 
 * @return true if the button is clicked, false if it's unclicked
 
 */
 
bool HandlePlacePushButton(Window *w, int widget, CursorID cursor, int mode, PlaceProc *placeproc)
 
{
 
	if (IsWindowWidgetDisabled(w, widget)) return false;
 

	
 
	SndPlayFx(SND_15_BEEP);
 
	SetWindowDirty(w);
 

	
 
	if (IsWindowWidgetLowered(w, widget)) {
 
		ResetObjectToPlace();
 
		return false;
 
	}
 

	
 
	SetObjectToPlace(cursor, mode, w->window_class, w->window_number);
 
	LowerWindowWidget(w, widget);
 
	_place_proc = placeproc;
 
	return true;
 
}
 

	
 

	
 
void CcPlaySound10(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) SndPlayTileFx(SND_12_EXPLOSION, tile);
 
}
 

	
 

	
 
static void ToolbarPauseClick(Window *w)
 
{
 
	if (_networking && !_network_server) return; // only server can pause the game
 

	
 
	if (DoCommandP(0, _pause ? 0 : 1, 0, NULL, CMD_PAUSE)) SndPlayFx(SND_15_BEEP);
 
}
 

	
 
static void ToolbarFastForwardClick(Window *w)
 
{
 
	_fast_forward ^= true;
 
	SndPlayFx(SND_15_BEEP);
 
}
 

	
 

	
 
static void MenuClickSettings(int index)
 
{
 
	switch (index) {
 
		case 0: ShowGameOptions();      return;
 
		case 1: ShowGameDifficulty();   return;
 
		case 2: ShowPatchesSelection(); return;
 
		case 3: ShowNewGRFSettings(!_networking, true, true, &_grfconfig);   return;
 

	
 
		case  5: _display_opt ^= DO_SHOW_TOWN_NAMES;    break;
 
		case  6: _display_opt ^= DO_SHOW_STATION_NAMES; break;
 
		case  7: _display_opt ^= DO_SHOW_SIGNS;         break;
 
		case  8: _display_opt ^= DO_WAYPOINTS;          break;
 
		case  9: _display_opt ^= DO_FULL_ANIMATION;     break;
 
		case 10: _display_opt ^= DO_FULL_DETAIL;        break;
 
		case 11: _display_opt ^= DO_TRANS_BUILDINGS;    break;
 
		case 12: _display_opt ^= DO_TRANS_SIGNS;        break;
 
	}
 
	MarkWholeScreenDirty();
 
}
 

	
 
static void MenuClickSaveLoad(int index)
 
{
 
	if (_game_mode == GM_EDITOR) {
 
		switch (index) {
 
			case 0: ShowSaveLoadDialog(SLD_SAVE_SCENARIO); break;
 
			case 1: ShowSaveLoadDialog(SLD_LOAD_SCENARIO); break;
 
			case 2: AskExitToGameMenu();                   break;
 
			case 4: HandleExitGameRequest();               break;
 
		}
 
	} else {
 
		switch (index) {
 
			case 0: ShowSaveLoadDialog(SLD_SAVE_GAME); break;
 
			case 1: ShowSaveLoadDialog(SLD_LOAD_GAME); break;
 
			case 2: AskExitToGameMenu();               break;
 
			case 3: HandleExitGameRequest();           break;
 
		}
 
	}
 
}
 

	
 
static void MenuClickMap(int index)
 
{
 
	switch (index) {
 
		case 0: ShowSmallMap();            break;
 
		case 1: ShowExtraViewPortWindow(); break;
 
		case 2: ShowSignList();            break;
 
	}
 
}
 

	
 
static void MenuClickTown(int index)
 
{
 
	ShowTownDirectory();
 
}
 

	
 
static void MenuClickScenMap(int index)
 
{
 
	switch (index) {
 
		case 0: ShowSmallMap();            break;
 
		case 1: ShowExtraViewPortWindow(); break;
 
		case 2: ShowSignList();            break;
 
		case 3: ShowTownDirectory();       break;
 
	}
 
}
 

	
 
static void MenuClickSubsidies(int index)
 
{
 
	ShowSubsidiesList();
 
}
 

	
 
static void MenuClickStations(int index)
 
{
 
	ShowPlayerStations(index);
 
}
 

	
 
static void MenuClickFinances(int index)
 
{
 
	ShowPlayerFinances(index);
 
}
 

	
 
static void MenuClickCompany(int index)
 
{
 
	if (_networking && index == 0) {
 
		ShowClientList();
 
	} else {
 
		if (_networking) index--;
 
		ShowPlayerCompany(index);
 
	}
 
}
 

	
 
static void MenuClickGraphs(int index)
 
{
 
	switch (index) {
 
		case 0: ShowOperatingProfitGraph();    break;
 
		case 1: ShowIncomeGraph();             break;
 
		case 2: ShowDeliveredCargoGraph();     break;
 
		case 3: ShowPerformanceHistoryGraph(); break;
 
		case 4: ShowCompanyValueGraph();       break;
 
		case 5: ShowCargoPaymentRates();       break;
 
	}
 
}
 

	
 
static void MenuClickLeague(int index)
 
{
 
	switch (index) {
 
		case 0: ShowCompanyLeagueTable();      break;
 
		case 1: ShowPerformanceRatingDetail(); break;
 
	}
 
}
 

	
 
static void MenuClickIndustry(int index)
 
{
 
	switch (index) {
 
		case 0: ShowIndustryDirectory();   break;
 
		case 1: ShowBuildIndustryWindow(); break;
 
	}
 
}
 

	
 
static void MenuClickShowTrains(int index)
 
{
 
	ShowVehicleListWindow(index, INVALID_STATION, VEH_Train);
 
}
 

	
 
static void MenuClickShowRoad(int index)
 
{
 
	ShowVehicleListWindow(index, INVALID_STATION, VEH_Road);
 
}
 

	
 
static void MenuClickShowShips(int index)
 
{
 
	ShowVehicleListWindow(index, INVALID_STATION, VEH_Ship);
 
}
 

	
 
static void MenuClickShowAir(int index)
 
{
 
	ShowVehicleListWindow(index, INVALID_STATION, VEH_Aircraft);
 
}
 

	
 
static void MenuClickBuildRail(int index)
 
{
 
	_last_built_railtype = index;
 
	ShowBuildRailToolbar(_last_built_railtype, -1);
 
}
 

	
 
static void MenuClickBuildRoad(int index)
 
{
 
	ShowBuildRoadToolbar();
 
}
 

	
 
static void MenuClickBuildWater(int index)
 
{
 
	ShowBuildDocksToolbar();
 
}
 

	
 
static void MenuClickBuildAir(int index)
 
{
 
	ShowBuildAirToolbar();
 
}
 

	
 
#ifdef ENABLE_NETWORK
 
void ShowNetworkGiveMoneyWindow(PlayerID player)
 
{
 
	_rename_id = player;
 
	_rename_what = 3;
 
	ShowQueryString(STR_EMPTY, STR_NETWORK_GIVE_MONEY_CAPTION, 30, 180, NULL, CS_NUMERAL);
 
}
 
#endif /* ENABLE_NETWORK */
 

	
 
void ShowRenameSignWindow(const Sign *si)
 
{
 
	_rename_id = si->index;
 
	_rename_what = 0;
 
	ShowQueryString(si->str, STR_280B_EDIT_SIGN_TEXT, 30, 180, NULL, CS_ALPHANUMERAL);
 
}
 

	
 
void ShowRenameWaypointWindow(const Waypoint *wp)
 
{
 
	int id = wp->index;
 

	
 
	/* Are we allowed to change the name of the waypoint? */
 
	if (!CheckTileOwnership(wp->xy)) {
 
		ShowErrorMessage(_error_message, STR_CANT_CHANGE_WAYPOINT_NAME,
 
			TileX(wp->xy) * TILE_SIZE, TileY(wp->xy) * TILE_SIZE);
 
		return;
 
	}
 

	
 
	_rename_id = id;
 
	_rename_what = 1;
 
	SetDParam(0, id);
 
	ShowQueryString(STR_WAYPOINT_RAW, STR_EDIT_WAYPOINT_NAME, 30, 180, NULL, CS_ALPHANUMERAL);
 
}
 

	
 
static void SelectSignTool(void)
 
{
 
	if (_cursor.sprite == SPR_CURSOR_SIGN) {
 
		ResetObjectToPlace();
 
	} else {
 
		SetObjectToPlace(SPR_CURSOR_SIGN, 1, 1, 0);
 
		_place_proc = PlaceProc_Sign;
 
	}
 
}
 

	
 
static void MenuClickForest(int index)
 
{
 
	switch (index) {
 
		case 0: ShowTerraformToolbar();  break;
 
		case 1: ShowBuildTreesToolbar(); break;
 
		case 2: SelectSignTool();        break;
 
	}
 
}
 

	
 
static void MenuClickMusicWindow(int index)
 
{
 
	ShowMusicWindow();
 
}
 

	
 
static void MenuClickNewspaper(int index)
 
{
 
	switch (index) {
 
		case 0: ShowLastNewsMessage(); break;
 
		case 1: ShowMessageOptions();  break;
 
		case 2: ShowMessageHistory();  break;
 
	}
 
}
 

	
 
static void MenuClickSmallScreenshot(void)
 
{
 
	SetScreenshotType(SC_VIEWPORT);
 
}
 

	
 
static void MenuClickWorldScreenshot(void)
 
{
 
	SetScreenshotType(SC_WORLD);
 
}
 

	
 
static void MenuClickHelp(int index)
 
{
 
	switch (index) {
 
		case 0: PlaceLandBlockInfo();       break;
 
		case 2: IConsoleSwitch();           break;
 
		case 3: MenuClickSmallScreenshot(); break;
 
		case 4: MenuClickWorldScreenshot(); break;
 
		case 5: ShowAboutWindow();          break;
 
	}
 
}
 

	
 

	
 
typedef void MenuClickedProc(int index);
 

	
 
static MenuClickedProc * const _menu_clicked_procs[] = {
 
	NULL,                 /* 0 */
 
	NULL,                 /* 1 */
 
	MenuClickSettings,    /* 2 */
 
	MenuClickSaveLoad,    /* 3 */
 
	MenuClickMap,         /* 4 */
 
	MenuClickTown,        /* 5 */
 
	MenuClickSubsidies,   /* 6 */
 
	MenuClickStations,    /* 7 */
 
	MenuClickFinances,    /* 8 */
 
	MenuClickCompany,     /* 9 */
 
	MenuClickGraphs,      /* 10 */
 
	MenuClickLeague,      /* 11 */
 
	MenuClickIndustry,    /* 12 */
 
	MenuClickShowTrains,  /* 13 */
 
	MenuClickShowRoad,    /* 14 */
 
	MenuClickShowShips,   /* 15 */
 
	MenuClickShowAir,     /* 16 */
 
	MenuClickScenMap,     /* 17 */
 
	NULL,                 /* 18 */
 
	MenuClickBuildRail,   /* 19 */
 
	MenuClickBuildRoad,   /* 20 */
 
	MenuClickBuildWater,  /* 21 */
 
	MenuClickBuildAir,    /* 22 */
 
	MenuClickForest,      /* 23 */
 
	MenuClickMusicWindow, /* 24 */
 
	MenuClickNewspaper,   /* 25 */
 
	MenuClickHelp,        /* 26 */
 
};
 

	
 
static void MenuWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_CREATE: w->widget[0].right = w->width - 1; break;
 

	
 
	case WE_PAINT: {
 
		int x, y;
 

	
 
		byte count = WP(w, menu_d).item_count;
 
		byte sel = WP(w, menu_d).sel_index;
 
		uint16 chk = WP(w, menu_d).checked_items;
 
		StringID string = WP(w, menu_d).string_id;
 
		byte dis = WP(w, menu_d).disabled_items;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		x = 1;
 
		y = 1;
 

	
 
		for (; count != 0; count--, string++, sel--) {
 
			byte color = HASBIT(dis, 0) ? 14 : (sel == 0) ? 12 : 16;
 
			if (sel == 0) GfxFillRect(x, y, x + w->width - 3, y + 9, 0);
 

	
 
			if (HASBIT(chk, 0)) DrawString(x + 2, y, STR_CHECKMARK, color);
 
			DrawString(x + 2, y, string, color);
 

	
 
			y += 10;
 
			chk >>= 1;
 
			dis >>= 1;
 
		}
 
	} break;
 

	
 
	case WE_DESTROY: {
 
			Window *v = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
			RaiseWindowWidget(v, WP(w,menu_d).main_button);
 
			SetWindowDirty(v);
 
			return;
 
		}
 

	
 
	case WE_POPUPMENU_SELECT: {
 
		int index = GetMenuItemIndex(w, e->we.popupmenu.pt.x, e->we.popupmenu.pt.y);
 
		int action_id;
 

	
 

	
 
		if (index < 0) {
 
			Window *w2 = FindWindowById(WC_MAIN_TOOLBAR,0);
 
			if (GetWidgetFromPos(w2, e->we.popupmenu.pt.x - w2->left, e->we.popupmenu.pt.y - w2->top) == WP(w,menu_d).main_button)
 
				index = WP(w,menu_d).sel_index;
 
		}
 

	
 
		action_id = WP(w,menu_d).action_id;
 
		DeleteWindow(w);
 

	
 
		if (index >= 0) {
 
			assert((uint)index <= lengthof(_menu_clicked_procs));
 
			_menu_clicked_procs[action_id](index);
 
		}
 

	
 
		break;
 
		}
 

	
 
	case WE_POPUPMENU_OVER: {
 
		int index = GetMenuItemIndex(w, e->we.popupmenu.pt.x, e->we.popupmenu.pt.y);
 

	
 
		if (index == -1 || index == WP(w,menu_d).sel_index) return;
 

	
 
		WP(w,menu_d).sel_index = index;
 
		SetWindowDirty(w);
 
		return;
 
		}
 
	}
 
}
 

	
 
/* Dynamic widget length determined by toolbar-string length.
 
 * See PopupMainToolbMenu en MenuWndProc */
 
static const Widget _menu_widgets[] = {
 
{    WWT_PANEL, RESIZE_NONE, 14, 0,  0, 0, 0, 0x0, STR_NULL},
 
{ WIDGETS_END},
 
};
 

	
 

	
 
static const Widget _player_menu_widgets[] = {
 
{    WWT_PANEL, RESIZE_NONE, 14, 0, 240, 0, 81, 0x0, STR_NULL},
 
{ WIDGETS_END},
 
};
 

	
 

	
 
static int GetPlayerIndexFromMenu(int index)
 
{
 
	if (index >= 0) {
 
		const Player *p;
 

	
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active && --index < 0) return p->index;
 
		}
 
	}
 
	return -1;
 
}
 

	
 
static void UpdatePlayerMenuHeight(Window *w)
 
{
 
	byte num = ActivePlayerCount();
 

	
 
	// Increase one to fit in PlayerList in the menu when in network
 
	if (_networking && WP(w,menu_d).main_button == 9) num++;
 

	
 
	if (WP(w,menu_d).item_count != num) {
 
		WP(w,menu_d).item_count = num;
 
		SetWindowDirty(w);
 
		num = num * 10 + 2;
 
		w->height = num;
 
		w->widget[0].bottom = w->widget[0].top + num - 1;
 
		SetWindowDirty(w);
 
	}
 
}
 

	
 
extern void DrawPlayerIcon(PlayerID pid, int x, int y);
 

	
 
static void PlayerMenuWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		int x,y;
 
		byte sel, color;
 
		Player *p;
 
		uint16 chk;
 

	
 
		UpdatePlayerMenuHeight(w);
 
		DrawWindowWidgets(w);
 

	
 
		x = 1;
 
		y = 1;
 
		sel = WP(w,menu_d).sel_index;
 
		chk = WP(w,menu_d).checked_items; // let this mean gray items.
 

	
 
		// 9 = playerlist
 
		if (_networking && WP(w,menu_d).main_button == 9) {
 
			if (sel == 0) {
 
				GfxFillRect(x, y, x + 238, y + 9, 0);
 
			}
 
			DrawString(x + 19, y, STR_NETWORK_CLIENT_LIST, 0x0);
 
			y += 10;
 
			sel--;
 
		}
 

	
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active) {
 
				if (p->index == sel) {
 
					GfxFillRect(x, y, x + 238, y + 9, 0);
 
				}
 

	
 
				DrawPlayerIcon(p->index, x + 2, y + 1);
 

	
 
				SetDParam(0, p->name_1);
 
				SetDParam(1, p->name_2);
 
				SetDParam(2, GetPlayerNameString(p->index, 3));
 

	
 
				color = (p->index == sel) ? 0xC : 0x10;
 
				if (chk&1) color = 14;
 
				DrawString(x + 19, y, STR_7021, color);
 

	
 
				y += 10;
 
			}
 
			chk >>= 1;
 
		}
 

	
 
		break;
 
		}
 

	
 
	case WE_DESTROY: {
 
		Window *v = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
		RaiseWindowWidget(v, WP(w,menu_d).main_button);
 
		SetWindowDirty(v);
 
		return;
 
		}
 

	
 
	case WE_POPUPMENU_SELECT: {
 
		int index = GetMenuItemIndex(w, e->we.popupmenu.pt.x, e->we.popupmenu.pt.y);
 
		int action_id = WP(w,menu_d).action_id;
 

	
 
		// We have a new entry at the top of the list of menu 9 when networking
 
		//  so keep that in count
 
		if (_networking && WP(w,menu_d).main_button == 9) {
 
			if (index > 0) index = GetPlayerIndexFromMenu(index - 1) + 1;
 
		} else {
 
			index = GetPlayerIndexFromMenu(index);
 
		}
 

	
 
		if (index < 0) {
 
			Window *w2 = FindWindowById(WC_MAIN_TOOLBAR,0);
 
			if (GetWidgetFromPos(w2, e->we.popupmenu.pt.x - w2->left, e->we.popupmenu.pt.y - w2->top) == WP(w,menu_d).main_button)
 
				index = WP(w,menu_d).sel_index;
 
		}
 

	
 
		DeleteWindow(w);
 

	
 
		if (index >= 0) {
 
			assert(index >= 0 && index < 30);
 
			_menu_clicked_procs[action_id](index);
 
		}
 
		break;
 
		}
 
	case WE_POPUPMENU_OVER: {
 
		int index;
 
		UpdatePlayerMenuHeight(w);
 
		index = GetMenuItemIndex(w, e->we.popupmenu.pt.x, e->we.popupmenu.pt.y);
 

	
 
		// We have a new entry at the top of the list of menu 9 when networking
 
		//  so keep that in count
 
		if (_networking && WP(w,menu_d).main_button == 9) {
 
			if (index > 0) index = GetPlayerIndexFromMenu(index - 1) + 1;
 
		} else {
 
			index = GetPlayerIndexFromMenu(index);
 
		}
 

	
 
		if (index == -1 || index == WP(w,menu_d).sel_index) return;
 

	
 
		WP(w,menu_d).sel_index = index;
 
		SetWindowDirty(w);
 
		return;
 
		}
 
	}
 
}
 

	
 
/** Get the maximum length of a given string in a string-list. This is an
 
 * implicit string-list where the ID's are consecutive
 
 * @param base_string StringID of the first string in the list
 
 * @param count amount of StringID's in the list
 
 * @return the length of the longest string */
 
static int GetStringListMaxWidth(StringID base_string, byte count)
 
{
 
	char buffer[512];
 
	int width, max_width;
 
	byte i;
 

	
 
	max_width = 0;
 
	for (i = 0; i != count; i++) {
 
		GetString(buffer, base_string + i, lastof(buffer));
 
		width = GetStringBoundingBox(buffer).width;
 
		if (width > max_width) max_width = width;
 
	}
 

	
 
	return max_width;
 
}
 

	
 
/** Show a general dropdown menu. The positioning of the dropdown menu
 
 * defaults to the left side of the parent_button, eg the button that caused
 
 * this window to appear. The only exceptions are when the right side of this
 
 * dropdown would fall outside the main toolbar window, in that case it is
 
 * aligned with the toolbar's right side.
 
 * Since the disable-mask is only 8 bits right now, these dropdowns are
 
 * restricted to 8 items max if any bits of disabled_mask are active.
 
 * @param w Pointer to a window this dropdown menu belongs to. Has no effect
 
 * whatsoever, only graphically for positioning.
 
 * @param parent_button The widget identifier of the button that was clicked for
 
 * this dropdown. The created dropdown then knows what button to raise (button) on
 
 * action and whose function to execute (action).
 
 * It is possible to appoint another button for an action event by setting the
 
 * upper 8 bits of this parameter. If non is set, action is presumed to be the same
 
 * as button. So<br>
 
 * button bits 0 -  7 - widget clicked to get dropdown
 
 * action bits 8 - 15 - function of widget to execute on select (defaults to bits 0 - 7)
 
 * @param base_string The first StringID shown in the dropdown list. All others are
 
 * consecutive indeces from the language file. XXX - fix? Use ingame-string tables?
 
 * @param item_count Number of strings in the list, see previous parameter
 
 * @param disabled_mask Bitmask of disabled strings in the list
 
 * @return Return a pointer to the newly created dropdown window */
 
static Window *PopupMainToolbMenu(Window *w, uint16 parent_button, StringID base_string, byte item_count, byte disabled_mask)
 
{
 
	int width;
 
	int x = w->widget[GB(parent_button, 0, 8)].left;
 

	
 
	assert(disabled_mask == 0 || item_count <= 8);
 
	LowerWindowWidget(w, parent_button);
 
	InvalidateWidget(w, parent_button);
 

	
 
	DeleteWindowById(WC_TOOLBAR_MENU, 0);
 

	
 
	/* Extend the dropdown toolbar to the longest string in the list and
 
	 * also make sure the dropdown is fully visible within the window.
 
	 * x + w->left because x is supposed to be the offset of the toolbar-button
 
	 * we clicked on and w->left the toolbar window itself. So meaning that
 
	 * the default position is aligned with the left side of the clicked button */
 
	width = max(GetStringListMaxWidth(base_string, item_count) + 6, 140);
 
	x = w->left + clamp(x, 0, w->width - width); // or alternatively '_screen.width - width'
 

	
 
	w = AllocateWindow(x, 22, width, item_count * 10 + 2, MenuWndProc, WC_TOOLBAR_MENU, _menu_widgets);
 
	w->widget[0].bottom = item_count * 10 + 1;
 
	w->flags4 &= ~WF_WHITE_BORDER_MASK;
 

	
 
	WP(w,menu_d).item_count = item_count;
 
	WP(w,menu_d).sel_index = 0;
 
	WP(w,menu_d).main_button = GB(parent_button, 0, 8);
 
	WP(w,menu_d).action_id = (GB(parent_button, 8, 8) != 0) ? GB(parent_button, 8, 8) : parent_button;
 
	WP(w,menu_d).string_id = base_string;
 
	WP(w,menu_d).checked_items = 0;
 
	WP(w,menu_d).disabled_items = disabled_mask;
 

	
 
	_popup_menu_active = true;
 

	
 
	SndPlayFx(SND_15_BEEP);
 
	return w;
 
}
 

	
 
static Window *PopupMainPlayerToolbMenu(Window *w, int x, int main_button, int gray)
 
{
 
	x += w->left;
 

	
 
	LowerWindowWidget(w, main_button);
 
	InvalidateWidget(w, main_button);
 

	
 
	DeleteWindowById(WC_TOOLBAR_MENU, 0);
 
	w = AllocateWindow(x, 0x16, 0xF1, 0x52, PlayerMenuWndProc, WC_TOOLBAR_MENU, _player_menu_widgets);
 
	w->flags4 &= ~WF_WHITE_BORDER_MASK;
 
	WP(w,menu_d).item_count = 0;
 
	WP(w,menu_d).sel_index = (_local_player != PLAYER_SPECTATOR) ? _local_player : GetPlayerIndexFromMenu(0);
 
	if (_networking && main_button == 9) {
 
		if (_local_player != PLAYER_SPECTATOR) {
 
			WP(w,menu_d).sel_index++;
 
		} else {
 
			/* Select client list by default for spectators */
 
			WP(w,menu_d).sel_index = 0;
 
		}
 
	}
 
	WP(w,menu_d).action_id = main_button;
 
	WP(w,menu_d).main_button = main_button;
 
	WP(w,menu_d).checked_items = gray;
 
	WP(w,menu_d).disabled_items = 0;
 
	_popup_menu_active = true;
 
	SndPlayFx(SND_15_BEEP);
 
	return w;
 
}
 

	
 
static void ToolbarSaveClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 3, STR_015C_SAVE_GAME, 4, 0);
 
}
 

	
 
static void ToolbarMapClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 4, STR_02DE_MAP_OF_WORLD, 3, 0);
 
}
 

	
 
static void ToolbarTownClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 5, STR_02BB_TOWN_DIRECTORY, 1, 0);
 
}
 

	
 
static void ToolbarSubsidiesClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 6, STR_02DD_SUBSIDIES, 1, 0);
 
}
 

	
 
static void ToolbarStationsClick(Window *w)
 
{
 
	PopupMainPlayerToolbMenu(w, 162, 7, 0);
 
}
 

	
 
static void ToolbarMoneyClick(Window *w)
 
{
 
	PopupMainPlayerToolbMenu(w, 191, 8, 0);
 
}
 

	
 
static void ToolbarPlayersClick(Window *w)
 
{
 
	PopupMainPlayerToolbMenu(w, 213, 9, 0);
 
}
 

	
 
static void ToolbarGraphsClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 10, STR_0154_OPERATING_PROFIT_GRAPH, 6, 0);
 
}
 

	
 
static void ToolbarLeagueClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 11, STR_015A_COMPANY_LEAGUE_TABLE, 2, 0);
 
}
 

	
 
static void ToolbarIndustryClick(Window *w)
 
{
 
	/* Disable build-industry menu if we are a spectator */
 
	PopupMainToolbMenu(w, 12, STR_INDUSTRY_DIR, 2, (_current_player == PLAYER_SPECTATOR) ? (1 << 1) : 0);
 
}
 

	
 
static void ToolbarTrainClick(Window *w)
 
{
 
	const Vehicle *v;
 
	int dis = -1;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Train && IsFrontEngine(v)) CLRBIT(dis, v->owner);
 
	}
 
	PopupMainPlayerToolbMenu(w, 310, 13, dis);
 
}
 

	
 
static void ToolbarRoadClick(Window *w)
 
{
 
	const Vehicle *v;
 
	int dis = -1;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Road) CLRBIT(dis, v->owner);
 
	}
 
	PopupMainPlayerToolbMenu(w, 332, 14, dis);
 
}
 

	
 
static void ToolbarShipClick(Window *w)
 
{
 
	const Vehicle *v;
 
	int dis = -1;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Ship) CLRBIT(dis, v->owner);
 
	}
 
	PopupMainPlayerToolbMenu(w, 354, 15, dis);
 
}
 

	
 
static void ToolbarAirClick(Window *w)
 
{
 
	const Vehicle *v;
 
	int dis = -1;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Aircraft) CLRBIT(dis, v->owner);
 
	}
 
	PopupMainPlayerToolbMenu(w, 376, 16, dis);
 
}
 

	
 
/* Zooms a viewport in a window in or out */
 
/* No button handling or what so ever */
 
bool DoZoomInOutWindow(int how, Window *w)
 
{
 
	ViewPort *vp;
 

	
 
	assert(w != NULL);
 
	vp = w->viewport;
 

	
 
	switch (how) {
 
		case ZOOM_IN:
 
			if (vp->zoom == 0) return false;
 
			vp->zoom--;
 
			vp->virtual_width >>= 1;
 
			vp->virtual_height >>= 1;
 

	
 
			WP(w,vp_d).scrollpos_x += vp->virtual_width >> 1;
 
			WP(w,vp_d).scrollpos_y += vp->virtual_height >> 1;
 
			break;
 
		case ZOOM_OUT:
 
			if (vp->zoom == 2) return false;
 
			vp->zoom++;
 

	
 
			WP(w,vp_d).scrollpos_x -= vp->virtual_width >> 1;
 
			WP(w,vp_d).scrollpos_y -= vp->virtual_height >> 1;
 

	
 
			vp->virtual_width <<= 1;
 
			vp->virtual_height <<= 1;
 
			break;
 
	}
 
	if (vp != NULL) { // the vp can be null when how == ZOOM_NONE
 
		vp->virtual_left = WP(w, vp_d).scrollpos_x;
 
		vp->virtual_top = WP(w, vp_d).scrollpos_y;
 
	}
 
	SetWindowDirty(w);
 
	/* Update the windows that have zoom-buttons to perhaps disable their buttons */
 
	SendWindowMessageClass(w->window_class, how, w->window_number, 0);
 
	return true;
 
}
 

	
 
static void ToolbarZoomInClick(Window *w)
 
{
 
	if (DoZoomInOutWindow(ZOOM_IN, FindWindowById(WC_MAIN_WINDOW, 0))) {
 
		HandleButtonClick(w, 17);
 
		SndPlayFx(SND_15_BEEP);
 
	}
 
}
 

	
 
static void ToolbarZoomOutClick(Window *w)
 
{
 
	if (DoZoomInOutWindow(ZOOM_OUT,FindWindowById(WC_MAIN_WINDOW, 0))) {
 
		HandleButtonClick(w, 18);
 
		SndPlayFx(SND_15_BEEP);
 
	}
 
}
 

	
 
static void ToolbarBuildRailClick(Window *w)
 
{
 
	const Player *p = GetPlayer(_local_player);
 
	Window *w2;
 
	w2 = PopupMainToolbMenu(w, 19, STR_1015_RAILROAD_CONSTRUCTION, RAILTYPE_END, ~p->avail_railtypes);
 
	WP(w2,menu_d).sel_index = _last_built_railtype;
 
}
 

	
 
static void ToolbarBuildRoadClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 20, STR_180A_ROAD_CONSTRUCTION, 1, 0);
 
}
 

	
 
static void ToolbarBuildWaterClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 21, STR_9800_DOCK_CONSTRUCTION, 1, 0);
 
}
 

	
 
static void ToolbarBuildAirClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 22, STR_A01D_AIRPORT_CONSTRUCTION, 1, 0);
 
}
 

	
 
static void ToolbarForestClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 23, STR_LANDSCAPING, 3, 0);
 
}
 

	
 
static void ToolbarMusicClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 24, STR_01D3_SOUND_MUSIC, 1, 0);
 
}
 

	
 
static void ToolbarNewspaperClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 25, STR_0200_LAST_MESSAGE_NEWS_REPORT, 3, 0);
 
}
 

	
 
static void ToolbarHelpClick(Window *w)
 
{
 
	PopupMainToolbMenu(w, 26, STR_02D5_LAND_BLOCK_INFO, 6, 0);
 
}
 

	
 
static void ToolbarOptionsClick(Window *w)
 
{
 
	uint16 x = 0;
 

	
 
	w = PopupMainToolbMenu(w, 2, STR_02C3_GAME_OPTIONS, 13, 0);
 

	
 
	if (_display_opt & DO_SHOW_TOWN_NAMES)    SETBIT(x,  5);
 
	if (_display_opt & DO_SHOW_STATION_NAMES) SETBIT(x,  6);
 
	if (_display_opt & DO_SHOW_SIGNS)         SETBIT(x,  7);
 
	if (_display_opt & DO_WAYPOINTS)          SETBIT(x,  8);
 
	if (_display_opt & DO_FULL_ANIMATION)     SETBIT(x,  9);
 
	if (_display_opt & DO_FULL_DETAIL)        SETBIT(x, 10);
 
	if (_display_opt & DO_TRANS_BUILDINGS)    SETBIT(x, 11);
 
	if (_display_opt & DO_TRANS_SIGNS)        SETBIT(x, 12);
 
	WP(w,menu_d).checked_items = x;
 
}
 

	
 

	
 
static void ToolbarScenSaveOrLoad(Window *w)
 
{
 
	PopupMainToolbMenu(w, 3, STR_0292_SAVE_SCENARIO, 5, 0);
 
}
 

	
 
static void ToolbarScenDateBackward(Window *w)
 
{
 
	// don't allow too fast scrolling
 
	if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
 
		HandleButtonClick(w, 6);
 
		SetWindowDirty(w);
 

	
 
		_patches_newgame.starting_year = clamp(_patches_newgame.starting_year - 1, MIN_YEAR, MAX_YEAR);
 
		SetDate(ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
 
	}
 
	_left_button_clicked = false;
 
}
 

	
 
static void ToolbarScenDateForward(Window *w)
 
{
 
	// don't allow too fast scrolling
 
	if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
 
		HandleButtonClick(w, 7);
 
		SetWindowDirty(w);
 

	
 
		_patches_newgame.starting_year = clamp(_patches_newgame.starting_year + 1, MIN_YEAR, MAX_YEAR);
 
		SetDate(ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
 
	}
 
	_left_button_clicked = false;
 
}
 

	
 
static void ToolbarScenMapTownDir(Window *w)
 
{
 
	/* Scenario editor button, *hack*hack* use different button to activate */
 
	PopupMainToolbMenu(w, 8 | (17 << 8), STR_02DE_MAP_OF_WORLD, 4, 0);
 
}
 

	
 
static void ToolbarScenZoomIn(Window *w)
 
{
 
	if (DoZoomInOutWindow(ZOOM_IN, FindWindowById(WC_MAIN_WINDOW, 0))) {
 
		HandleButtonClick(w, 9);
 
		SndPlayFx(SND_15_BEEP);
 
	}
 
}
 

	
 
static void ToolbarScenZoomOut(Window *w)
 
{
 
	if (DoZoomInOutWindow(ZOOM_OUT, FindWindowById(WC_MAIN_WINDOW, 0))) {
 
		HandleButtonClick(w, 10);
 
		SndPlayFx(SND_15_BEEP);
 
	}
 
}
 

	
 
void ZoomInOrOutToCursorWindow(bool in, Window *w)
 
{
 
	ViewPort *vp;
 
	Point pt;
 

	
 
	assert(w != 0);
 

	
 
	vp = w->viewport;
 

	
 
	if (_game_mode != GM_MENU) {
 
		if ((in && vp->zoom == 0) || (!in && vp->zoom == 2))
 
			return;
 

	
 
		pt = GetTileZoomCenterWindow(in,w);
 
		if (pt.x != -1) {
 
			ScrollWindowTo(pt.x, pt.y, w);
 

	
 
			DoZoomInOutWindow(in ? ZOOM_IN : ZOOM_OUT, w);
 
		}
 
	}
 
}
 

	
 
// TODO - Incorporate into game itself to allow for ingame raising/lowering of
 
// larger chunks at the same time OR remove altogether, as we have 'level land' ?
 
/**
 
 * Raise/Lower a bigger chunk of land at the same time in the editor. When
 
 * raising get the lowest point, when lowering the highest point, and set all
 
 * tiles in the selection to that height.
 
 * @param tile The top-left tile where the terraforming will start
 
 * @param mode 1 for raising, 0 for lowering land
 
 */
 
static void CommonRaiseLowerBigLand(TileIndex tile, int mode)
 
{
 
	int sizex, sizey;
 
	byte h;
 

	
 
	_generating_world = true; // used to create green terraformed land
 

	
 
	if (_terraform_size == 1) {
 
		StringID msg =
 
			mode ? STR_0808_CAN_T_RAISE_LAND_HERE : STR_0809_CAN_T_LOWER_LAND_HERE;
 

	
 
		DoCommandP(tile, 8, (uint32)mode, CcTerraform, CMD_TERRAFORM_LAND | CMD_AUTO | CMD_MSG(msg));
 
	} else {
 
		SndPlayTileFx(SND_1F_SPLAT, tile);
 

	
 
		assert(_terraform_size != 0);
 
		// check out for map overflows
 
		sizex = min(MapSizeX() - TileX(tile) - 1, _terraform_size);
 
		sizey = min(MapSizeY() - TileY(tile) - 1, _terraform_size);
 

	
 
		if (sizex == 0 || sizey == 0) return;
 

	
 
		if (mode != 0) {
 
			/* Raise land */
 
			h = 15; // XXX - max height
 
			BEGIN_TILE_LOOP(tile2, sizex, sizey, tile) {
 
				h = min(h, TileHeight(tile2));
 
			} END_TILE_LOOP(tile2, sizex, sizey, tile)
 
		} else {
 
			/* Lower land */
 
			h = 0;
 
			BEGIN_TILE_LOOP(tile2, sizex, sizey, tile) {
 
				h = max(h, TileHeight(tile2));
 
			} END_TILE_LOOP(tile2, sizex, sizey, tile)
 
		}
 

	
 
		BEGIN_TILE_LOOP(tile2, sizex, sizey, tile) {
 
			if (TileHeight(tile2) == h) {
 
				DoCommandP(tile2, 8, (uint32)mode, NULL, CMD_TERRAFORM_LAND | CMD_AUTO);
 
			}
 
		} END_TILE_LOOP(tile2, sizex, sizey, tile)
 
	}
 

	
 
	_generating_world = false;
 
}
 

	
 
static void PlaceProc_RaiseBigLand(TileIndex tile)
 
{
 
	CommonRaiseLowerBigLand(tile, 1);
 
}
 

	
 
static void PlaceProc_LowerBigLand(TileIndex tile)
 
{
 
	CommonRaiseLowerBigLand(tile, 0);
 
}
 

	
 
static void PlaceProc_RockyArea(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_X_AND_Y | GUI_PlaceProc_RockyArea);
 
}
 

	
 
static void PlaceProc_LightHouse(TileIndex tile)
 
{
 
	if (!IsTileType(tile, MP_CLEAR) || IsSteepSlope(GetTileSlope(tile, NULL))) {
 
		return;
 
	}
 

	
 
	MakeLighthouse(tile);
 
	MarkTileDirtyByTile(tile);
 
	SndPlayTileFx(SND_1F_SPLAT, tile);
 
}
 

	
 
static void PlaceProc_Transmitter(TileIndex tile)
 
{
 
	if (!IsTileType(tile, MP_CLEAR) || IsSteepSlope(GetTileSlope(tile, NULL))) {
 
		return;
 
	}
 

	
 
	MakeTransmitter(tile);
 
	MarkTileDirtyByTile(tile);
 
	SndPlayTileFx(SND_1F_SPLAT, tile);
 
}
 

	
 
static void PlaceProc_DesertArea(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_X_AND_Y | GUI_PlaceProc_DesertArea);
 
}
 

	
 
static void PlaceProc_WaterArea(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_X_AND_Y | GUI_PlaceProc_WaterArea);
 
}
 

	
 
static const Widget _scen_edit_land_gen_widgets[] = {
 
{  WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                  STR_018B_CLOSE_WINDOW},
 
{   WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_0223_LAND_GENERATION,  STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_STICKYBOX,   RESIZE_NONE,     7,   170,   181,     0,    13, STR_NULL,                  STR_STICKY_BUTTON},
 
{     WWT_PANEL,   RESIZE_NONE,     7,     0,   181,    14,    95, 0x0,                       STR_NULL},
 
{    WWT_IMGBTN,   RESIZE_NONE,    14,     2,    23,    14,    35, SPR_IMG_DYNAMITE,          STR_018D_DEMOLISH_BUILDINGS_ETC},
 
{    WWT_IMGBTN,   RESIZE_NONE,    14,    24,    45,    14,    35, SPR_IMG_TERRAFORM_DOWN,    STR_018E_LOWER_A_CORNER_OF_LAND},
 
{    WWT_IMGBTN,   RESIZE_NONE,    14,    46,    67,    14,    35, SPR_IMG_TERRAFORM_UP,      STR_018F_RAISE_A_CORNER_OF_LAND},
 
{    WWT_IMGBTN,   RESIZE_NONE,    14,    68,    89,    14,    35, SPR_IMG_LEVEL_LAND,        STR_LEVEL_LAND_TOOLTIP},
 
{    WWT_IMGBTN,   RESIZE_NONE,    14,    90,   111,    14,    35, SPR_IMG_BUILD_CANAL,       STR_CREATE_LAKE},
 
{    WWT_IMGBTN,   RESIZE_NONE,    14,   112,   134,    14,    35, SPR_IMG_ROCKS,             STR_028C_PLACE_ROCKY_AREAS_ON_LANDSCAPE},
 
{    WWT_IMGBTN,   RESIZE_NONE,    14,   135,   157,    14,    35, SPR_IMG_LIGHTHOUSE_DESERT, STR_NULL}, // XXX - dynamic
 
{    WWT_IMGBTN,   RESIZE_NONE,    14,   158,   179,    14,    35, SPR_IMG_TRANSMITTER,       STR_028E_PLACE_TRANSMITTER},
 
{   WWT_TEXTBTN,   RESIZE_NONE,    14,   139,   149,    43,    54, STR_0224,                  STR_0228_INCREASE_SIZE_OF_LAND_AREA},
 
{   WWT_TEXTBTN,   RESIZE_NONE,    14,   139,   149,    56,    67, STR_0225,                  STR_0229_DECREASE_SIZE_OF_LAND_AREA},
 
{   WWT_TEXTBTN,   RESIZE_NONE,    14,    34,   149,    75,    86, STR_SE_NEW_WORLD,          STR_022A_GENERATE_RANDOM_LAND},
 
{   WIDGETS_END},
 
};
 

	
 
static const int8 _multi_terraform_coords[][2] = {
 
	{  0, -2},
 
	{  4,  0}, { -4,  0}, {  0,  2},
 
	{ -8,  2}, { -4,  4}, {  0,  6}, {  4,  4}, {  8,  2},
 
	{-12,  0}, { -8, -2}, { -4, -4}, {  0, -6}, {  4, -4}, {  8, -2}, { 12,  0},
 
	{-16,  2}, {-12,  4}, { -8,  6}, { -4,  8}, {  0, 10}, {  4,  8}, {  8,  6}, { 12,  4}, { 16,  2},
 
	{-20,  0}, {-16, -2}, {-12, -4}, { -8, -6}, { -4, -8}, {  0,-10}, {  4, -8}, {  8, -6}, { 12, -4}, { 16, -2}, { 20,  0},
 
	{-24,  2}, {-20,  4}, {-16,  6}, {-12,  8}, { -8, 10}, { -4, 12}, {  0, 14}, {  4, 12}, {  8, 10}, { 12,  8}, { 16,  6}, { 20,  4}, { 24,  2},
 
	{-28,  0}, {-24, -2}, {-20, -4}, {-16, -6}, {-12, -8}, { -8,-10}, { -4,-12}, {  0,-14}, {  4,-12}, {  8,-10}, { 12, -8}, { 16, -6}, { 20, -4}, { 24, -2}, { 28,  0},
 
};
 

	
 
// TODO - Merge with terraform_gui.c (move there) after I have cooled down at its braindeadness
 
// and changed OnButtonClick to include the widget as well in the function decleration. Post 0.4.0 - Darkvater
 
static void EditorTerraformClick_Dynamite(Window *w)
 
{
 
	HandlePlacePushButton(w, 4, ANIMCURSOR_DEMOLISH, 1, PlaceProc_DemolishArea);
 
}
 

	
 
static void EditorTerraformClick_LowerBigLand(Window *w)
 
{
 
	HandlePlacePushButton(w, 5, ANIMCURSOR_LOWERLAND, 2, PlaceProc_LowerBigLand);
 
}
 

	
 
static void EditorTerraformClick_RaiseBigLand(Window *w)
 
{
 
	HandlePlacePushButton(w, 6, ANIMCURSOR_RAISELAND, 2, PlaceProc_RaiseBigLand);
 
}
 

	
 
static void EditorTerraformClick_LevelLand(Window *w)
 
{
 
	HandlePlacePushButton(w, 7, SPR_CURSOR_LEVEL_LAND, 2, PlaceProc_LevelLand);
 
}
 

	
 
static void EditorTerraformClick_WaterArea(Window *w)
 
{
 
	HandlePlacePushButton(w, 8, SPR_CURSOR_CANAL, 1, PlaceProc_WaterArea);
 
}
 

	
 
static void EditorTerraformClick_RockyArea(Window *w)
 
{
 
	HandlePlacePushButton(w, 9, SPR_CURSOR_ROCKY_AREA, 1, PlaceProc_RockyArea);
 
}
 

	
 
static void EditorTerraformClick_DesertLightHouse(Window *w)
 
{
 
	HandlePlacePushButton(w, 10, SPR_CURSOR_LIGHTHOUSE, 1, (_opt.landscape == LT_DESERT) ? PlaceProc_DesertArea : PlaceProc_LightHouse);
 
}
 

	
 
static void EditorTerraformClick_Transmitter(Window *w)
 
{
 
	HandlePlacePushButton(w, 11, SPR_CURSOR_TRANSMITTER, 1, PlaceProc_Transmitter);
 
}
 

	
 
static const uint16 _editor_terraform_keycodes[] = {
 
	'D',
 
	'Q',
 
	'W',
 
	'E',
 
	'R',
 
	'T',
 
	'Y',
 
	'U'
 
};
 

	
 
typedef void OnButtonClick(Window *w);
 
static OnButtonClick * const _editor_terraform_button_proc[] = {
 
	EditorTerraformClick_Dynamite,
 
	EditorTerraformClick_LowerBigLand,
 
	EditorTerraformClick_RaiseBigLand,
 
	EditorTerraformClick_LevelLand,
 
	EditorTerraformClick_WaterArea,
 
	EditorTerraformClick_RockyArea,
 
	EditorTerraformClick_DesertLightHouse,
 
	EditorTerraformClick_Transmitter
 
};
 

	
 
static void ScenEditLandGenWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE:
 
		// XXX - lighthouse button is widget 10!! Don't forget when changing
 
		w->widget[10].tooltips = (_opt.landscape == LT_DESERT) ? STR_028F_DEFINE_DESERT_AREA : STR_028D_PLACE_LIGHTHOUSE;
 
		break;
 

	
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 

	
 
		{
 
			int n = _terraform_size * _terraform_size;
 
			const int8 *coords = &_multi_terraform_coords[0][0];
 

	
 
			assert(n != 0);
 
			do {
 
				DrawSprite(SPR_WHITE_POINT, 77 + coords[0], 55 + coords[1]);
 
				coords += 2;
 
			} while (--n);
 
		}
 

	
 
		if (IsWindowWidgetLowered(w, 5) || IsWindowWidgetLowered(w, 6)) // change area-size if raise/lower corner is selected
 
			SetTileSelectSize(_terraform_size, _terraform_size);
 

	
 
		break;
 

	
 
	case WE_KEYPRESS: {
 
		uint i;
 

	
 
		for (i = 0; i != lengthof(_editor_terraform_keycodes); i++) {
 
			if (e->we.keypress.keycode == _editor_terraform_keycodes[i]) {
 
				e->we.keypress.cont = false;
 
				_editor_terraform_button_proc[i](w);
 
				break;
 
			}
 
		}
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11:
 
			_editor_terraform_button_proc[e->we.click.widget - 4](w);
 
			break;
 
		case 12: case 13: { /* Increase/Decrease terraform size */
 
			int size = (e->we.click.widget == 12) ? 1 : -1;
 
			HandleButtonClick(w, e->we.click.widget);
 
			size += _terraform_size;
 

	
 
			if (!IS_INT_INSIDE(size, 1, 8 + 1)) return;
 
			_terraform_size = size;
 

	
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
		} break;
 
		case 14: /* gen random land */
 
			HandleButtonClick(w, 14);
 
			ShowCreateScenario();
 
			break;
 
		}
 
		break;
 

	
 
	case WE_TIMEOUT: {
 
		uint i;
 
		for (i = 0; i < w->widget_count; i++) {
 
			if (IsWindowWidgetLowered(w, i)) {
 
				RaiseWindowWidget(w, i);
 
				InvalidateWidget(w, i);
 
			}
 
			if (i == 3) i = 11;
 
		}
 
		break;
 
	}
 
	case WE_PLACE_OBJ:
 
		_place_proc(e->we.place.tile);
 
		break;
 
	case WE_PLACE_DRAG:
 
		VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata & 0xF);
 
		break;
 

	
 
	case WE_PLACE_MOUSEUP:
 
		if (e->we.place.pt.x != -1) {
 
			if ((e->we.place.userdata & 0xF) == VPM_X_AND_Y) // dragged actions
 
				GUIPlaceProcDragXY(e);
 
		}
 
		break;
 

	
 
	case WE_ABORT_PLACE_OBJ:
 
		RaiseWindowButtons(w);
 
		SetWindowDirty(w);
 
		break;
 
	}
 
}
 

	
 
static const WindowDesc _scen_edit_land_gen_desc = {
 
	WDP_AUTO, WDP_AUTO, 182, 96,
 
	WC_SCEN_LAND_GEN,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
 
	_scen_edit_land_gen_widgets,
 
	ScenEditLandGenWndProc,
 
};
 

	
 
static inline void ShowEditorTerraformToolBar(void)
 
{
 
	AllocateWindowDescFront(&_scen_edit_land_gen_desc, 0);
 
}
 

	
 
static void ToolbarScenGenLand(Window *w)
 
{
 
	HandleButtonClick(w, 11);
 
	SndPlayFx(SND_15_BEEP);
 

	
 
	ShowEditorTerraformToolBar();
 
}
 

	
 
void CcBuildTown(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_1F_SPLAT, tile);
 
		ResetObjectToPlace();
 
	}
 
}
 

	
 
static void PlaceProc_Town(TileIndex tile)
 
{
 
	DoCommandP(tile, _scengen_town_size, 0, CcBuildTown, CMD_BUILD_TOWN | CMD_MSG(STR_0236_CAN_T_BUILD_TOWN_HERE));
 
}
 

	
 

	
 
static const Widget _scen_edit_town_gen_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   147,     0,    13, STR_0233_TOWN_GENERATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,     7,   148,   159,     0,    13, 0x0,                      STR_STICKY_BUTTON},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   159,    14,    81, 0x0,                      STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   157,    16,    27, STR_0234_NEW_TOWN,        STR_0235_CONSTRUCT_NEW_TOWN},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   157,    29,    40, STR_023D_RANDOM_TOWN,     STR_023E_BUILD_TOWN_IN_RANDOM_LOCATION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   157,    42,    53, STR_MANY_RANDOM_TOWNS,    STR_RANDOM_TOWNS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,    53,    68,    79, STR_02A1_SMALL,           STR_02A4_SELECT_TOWN_SIZE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    54,   105,    68,    79, STR_02A2_MEDIUM,          STR_02A4_SELECT_TOWN_SIZE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   106,   157,    68,    79, STR_02A3_LARGE,           STR_02A4_SELECT_TOWN_SIZE},
 
{      WWT_LABEL,   RESIZE_NONE,     7,     0,   147,    54,    67, STR_02A5_TOWN_SIZE,       STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static void ScenEditTownGenWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 
		break;
 

	
 
	case WE_CREATE:
 
		LowerWindowWidget(w, (_scengen_town_size - 1)+ 7);
 
		break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 4: /* new town */
 
			HandlePlacePushButton(w, 4, SPR_CURSOR_TOWN, 1, PlaceProc_Town);
 
			break;
 
		case 5: {/* random town */
 
			Town *t;
 

	
 
			HandleButtonClick(w, 5);
 
			_generating_world = true;
 
			t = CreateRandomTown(20, _scengen_town_size);
 
			_generating_world = false;
 

	
 
			if (t == NULL) {
 
				ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0);
 
			} else {
 
				ScrollMainWindowToTile(t->xy);
 
			}
 

	
 
			break;
 
		}
 
		case 6: {/* many random towns */
 
			HandleButtonClick(w, 6);
 

	
 
			_generating_world = true;
 
			if (!GenerateTowns()) ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0);
 
			_generating_world = false;
 
			break;
 
		}
 

	
 
		case 7: case 8: case 9:
 
			RaiseWindowWidget(w, (_scengen_town_size - 1) + 7);
 
			_scengen_town_size = (e->we.click.widget - 7) + 1;
 
			LowerWindowWidget(w, (_scengen_town_size - 1) + 7);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_TIMEOUT:
 
		RaiseWindowWidget(w, 5);
 
		RaiseWindowWidget(w, 6);
 
		SetWindowDirty(w);
 
		break;
 
	case WE_PLACE_OBJ:
 
		_place_proc(e->we.place.tile);
 
		break;
 
	case WE_ABORT_PLACE_OBJ:
 
		RaiseWindowButtons(w);
 
		LowerWindowWidget(w, (_scengen_town_size - 1) + 7);
 
		SetWindowDirty(w);
 
		break;
 
	}
 
}
 

	
 
static const WindowDesc _scen_edit_town_gen_desc = {
 
	WDP_AUTO, WDP_AUTO, 160, 82,
 
	WC_SCEN_TOWN_GEN,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
 
	_scen_edit_town_gen_widgets,
 
	ScenEditTownGenWndProc,
 
};
 

	
 
static void ToolbarScenGenTown(Window *w)
 
{
 
	HandleButtonClick(w, 12);
 
	SndPlayFx(SND_15_BEEP);
 

	
 
	AllocateWindowDescFront(&_scen_edit_town_gen_desc, 0);
 
}
 

	
 

	
 
static const Widget _scenedit_industry_normal_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                     STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_023F_INDUSTRY_GENERATION, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   224, 0x0,                          STR_NULL},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_MANY_RANDOM_INDUSTRIES,   STR_RANDOM_INDUSTRIES_TIP},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_0240_COAL_MINE,           STR_0262_CONSTRUCT_COAL_MINE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_0241_POWER_STATION,       STR_0263_CONSTRUCT_POWER_STATION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    68,    79, STR_0242_SAWMILL,             STR_0264_CONSTRUCT_SAWMILL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    81,    92, STR_0243_FOREST,              STR_0265_PLANT_FOREST},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    94,   105, STR_0244_OIL_REFINERY,        STR_0266_CONSTRUCT_OIL_REFINERY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   107,   118, STR_0245_OIL_RIG,             STR_0267_CONSTRUCT_OIL_RIG_CAN_ONLY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   120,   131, STR_0246_FACTORY,             STR_0268_CONSTRUCT_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   133,   144, STR_0247_STEEL_MILL,          STR_0269_CONSTRUCT_STEEL_MILL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   146,   157, STR_0248_FARM,                STR_026A_CONSTRUCT_FARM},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   159,   170, STR_0249_IRON_ORE_MINE,       STR_026B_CONSTRUCT_IRON_ORE_MINE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   172,   183, STR_024A_OIL_WELLS,           STR_026C_CONSTRUCT_OIL_WELLS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   185,   196, STR_024B_BANK,                STR_026D_CONSTRUCT_BANK_CAN_ONLY},
 
{   WIDGETS_END},
 
};
 

	
 

	
 
static const Widget _scenedit_industry_hilly_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_023F_INDUSTRY_GENERATION,   STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   224, 0x0,                            STR_NULL},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_MANY_RANDOM_INDUSTRIES,     STR_RANDOM_INDUSTRIES_TIP},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_0240_COAL_MINE,             STR_0262_CONSTRUCT_COAL_MINE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_0241_POWER_STATION,         STR_0263_CONSTRUCT_POWER_STATION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    68,    79, STR_024C_PAPER_MILL,            STR_026E_CONSTRUCT_PAPER_MILL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    81,    92, STR_0243_FOREST,                STR_0265_PLANT_FOREST},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    94,   105, STR_0244_OIL_REFINERY,          STR_0266_CONSTRUCT_OIL_REFINERY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   107,   118, STR_024D_FOOD_PROCESSING_PLANT, STR_026F_CONSTRUCT_FOOD_PROCESSING},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   120,   131, STR_024E_PRINTING_WORKS,        STR_0270_CONSTRUCT_PRINTING_WORKS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   133,   144, STR_024F_GOLD_MINE,             STR_0271_CONSTRUCT_GOLD_MINE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   146,   157, STR_0248_FARM,                  STR_026A_CONSTRUCT_FARM},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   159,   170, STR_024B_BANK,                  STR_0272_CONSTRUCT_BANK_CAN_ONLY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   172,   183, STR_024A_OIL_WELLS,             STR_026C_CONSTRUCT_OIL_WELLS},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _scenedit_industry_desert_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_023F_INDUSTRY_GENERATION,    STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   224, 0x0,                             STR_NULL},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_MANY_RANDOM_INDUSTRIES,      STR_RANDOM_INDUSTRIES_TIP},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_0250_LUMBER_MILL,            STR_0273_CONSTRUCT_LUMBER_MILL_TO},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_0251_FRUIT_PLANTATION,       STR_0274_PLANT_FRUIT_PLANTATION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    68,    79, STR_0252_RUBBER_PLANTATION,      STR_0275_PLANT_RUBBER_PLANTATION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    81,    92, STR_0244_OIL_REFINERY,           STR_0266_CONSTRUCT_OIL_REFINERY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    94,   105, STR_024D_FOOD_PROCESSING_PLANT,  STR_026F_CONSTRUCT_FOOD_PROCESSING},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   107,   118, STR_0246_FACTORY,                STR_0268_CONSTRUCT_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   120,   131, STR_0253_WATER_SUPPLY,           STR_0276_CONSTRUCT_WATER_SUPPLY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   133,   144, STR_0248_FARM,                   STR_026A_CONSTRUCT_FARM},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   146,   157, STR_0254_WATER_TOWER,            STR_0277_CONSTRUCT_WATER_TOWER_CAN},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   159,   170, STR_024A_OIL_WELLS,              STR_026C_CONSTRUCT_OIL_WELLS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   172,   183, STR_024B_BANK,                   STR_0272_CONSTRUCT_BANK_CAN_ONLY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   185,   196, STR_0255_DIAMOND_MINE,           STR_0278_CONSTRUCT_DIAMOND_MINE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   198,   209, STR_0256_COPPER_ORE_MINE,        STR_0279_CONSTRUCT_COPPER_ORE_MINE},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _scenedit_industry_candy_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                     STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   169,     0,    13, STR_023F_INDUSTRY_GENERATION, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   169,    14,   224, 0x0,                          STR_NULL},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    16,    27, STR_MANY_RANDOM_INDUSTRIES,   STR_RANDOM_INDUSTRIES_TIP},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    42,    53, STR_0257_COTTON_CANDY_FOREST, STR_027A_PLANT_COTTON_CANDY_FOREST},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    55,    66, STR_0258_CANDY_FACTORY,       STR_027B_CONSTRUCT_CANDY_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    68,    79, STR_0259_BATTERY_FARM,        STR_027C_CONSTRUCT_BATTERY_FARM},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    81,    92, STR_025A_COLA_WELLS,          STR_027D_CONSTRUCT_COLA_WELLS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,    94,   105, STR_025B_TOY_SHOP,            STR_027E_CONSTRUCT_TOY_SHOP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   107,   118, STR_025C_TOY_FACTORY,         STR_027F_CONSTRUCT_TOY_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   120,   131, STR_025D_PLASTIC_FOUNTAINS,   STR_0280_CONSTRUCT_PLASTIC_FOUNTAINS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   133,   144, STR_025E_FIZZY_DRINK_FACTORY, STR_0281_CONSTRUCT_FIZZY_DRINK_FACTORY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   146,   157, STR_025F_BUBBLE_GENERATOR,    STR_0282_CONSTRUCT_BUBBLE_GENERATOR},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   159,   170, STR_0260_TOFFEE_QUARRY,       STR_0283_CONSTRUCT_TOFFEE_QUARRY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   167,   172,   183, STR_0261_SUGAR_MINE,          STR_0284_CONSTRUCT_SUGAR_MINE},
 
{   WIDGETS_END},
 
};
 

	
 

	
 
static bool AnyTownExists(void)
 
{
 
	const Town *t;
 

	
 
	FOR_ALL_TOWNS(t) return true;
 

	
 
	return false;
 
}
 

	
 
extern Industry *CreateNewIndustry(TileIndex tile, int type);
 

	
 
/**
 
 * Search callback function for TryBuildIndustry
 
 * @param tile to test
 
 * @param data that is passed by the caller.  In this case, the type of industry been tested
 
 * @result of the operation
 
 */
 
static bool SearchTileForIndustry(TileIndex tile, uint32 data)
 
{
 
	return CreateNewIndustry(tile, data) != NULL;
 
}
 

	
 
/**
 
 * Perform a 9*9 tiles circular search around a tile
 
 * in order to find a suitable zone to create the desired industry
 
 * @param tile to start search for
 
 * @param type of the desired industry
 
 */
 
static bool TryBuildIndustry(TileIndex tile, int type)
 
{
 
	return CircularTileSearch(tile, 9, SearchTileForIndustry, type);
 
}
 

	
 

	
 
static const byte _industry_type_list[4][16] = {
 
	{ 0,  1,  2,  3,  4,  5,  6,  8,  9, 18, 11, 12},
 
	{ 0,  1, 14,  3,  4, 13,  7, 15,  9, 16, 11, 12},
 
	{25, 19, 20,  4, 13, 23, 21, 24, 22, 11, 16, 17, 10},
 
	{26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36},
 
};
 

	
 
static int _industry_type_to_place;
 
bool _ignore_restrictions;
 

	
 
static void ScenEditIndustryWndProc(Window *w, WindowEvent *e)
 
{
 
	int button;
 

	
 
	switch (e->event) {
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		if (e->we.click.widget == 3) {
 
			HandleButtonClick(w, 3);
 

	
 
			if (!AnyTownExists()) {
 
				ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_CAN_T_GENERATE_INDUSTRIES, 0, 0);
 
				return;
 
			}
 

	
 
			_generating_world = true;
 
			GenerateIndustries();
 
			_generating_world = false;
 
		}
 

	
 
		if ((button=e->we.click.widget) >= 4) {
 
			if (HandlePlacePushButton(w, button, SPR_CURSOR_INDUSTRY, 1, NULL))
 
				_industry_type_to_place = _industry_type_list[_opt.landscape][button - 4];
 
		}
 
		break;
 
	case WE_PLACE_OBJ: {
 
		int type;
 

	
 
		// Show error if no town exists at all
 
		type = _industry_type_to_place;
 
		if (!AnyTownExists()) {
 
			SetDParam(0, GetIndustrySpec(type)->name);
 
			ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_0285_CAN_T_BUILD_HERE, e->we.place.pt.x, e->we.place.pt.y);
 
			return;
 
		}
 

	
 
		_current_player = OWNER_NONE;
 
		_generating_world = true;
 
		_ignore_restrictions = true;
 
		if (!TryBuildIndustry(e->we.place.tile,type)) {
 
			SetDParam(0, GetIndustrySpec(type)->name);
 
			ShowErrorMessage(_error_message, STR_0285_CAN_T_BUILD_HERE, e->we.place.pt.x, e->we.place.pt.y);
 
		}
 
		_ignore_restrictions = false;
 
		_generating_world = false;
 
		break;
 
	}
 
	case WE_ABORT_PLACE_OBJ:
 
		RaiseWindowButtons(w);
 
		SetWindowDirty(w);
 
		break;
 
	case WE_TIMEOUT:
 
		RaiseWindowWidget(w, 3);
 
		InvalidateWidget(w, 3);
 
		break;
 
	}
 
}
 

	
 
static const WindowDesc _scenedit_industry_normal_desc = {
 
	WDP_AUTO, WDP_AUTO, 170, 225,
 
	WC_SCEN_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_scenedit_industry_normal_widgets,
 
	ScenEditIndustryWndProc,
 
};
 

	
 
static const WindowDesc _scenedit_industry_hilly_desc = {
 
	WDP_AUTO, WDP_AUTO, 170, 225,
 
	WC_SCEN_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_scenedit_industry_hilly_widgets,
 
	ScenEditIndustryWndProc,
 
};
 

	
 
static const WindowDesc _scenedit_industry_desert_desc = {
 
	WDP_AUTO, WDP_AUTO, 170, 225,
 
	WC_SCEN_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_scenedit_industry_desert_widgets,
 
	ScenEditIndustryWndProc,
 
};
 

	
 
static const WindowDesc _scenedit_industry_candy_desc = {
 
	WDP_AUTO, WDP_AUTO, 170, 225,
 
	WC_SCEN_INDUSTRY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_scenedit_industry_candy_widgets,
 
	ScenEditIndustryWndProc,
 
};
 

	
 
static const WindowDesc * const _scenedit_industry_descs[] = {
 
	&_scenedit_industry_normal_desc,
 
	&_scenedit_industry_hilly_desc,
 
	&_scenedit_industry_desert_desc,
 
	&_scenedit_industry_candy_desc,
 
};
 

	
 

	
 
static void ToolbarScenGenIndustry(Window *w)
 
{
 
	HandleButtonClick(w, 13);
 
	SndPlayFx(SND_15_BEEP);
 
	AllocateWindowDescFront(_scenedit_industry_descs[_opt.landscape],0);
 
}
 

	
 
static void ToolbarScenBuildRoad(Window *w)
 
{
 
	HandleButtonClick(w, 14);
 
	SndPlayFx(SND_15_BEEP);
 
	ShowBuildRoadScenToolbar();
 
}
 

	
 
static void ToolbarScenPlantTrees(Window *w)
 
{
 
	HandleButtonClick(w, 15);
 
	SndPlayFx(SND_15_BEEP);
 
	ShowBuildTreesScenToolbar();
 
}
 

	
 
static void ToolbarScenPlaceSign(Window *w)
 
{
 
	HandleButtonClick(w, 16);
 
	SndPlayFx(SND_15_BEEP);
 
	SelectSignTool();
 
}
 

	
 
static void ToolbarBtn_NULL(Window *w)
 
{
 
}
 

	
 

	
 
typedef void ToolbarButtonProc(Window *w);
 

	
 
static ToolbarButtonProc * const _toolbar_button_procs[] = {
 
	ToolbarPauseClick,
 
	ToolbarFastForwardClick,
 
	ToolbarOptionsClick,
 
	ToolbarSaveClick,
 
	ToolbarMapClick,
 
	ToolbarTownClick,
 
	ToolbarSubsidiesClick,
 
	ToolbarStationsClick,
 
	ToolbarMoneyClick,
 
	ToolbarPlayersClick,
 
	ToolbarGraphsClick,
 
	ToolbarLeagueClick,
 
	ToolbarIndustryClick,
 
	ToolbarTrainClick,
 
	ToolbarRoadClick,
 
	ToolbarShipClick,
 
	ToolbarAirClick,
 
	ToolbarZoomInClick,
 
	ToolbarZoomOutClick,
 
	ToolbarBuildRailClick,
 
	ToolbarBuildRoadClick,
 
	ToolbarBuildWaterClick,
 
	ToolbarBuildAirClick,
 
	ToolbarForestClick,
 
	ToolbarMusicClick,
 
	ToolbarNewspaperClick,
 
	ToolbarHelpClick,
 
};
 

	
 
static void MainToolbarWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		// Draw brown-red toolbar bg.
 
		GfxFillRect(0, 0, w->width-1, w->height-1, 0xB2);
 
		GfxFillRect(0, 0, w->width-1, w->height-1, 0xB4 | PALETTE_MODIFIER_GREYOUT);
 

	
 
		/* If spectator, disable all construction buttons
 
		 * ie : Build road, rail, ships, airports and landscaping
 
		 * Since enabled state is the default, just disable when needed */
 
		SetWindowWidgetsDisabledState(w, _current_player == PLAYER_SPECTATOR, 19, 20, 21, 22, 23, WIDGET_LIST_END);
 
		/* disable company list drop downs, if there are no companies */
 
		SetWindowWidgetsDisabledState(w, ActivePlayerCount() == 0, 7, 8, 13, 14, 15, 16, WIDGET_LIST_END);
 

	
 
		DrawWindowWidgets(w);
 
		break;
 

	
 
	case WE_CLICK: {
 
		if (_game_mode != GM_MENU && !IsWindowWidgetDisabled(w, e->we.click.widget))
 
			_toolbar_button_procs[e->we.click.widget](w);
 
	} break;
 

	
 
	case WE_KEYPRESS: {
 
		switch (e->we.keypress.keycode) {
 
		case WKC_F1: case WKC_PAUSE: ToolbarPauseClick(w); break;
 
		case WKC_F2: ShowGameOptions(); break;
 
		case WKC_F3: MenuClickSaveLoad(0); break;
 
		case WKC_F4: ShowSmallMap(); break;
 
		case WKC_F5: ShowTownDirectory(); break;
 
		case WKC_F6: ShowSubsidiesList(); break;
 
		case WKC_F7: ShowPlayerStations(_local_player); break;
 
		case WKC_F8: ShowPlayerFinances(_local_player); break;
 
		case WKC_F9: ShowPlayerCompany(_local_player); break;
 
		case WKC_F10:ShowOperatingProfitGraph(); break;
 
		case WKC_F11: ShowCompanyLeagueTable(); break;
 
		case WKC_F12: ShowBuildIndustryWindow(); break;
 
		case WKC_SHIFT | WKC_F1: ShowVehicleListWindow(_local_player, INVALID_STATION, VEH_Train); break;
 
		case WKC_SHIFT | WKC_F2: ShowVehicleListWindow(_local_player, INVALID_STATION, VEH_Road); break;
 
		case WKC_SHIFT | WKC_F3: ShowVehicleListWindow(_local_player, INVALID_STATION, VEH_Ship); break;
 
		case WKC_SHIFT | WKC_F4: ShowVehicleListWindow(_local_player, INVALID_STATION, VEH_Aircraft); break;
 
		case WKC_SHIFT | WKC_F5: ToolbarZoomInClick(w); break;
 
		case WKC_SHIFT | WKC_F6: ToolbarZoomOutClick(w); break;
 
		case WKC_SHIFT | WKC_F7: ShowBuildRailToolbar(_last_built_railtype, -1); break;
 
		case WKC_SHIFT | WKC_F8: ShowBuildRoadToolbar(); break;
 
		case WKC_SHIFT | WKC_F9: ShowBuildDocksToolbar(); break;
 
		case WKC_SHIFT | WKC_F10:ShowBuildAirToolbar(); break;
 
		case WKC_SHIFT | WKC_F11: ShowBuildTreesToolbar(); break;
 
		case WKC_SHIFT | WKC_F12: ShowMusicWindow(); break;
 
		case WKC_CTRL  | 'S': MenuClickSmallScreenshot(); break;
 
		case WKC_CTRL  | 'G': MenuClickWorldScreenshot(); break;
 
		case WKC_CTRL | WKC_ALT | 'C': if (!_networking) ShowCheatWindow(); break;
 
		case 'A': ShowBuildRailToolbar(_last_built_railtype, 4); break; /* Invoke Autorail */
 
		case 'L': ShowTerraformToolbar(); break;
 
		default: return;
 
		}
 
		e->we.keypress.cont = false;
 
	} break;
 

	
 
	case WE_PLACE_OBJ: {
 
		_place_proc(e->we.place.tile);
 
	} break;
 

	
 
	case WE_ABORT_PLACE_OBJ: {
 
		RaiseWindowWidget(w, 25);
 
		SetWindowDirty(w);
 
	} break;
 

	
 
	case WE_MOUSELOOP:
 
		if (IsWindowWidgetLowered(w, 0) != !!_pause) {
 
			ToggleWidgetLoweredState(w, 0);
 
			InvalidateWidget(w, 0);
 
		}
 

	
 
		if (IsWindowWidgetLowered(w, 1) != !!_fast_forward) {
 
			ToggleWidgetLoweredState(w, 1);
 
			InvalidateWidget(w, 1);
 
		}
 
		break;
 

	
 
	case WE_TIMEOUT: {
 
		uint i;
 
		for (i = 2; i < w->widget_count; i++) {
 
			if (IsWindowWidgetLowered(w, i)) {
 
				RaiseWindowWidget(w, i);
 
				InvalidateWidget(w, i);
 
			}
 
		}
 
		break;
 
	}
 

	
 
		case WE_MESSAGE:
 
			HandleZoomMessage(w, FindWindowById(WC_MAIN_WINDOW, 0)->viewport, 17, 18);
 
			break;
 
	}
 
}
 

	
 
static const Widget _toolb_normal_widgets[] = {
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,     0,    21,     0,    21, SPR_IMG_PAUSE,           STR_0171_PAUSE_GAME},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,    22,    43,     0,    21, SPR_IMG_FASTFORWARD,     STR_FAST_FORWARD},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,    44,    65,     0,    21, SPR_IMG_SETTINGS,        STR_0187_OPTIONS},
 
{   WWT_IMGBTN_2,   RESIZE_NONE,    14,    66,    87,     0,    21, SPR_IMG_SAVE,            STR_0172_SAVE_GAME_ABANDON_GAME},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,    96,   117,     0,    21, SPR_IMG_SMALLMAP,        STR_0174_DISPLAY_MAP},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   118,   139,     0,    21, SPR_IMG_TOWN,            STR_0176_DISPLAY_TOWN_DIRECTORY},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   140,   161,     0,    21, SPR_IMG_SUBSIDIES,       STR_02DC_DISPLAY_SUBSIDIES},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   162,   183,     0,    21, SPR_IMG_COMPANY_LIST,    STR_0173_DISPLAY_LIST_OF_COMPANY},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   191,   212,     0,    21, SPR_IMG_COMPANY_FINANCE, STR_0177_DISPLAY_COMPANY_FINANCES},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   213,   235,     0,    21, SPR_IMG_COMPANY_GENERAL, STR_0178_DISPLAY_COMPANY_GENERAL},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   236,   257,     0,    21, SPR_IMG_GRAPHS,          STR_0179_DISPLAY_GRAPHS},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   258,   279,     0,    21, SPR_IMG_COMPANY_LEAGUE,  STR_017A_DISPLAY_COMPANY_LEAGUE},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   280,   301,     0,    21, SPR_IMG_INDUSTRY,        STR_0312_FUND_CONSTRUCTION_OF_NEW},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   310,   331,     0,    21, SPR_IMG_TRAINLIST,       STR_017B_DISPLAY_LIST_OF_COMPANY},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   332,   353,     0,    21, SPR_IMG_TRUCKLIST,       STR_017C_DISPLAY_LIST_OF_COMPANY},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   354,   375,     0,    21, SPR_IMG_SHIPLIST,        STR_017D_DISPLAY_LIST_OF_COMPANY},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   376,   397,     0,    21, SPR_IMG_AIRPLANESLIST,   STR_017E_DISPLAY_LIST_OF_COMPANY},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   406,   427,     0,    21, SPR_IMG_ZOOMIN,          STR_017F_ZOOM_THE_VIEW_IN},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   428,   449,     0,    21, SPR_IMG_ZOOMOUT,         STR_0180_ZOOM_THE_VIEW_OUT},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   457,   478,     0,    21, SPR_IMG_BUILDRAIL,       STR_0181_BUILD_RAILROAD_TRACK},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   479,   500,     0,    21, SPR_IMG_BUILDROAD,       STR_0182_BUILD_ROADS},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   501,   522,     0,    21, SPR_IMG_BUILDWATER,      STR_0183_BUILD_SHIP_DOCKS},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   523,   544,     0,    21, SPR_IMG_BUILDAIR,        STR_0184_BUILD_AIRPORTS},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   545,   566,     0,    21, SPR_IMG_LANDSCAPING,     STR_LANDSCAPING_TOOLBAR_TIP}, // tree icon is 0x2E6
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   574,   595,     0,    21, SPR_IMG_MUSIC,           STR_01D4_SHOW_SOUND_MUSIC_WINDOW},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   596,   617,     0,    21, SPR_IMG_MESSAGES,        STR_0203_SHOW_LAST_MESSAGE_NEWS},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   618,   639,     0,    21, SPR_IMG_QUERY,           STR_0186_LAND_BLOCK_INFORMATION},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _toolb_normal_desc = {
 
	0, 0, 640, 22,
 
	WC_MAIN_TOOLBAR,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
 
	_toolb_normal_widgets,
 
	MainToolbarWndProc
 
};
 

	
 

	
 
static const Widget _toolb_scen_widgets[] = {
 
{  WWT_IMGBTN, RESIZE_NONE, 14,   0,  21,  0, 21, SPR_IMG_PAUSE,       STR_0171_PAUSE_GAME},
 
{  WWT_IMGBTN, RESIZE_NONE, 14,  22,  43,  0, 21, SPR_IMG_FASTFORWARD, STR_FAST_FORWARD},
 
{  WWT_IMGBTN, RESIZE_NONE, 14,  44,  65,  0, 21, SPR_IMG_SETTINGS,    STR_0187_OPTIONS},
 
{WWT_IMGBTN_2, RESIZE_NONE, 14,  66,  87,  0, 21, SPR_IMG_SAVE,        STR_0297_SAVE_SCENARIO_LOAD_SCENARIO},
 

	
 
{   WWT_PANEL, RESIZE_NONE, 14,  96, 225,  0, 21, 0x0,                 STR_NULL},
 

	
 
{   WWT_PANEL, RESIZE_NONE, 14, 233, 362,  0, 21, 0x0,                 STR_NULL},
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 236, 247,  5, 16, SPR_ARROW_DOWN,      STR_029E_MOVE_THE_STARTING_DATE},
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 347, 358,  5, 16, SPR_ARROW_UP,        STR_029F_MOVE_THE_STARTING_DATE},
 

	
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 371, 392,  0, 21, SPR_IMG_SMALLMAP,    STR_0175_DISPLAY_MAP_TOWN_DIRECTORY},
 

	
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 400, 421,  0, 21, SPR_IMG_ZOOMIN,      STR_017F_ZOOM_THE_VIEW_IN},
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 422, 443,  0, 21, SPR_IMG_ZOOMOUT,     STR_0180_ZOOM_THE_VIEW_OUT},
 

	
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 452, 473,  0, 21, SPR_IMG_LANDSCAPING, STR_022E_LANDSCAPE_GENERATION},
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 474, 495,  0, 21, SPR_IMG_TOWN,        STR_022F_TOWN_GENERATION},
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 496, 517,  0, 21, SPR_IMG_INDUSTRY,    STR_0230_INDUSTRY_GENERATION},
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 518, 539,  0, 21, SPR_IMG_BUILDROAD,   STR_0231_ROAD_CONSTRUCTION},
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 540, 561,  0, 21, SPR_IMG_PLANTTREES,  STR_0288_PLANT_TREES},
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 562, 583,  0, 21, SPR_IMG_SIGN,        STR_0289_PLACE_SIGN},
 

	
 
{   WWT_EMPTY, RESIZE_NONE,  0,   0,   0,  0,  0, 0x0,                 STR_NULL},
 
{   WWT_EMPTY, RESIZE_NONE,  0,   0,   0,  0,  0, 0x0,                 STR_NULL},
 
{   WWT_EMPTY, RESIZE_NONE,  0,   0,   0,  0,  0, 0x0,                 STR_NULL},
 
{   WWT_EMPTY, RESIZE_NONE,  0,   0,   0,  0,  0, 0x0,                 STR_NULL},
 
{   WWT_EMPTY, RESIZE_NONE,  0,   0,   0,  0,  0, 0x0,                 STR_NULL},
 
{   WWT_EMPTY, RESIZE_NONE,  0,   0,   0,  0,  0, 0x0,                 STR_NULL},
 
{   WWT_EMPTY, RESIZE_NONE,  0,   0,   0,  0,  0, 0x0,                 STR_NULL},
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 596, 617,  0, 21, SPR_IMG_MUSIC,       STR_01D4_SHOW_SOUND_MUSIC_WINDOW},
 
{   WWT_EMPTY, RESIZE_NONE,  0,   0,   0,  0,  0, 0x0,                 STR_NULL},
 
{  WWT_IMGBTN, RESIZE_NONE, 14, 618, 639,  0, 21, SPR_IMG_QUERY,       STR_0186_LAND_BLOCK_INFORMATION},
 
{WIDGETS_END},
 
};
 

	
 
static ToolbarButtonProc * const _scen_toolbar_button_procs[] = {
 
	ToolbarPauseClick,
 
	ToolbarFastForwardClick,
 
	ToolbarOptionsClick,
 
	ToolbarScenSaveOrLoad,
 
	ToolbarBtn_NULL,
 
	ToolbarBtn_NULL,
 
	ToolbarScenDateBackward,
 
	ToolbarScenDateForward,
 
	ToolbarScenMapTownDir,
 
	ToolbarScenZoomIn,
 
	ToolbarScenZoomOut,
 
	ToolbarScenGenLand,
 
	ToolbarScenGenTown,
 
	ToolbarScenGenIndustry,
 
	ToolbarScenBuildRoad,
 
	ToolbarScenPlantTrees,
 
	ToolbarScenPlaceSign,
 
	NULL,
 
	NULL,
 
	NULL,
 
	NULL,
 
	NULL,
 
	NULL,
 
	NULL,
 
	ToolbarMusicClick,
 
	NULL,
 
	ToolbarHelpClick,
 
};
 

	
 
static void ScenEditToolbarWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		SetWindowWidgetDisabledState(w, 6, _patches_newgame.starting_year <= MIN_YEAR);
 
		SetWindowWidgetDisabledState(w, 7, _patches_newgame.starting_year >= MAX_YEAR);
 

	
 
		// Draw brown-red toolbar bg.
 
		GfxFillRect(0, 0, w->width-1, w->height-1, 0xB2);
 
		GfxFillRect(0, 0, w->width-1, w->height-1, 0xB4 | PALETTE_MODIFIER_GREYOUT);
 

	
 
		DrawWindowWidgets(w);
 

	
 
		SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
 
		DrawStringCentered(298, 6, STR_00AF, 0);
 

	
 
		SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
 
		DrawStringCentered(161, 1, STR_0221_OPENTTD, 0);
 
		DrawStringCentered(161, 11,STR_0222_SCENARIO_EDITOR, 0);
 

	
 
		break;
 

	
 
	case WE_CLICK: {
 
		if (_game_mode == GM_MENU) return;
 
		_scen_toolbar_button_procs[e->we.click.widget](w);
 
	} break;
 

	
 
	case WE_KEYPRESS:
 
		switch (e->we.keypress.keycode) {
 
			case WKC_F1: ToolbarPauseClick(w); break;
 
			case WKC_F2: ShowGameOptions(); break;
 
			case WKC_F3: MenuClickSaveLoad(0); break;
 
			case WKC_F4: ToolbarScenGenLand(w); break;
 
			case WKC_F5: ToolbarScenGenTown(w); break;
 
			case WKC_F6: ToolbarScenGenIndustry(w); break;
 
			case WKC_F7: ToolbarScenBuildRoad(w); break;
 
			case WKC_F8: ToolbarScenPlantTrees(w); break;
 
			case WKC_F9: ToolbarScenPlaceSign(w); break;
 
			case WKC_F10: ShowMusicWindow(); break;
 
			case WKC_F11: PlaceLandBlockInfo(); break;
 
			case WKC_CTRL | 'S': MenuClickSmallScreenshot(); break;
 
			case WKC_CTRL | 'G': MenuClickWorldScreenshot(); break;
 
			case 'L': ShowEditorTerraformToolBar(); break;
 
			default: return;
 
		}
 
		e->we.keypress.cont = false;
 
		break;
 

	
 
	case WE_PLACE_OBJ: {
 
		_place_proc(e->we.place.tile);
 
	} break;
 

	
 
	case WE_ABORT_PLACE_OBJ: {
 
		RaiseWindowWidget(w, 25);
 
		SetWindowDirty(w);
 
	} break;
 

	
 
	case WE_MOUSELOOP:
 
		if (IsWindowWidgetLowered(w, 0) != !!_pause) {
 
			ToggleWidgetLoweredState(w, 0);
 
			SetWindowDirty(w);
 
		}
 

	
 
		if (IsWindowWidgetLowered(w, 1) != !!_fast_forward) {
 
			ToggleWidgetLoweredState(w, 1);
 
			SetWindowDirty(w);
 
		}
 
		break;
 

	
 
		case WE_MESSAGE:
 
			HandleZoomMessage(w, FindWindowById(WC_MAIN_WINDOW, 0)->viewport, 9, 10);
 
			break;
 
	}
 
}
 

	
 
static const WindowDesc _toolb_scen_desc = {
 
	0, 0, 640, 22,
 
	WC_MAIN_TOOLBAR,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_toolb_scen_widgets,
 
	ScenEditToolbarWndProc
 
};
 

	
 
extern GetNewsStringCallbackProc * const _get_news_string_callback[];
 

	
 

	
 
static bool DrawScrollingStatusText(const NewsItem *ni, int pos)
 
{
 
	char buf[512];
 
	StringID str;
 
	const char *s;
 
	char *d;
 
	DrawPixelInfo tmp_dpi, *old_dpi;
 
	int x;
 
	char buffer[256];
 

	
 
	if (ni->display_mode == 3) {
 
		str = _get_news_string_callback[ni->callback](ni);
 
	} else {
 
		COPY_IN_DPARAM(0, ni->params, lengthof(ni->params));
 
		str = ni->string_id;
 
	}
 

	
 
	GetString(buf, str, lastof(buf));
 

	
 
	s = buf;
 
	d = buffer;
 

	
 
	for (;;) {
 
		WChar c = Utf8Consume(&s);
 
		if (c == 0) {
 
			*d = '\0';
 
			break;
 
		} else if (*s == 0x0D) {
 
			d[0] = d[1] = d[2] = d[3] = ' ';
 
			d += 4;
 
		} else if (IsPrintable(c)) {
 
			d += Utf8Encode(d, c);
 
		}
 
	}
 

	
 
	if (!FillDrawPixelInfo(&tmp_dpi, 141, 1, 358, 11)) return true;
 

	
 
	old_dpi = _cur_dpi;
 
	_cur_dpi = &tmp_dpi;
 

	
 
	x = DoDrawString(buffer, pos, 0, 13);
 
	_cur_dpi = old_dpi;
 

	
 
	return x > 0;
 
}
 

	
 
static void StatusBarWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const Player *p = (_local_player == PLAYER_SPECTATOR) ? NULL : GetPlayer(_local_player);
 

	
 
		DrawWindowWidgets(w);
 
		SetDParam(0, _date);
 
		DrawStringCentered(
 
			70, 1, (_pause || _patches.status_long_date) ? STR_00AF : STR_00AE, 0
 
		);
 

	
 
		if (p != NULL) {
 
			// Draw player money
 
			SetDParam64(0, p->money64);
 
			DrawStringCentered(570, 1, p->player_money >= 0 ? STR_0004 : STR_0005, 0);
 
		}
 

	
 
		// Draw status bar
 
		if (w->message.msg) { // true when saving is active
 
			DrawStringCentered(320, 1, STR_SAVING_GAME, 0);
 
		} else if (_do_autosave) {
 
			DrawStringCentered(320, 1, STR_032F_AUTOSAVE, 0);
 
		} else if (_pause) {
 
			DrawStringCentered(320, 1, STR_0319_PAUSED, 0);
 
		} else if (WP(w,def_d).data_1 > -1280 && FindWindowById(WC_NEWS_WINDOW,0) == NULL && _statusbar_news_item.string_id != 0) {
 
			// Draw the scrolling news text
 
			if (!DrawScrollingStatusText(&_statusbar_news_item, WP(w,def_d).data_1))
 
				WP(w,def_d).data_1 = -1280;
 
		} else {
 
			if (p != NULL) {
 
				// This is the default text
 
				SetDParam(0, p->name_1);
 
				SetDParam(1, p->name_2);
 
				DrawStringCentered(320, 1, STR_02BA, 0);
 
			}
 
		}
 

	
 
		if (WP(w, def_d).data_2 > 0) DrawSprite(SPR_BLOT | PALETTE_TO_RED, 489, 2);
 
	} break;
 

	
 
	case WE_MESSAGE:
 
		w->message.msg = e->we.message.msg;
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
			case 1: ShowLastNewsMessage(); break;
 
			case 2: if (_local_player != PLAYER_SPECTATOR) ShowPlayerFinances(_local_player); break;
 
			default: ResetObjectToPlace();
 
		}
 
		break;
 

	
 
	case WE_TICK: {
 
		if (_pause) return;
 

	
 
		if (WP(w, def_d).data_1 > -1280) { /* Scrolling text */
 
			WP(w, def_d).data_1 -= 2;
 
			InvalidateWidget(w, 1);
 
		}
 

	
 
		if (WP(w, def_d).data_2 > 0) { /* Red blot to show there are new unread newsmessages */
 
			WP(w, def_d).data_2 -= 2;
 
		} else if (WP(w, def_d).data_2 < 0) {
 
			WP(w, def_d).data_2 = 0;
 
			InvalidateWidget(w, 1);
 
		}
 

	
 
		break;
 
	}
 
	}
 
}
 

	
 
static const Widget _main_status_widgets[] = {
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   139,     0,    11, 0x0, STR_NULL},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   140,   499,     0,    11, 0x0, STR_02B7_SHOW_LAST_MESSAGE_OR_NEWS},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   500,   639,     0,    11, 0x0, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static WindowDesc _main_status_desc = {
 
	WDP_CENTER, 0, 640, 12,
 
	WC_STATUS_BAR,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_main_status_widgets,
 
	StatusBarWndProc
 
};
 

	
 
extern void UpdateAllStationVirtCoord(void);
 

	
 
static void MainWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	int off_x;
 

	
 
	switch (e->event) {
 
	case WE_PAINT:
 
		DrawWindowViewport(w);
 
		if (_game_mode == GM_MENU) {
 
			off_x = _screen.width / 2;
 

	
 
			DrawSprite(SPR_OTTD_O, off_x - 120, 50);
 
			DrawSprite(SPR_OTTD_P, off_x -  86, 50);
 
			DrawSprite(SPR_OTTD_E, off_x -  53, 50);
 
			DrawSprite(SPR_OTTD_N, off_x -  22, 50);
 

	
 
			DrawSprite(SPR_OTTD_T, off_x +  34, 50);
 
			DrawSprite(SPR_OTTD_T, off_x +  65, 50);
 
			DrawSprite(SPR_OTTD_D, off_x +  96, 50);
 

	
 
			/*
 
			DrawSprite(SPR_OTTD_R, off_x + 119, 50);
 
			DrawSprite(SPR_OTTD_A, off_x + 148, 50);
 
			DrawSprite(SPR_OTTD_N, off_x + 181, 50);
 
			DrawSprite(SPR_OTTD_S, off_x + 215, 50);
 
			DrawSprite(SPR_OTTD_P, off_x + 246, 50);
 
			DrawSprite(SPR_OTTD_O, off_x + 275, 50);
 
			DrawSprite(SPR_OTTD_R, off_x + 307, 50);
 
			DrawSprite(SPR_OTTD_T, off_x + 337, 50);
 

	
 
			DrawSprite(SPR_OTTD_T, off_x + 390, 50);
 
			DrawSprite(SPR_OTTD_Y, off_x + 417, 50);
 
			DrawSprite(SPR_OTTD_C, off_x + 447, 50);
 
			DrawSprite(SPR_OTTD_O, off_x + 478, 50);
 
			DrawSprite(SPR_OTTD_O, off_x + 509, 50);
 
			DrawSprite(SPR_OTTD_N, off_x + 541, 50);
 
			*/
 
		}
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		switch (e->we.keypress.keycode) {
 
			case 'Q' | WKC_CTRL:
 
			case 'Q' | WKC_META:
 
				HandleExitGameRequest();
 
				break;
 
		}
 

	
 
		/* Disable all key shortcuts, except quit shortcuts when
 
		 * generating the world, otherwise they create threading
 
		 * problem during the generating, resulting in random
 
		 * assertions that are hard to trigger and debug */
 
		if (IsGeneratingWorld()) break;
 

	
 
		if (e->we.keypress.keycode == WKC_BACKQUOTE) {
 
			IConsoleSwitch();
 
			e->we.keypress.cont = false;
 
			break;
 
		}
 

	
 
		if (_game_mode == GM_MENU) break;
 

	
 
		switch (e->we.keypress.keycode) {
 
			case 'C':
 
			case 'Z': {
 
				Point pt = GetTileBelowCursor();
 
				if (pt.x != -1) {
 
					ScrollMainWindowTo(pt.x, pt.y);
 
					if (e->we.keypress.keycode == 'Z') MaxZoomInOut(ZOOM_IN, w);
 
				}
 
				break;
 
			}
 

	
 
			case WKC_ESC: ResetObjectToPlace(); break;
 
			case WKC_DELETE: DeleteNonVitalWindows(); break;
 
			case WKC_DELETE | WKC_SHIFT: DeleteAllNonVitalWindows(); break;
 
			case 'R' | WKC_CTRL: MarkWholeScreenDirty(); break;
 

	
 
#if defined(_DEBUG)
 
			case '0' | WKC_ALT: /* Crash the game */
 
				*(byte*)0 = 0;
 
				break;
 

	
 
			case '1' | WKC_ALT: /* Gimme money */
 
				/* Server can not cheat in advertise mode either! */
 
				if (!_networking || !_network_server || !_network_advertise)
 
					DoCommandP(0, -10000000, 0, NULL, CMD_MONEY_CHEAT);
 
				break;
 

	
 
			case '2' | WKC_ALT: /* Update the coordinates of all station signs */
 
				UpdateAllStationVirtCoord();
 
				break;
 
#endif
 

	
 
			case 'X':
 
				_display_opt ^= DO_TRANS_BUILDINGS;
 
				MarkWholeScreenDirty();
 
				break;
 

	
 
#ifdef ENABLE_NETWORK
 
			case WKC_RETURN: case 'T': // smart chat; send to team if any, otherwise to all
 
				if (_networking) {
 
					const NetworkClientInfo *cio = NetworkFindClientInfoFromIndex(_network_own_client_index);
 
					bool teamchat = false;
 

	
 
					/* Only players actually playing can speak to team. Eg spectators cannot */
 
					if (_patches.prefer_teamchat && IsValidPlayer(cio->client_playas)) {
 
						const NetworkClientInfo *ci;
 
						FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
 
							if (ci->client_playas == cio->client_playas && ci != cio) {
 
								teamchat = true;
 
								break;
 
							}
 
						}
 
					}
 

	
 
					ShowNetworkChatQueryWindow(teamchat ? DESTTYPE_TEAM : DESTTYPE_BROADCAST, cio->client_playas);
 
				}
 
				break;
 

	
 
			case WKC_SHIFT | WKC_RETURN: case WKC_SHIFT | 'T': // send text message to all players
 
				if (_networking) ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0);
 
				break;
 

	
 
			case WKC_CTRL | WKC_RETURN: case WKC_CTRL | 'T': // send text to all team mates
 
				if (_networking) {
 
					const NetworkClientInfo *ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
 
					ShowNetworkChatQueryWindow(DESTTYPE_TEAM, ci->client_playas);
 
				}
 
				break;
 
#endif
 

	
 
			default: return;
 
		}
 
		e->we.keypress.cont = false;
 
		break;
 

	
 
		case WE_SCROLL: {
 
			ViewPort *vp = IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y);
 

	
 
			if (vp == NULL) {
 
				_cursor.fix_at = false;
 
				_scrolling_viewport = false;
 
			}
 

	
 
			WP(w, vp_d).scrollpos_x += e->we.scroll.delta.x << vp->zoom;
 
			WP(w, vp_d).scrollpos_y += e->we.scroll.delta.y << vp->zoom;
 
		} break;
 

	
 
		case WE_MOUSEWHEEL:
 
			ZoomInOrOutToCursorWindow(e->we.wheel.wheel < 0, w);
 
			break;
 

	
 
		case WE_MESSAGE:
 
			/* Forward the message to the appropiate toolbar (ingame or scenario editor) */
 
			SendWindowMessage(WC_MAIN_TOOLBAR, 0, e->we.message.msg, e->we.message.wparam, e->we.message.lparam);
 
			break;
 
	}
 
}
 

	
 

	
 
void ShowSelectGameWindow(void);
 

	
 
void SetupColorsAndInitialWindow(void)
 
{
 
	uint i;
 
	Window *w;
 
	int width, height;
 

	
 
	for (i = 0; i != 16; i++) {
 
		const byte *b = GetNonSprite(PALETTE_RECOLOR_START + i);
 

	
 
		assert(b);
 
		memcpy(_colour_gradient[i], b + 0xC6, sizeof(_colour_gradient[i]));
 
	}
 

	
 
	width = _screen.width;
 
	height = _screen.height;
 

	
 
	w = AllocateWindow(0, 0, width, height, MainWindowWndProc, WC_MAIN_WINDOW, NULL);
 
	AssignWindowViewport(w, 0, 0, width, height, TileXY(32, 32), 0);
 

	
 
	// XXX: these are not done
 
	switch (_game_mode) {
 
		default: NOT_REACHED();
 
		case GM_MENU:
 
			ShowSelectGameWindow();
 
			break;
 

	
 
		case GM_NORMAL:
 
		case GM_EDITOR:
 
			ShowVitalWindows();
 
			break;
 
	}
 
}
 

	
 
void ShowVitalWindows(void)
 
{
 
	Window *w;
 

	
 
	w = AllocateWindowDesc((_game_mode != GM_EDITOR) ? &_toolb_normal_desc : &_toolb_scen_desc);
 
	DoZoomInOutWindow(ZOOM_NONE, w);
 

	
 
	CLRBITS(w->flags4, WF_WHITE_BORDER_MASK);
 

	
 
	SetWindowWidgetDisabledState(w, 0, _networking && !_network_server); // if not server, disable pause button
 
	SetWindowWidgetDisabledState(w, 1, _networking); // if networking, disable fast-forward button
 

	
 
	/* 'w' is for sure a WC_MAIN_TOOLBAR */
 
	PositionMainToolbar(w);
 

	
 
	/* Status bad only for normal games */
 
	if (_game_mode == GM_EDITOR) return;
 

	
 
	_main_status_desc.top = _screen.height - 12;
 
	w = AllocateWindowDesc(&_main_status_desc);
 
	CLRBITS(w->flags4, WF_WHITE_BORDER_MASK);
 

	
 
	WP(w,def_d).data_1 = -1280;
 
}
 

	
 
void GameSizeChanged(void)
 
{
 
	_cur_resolution[0] = _screen.width;
 
	_cur_resolution[1] = _screen.height;
 
	RelocateAllWindows(_screen.width, _screen.height);
 
	ScreenSizeChanged();
 
	MarkWholeScreenDirty();
 
}
 

	
 
void InitializeMainGui(void)
 
{
 
	/* Clean old GUI values */
 
	_last_built_railtype = 0;
 
}
 

	
 

	
src/map.c
Show inline comments
 
deleted file
src/map.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "macros.h"
 
#include "map.h"
 
#include "direction.h"
 

	
 
#if defined(_MSC_VER) && _MSC_VER >= 1400 /* VStudio 2005 is stupid! */
 
/* Why the hell is that not in all MSVC headers?? */
 
_CRTIMP void __cdecl _assert(void *, void *, unsigned);
 
#endif
 

	
 
uint _map_log_x;
 
uint _map_size_x;
 
uint _map_size_y;
 
uint _map_tile_mask;
 
uint _map_size;
 

	
 
Tile* _m = NULL;
 

	
 

	
 
void AllocateMap(uint size_x, uint size_y)
 
{
 
	// Make sure that the map size is within the limits and that
 
	// the x axis size is a power of 2.
 
	if (size_x < 64 || size_x > 2048 ||
 
			size_y < 64 || size_y > 2048 ||
 
			(size_x&(size_x-1)) != 0 ||
 
			(size_y&(size_y-1)) != 0)
 
		error("Invalid map size");
 

	
 
	DEBUG(map, 1, "Allocating map of size %dx%d", size_x, size_y);
 

	
 
	_map_log_x = FindFirstBit(size_x);
 
	_map_size_x = size_x;
 
	_map_size_y = size_y;
 
	_map_size = size_x * size_y;
 
	_map_tile_mask = _map_size - 1;
 

	
 
	free(_m);
 
	_m = calloc(_map_size, sizeof(*_m));
 

	
 
	// XXX TODO handle memory shortage more gracefully
 
	if (_m == NULL) error("Failed to allocate memory for the map");
 
}
 

	
 

	
 
#ifdef _DEBUG
 
TileIndex TileAdd(TileIndex tile, TileIndexDiff add,
 
	const char *exp, const char *file, int line)
 
{
 
	int dx;
 
	int dy;
 
	uint x;
 
	uint y;
 

	
 
	dx = add & MapMaxX();
 
	if (dx >= (int)MapSizeX() / 2) dx -= MapSizeX();
 
	dy = (add - dx) / (int)MapSizeX();
 

	
 
	x = TileX(tile) + dx;
 
	y = TileY(tile) + dy;
 

	
 
	if (x >= MapSizeX() || y >= MapSizeY()) {
 
		char buf[512];
 

	
 
		snprintf(buf, lengthof(buf), "TILE_ADD(%s) when adding 0x%.4X and 0x%.4X failed",
 
			exp, tile, add);
 
#if !defined(_MSC_VER)
 
		fprintf(stderr, "%s:%d %s\n", file, line, buf);
 
#else
 
		_assert(buf, (char*)file, line);
 
#endif
 
	}
 

	
 
	assert(TileXY(x,y) == TILE_MASK(tile + add));
 

	
 
	return TileXY(x,y);
 
}
 
#endif
 

	
 

	
 
uint ScaleByMapSize(uint n)
 
{
 
	// First shift by 12 to prevent integer overflow for large values of n.
 
	// >>12 is safe since the min mapsize is 64x64
 
	// Add (1<<4)-1 to round upwards.
 
	return (n * (MapSize() >> 12) + (1<<4) - 1) >> 4;
 
}
 

	
 

	
 
// Scale relative to the circumference of the map
 
uint ScaleByMapSize1D(uint n)
 
{
 
	// Normal circumference for the X+Y is 256+256 = 1<<9
 
	// Note, not actually taking the full circumference into account,
 
	// just half of it.
 
	// (1<<9) - 1 is there to scale upwards.
 
	return (n * (MapSizeX() + MapSizeY()) + (1<<9) - 1) >> 9;
 
}
 

	
 

	
 
// This function checks if we add addx/addy to tile, if we
 
//  do wrap around the edges. For example, tile = (10,2) and
 
//  addx = +3 and addy = -4. This function will now return
 
//  INVALID_TILE, because the y is wrapped. This is needed in
 
//  for example, farmland. When the tile is not wrapped,
 
//  the result will be tile + TileDiffXY(addx, addy)
 
uint TileAddWrap(TileIndex tile, int addx, int addy)
 
{
 
	uint x = TileX(tile) + addx;
 
	uint y = TileY(tile) + addy;
 

	
 
	// Are we about to wrap?
 
	if (x < MapMaxX() && y < MapMaxY())
 
		return tile + TileDiffXY(addx, addy);
 

	
 
	return INVALID_TILE;
 
}
 

	
 
const TileIndexDiffC _tileoffs_by_diagdir[] = {
 
	{-1,  0}, // DIAGDIR_NE
 
	{ 0,  1}, // DIAGDIR_SE
 
	{ 1,  0}, // DIAGDIR_SW
 
	{ 0, -1}  // DIAGDIR_NW
 
};
 

	
 
const TileIndexDiffC _tileoffs_by_dir[] = {
 
	{-1, -1}, // DIR_N
 
	{-1,  0}, // DIR_NE
 
	{-1,  1}, // DIR_E
 
	{ 0,  1}, // DIR_SE
 
	{ 1,  1}, // DIR_S
 
	{ 1,  0}, // DIR_SW
 
	{ 1, -1}, // DIR_W
 
	{ 0, -1}  // DIR_NW
 
};
 

	
 
uint DistanceManhattan(TileIndex t0, TileIndex t1)
 
{
 
	const uint dx = abs(TileX(t0) - TileX(t1));
 
	const uint dy = abs(TileY(t0) - TileY(t1));
 
	return dx + dy;
 
}
 

	
 

	
 
uint DistanceSquare(TileIndex t0, TileIndex t1)
 
{
 
	const int dx = TileX(t0) - TileX(t1);
 
	const int dy = TileY(t0) - TileY(t1);
 
	return dx * dx + dy * dy;
 
}
 

	
 

	
 
uint DistanceMax(TileIndex t0, TileIndex t1)
 
{
 
	const uint dx = abs(TileX(t0) - TileX(t1));
 
	const uint dy = abs(TileY(t0) - TileY(t1));
 
	return dx > dy ? dx : dy;
 
}
 

	
 

	
 
uint DistanceMaxPlusManhattan(TileIndex t0, TileIndex t1)
 
{
 
	const uint dx = abs(TileX(t0) - TileX(t1));
 
	const uint dy = abs(TileY(t0) - TileY(t1));
 
	return dx > dy ? 2 * dx + dy : 2 * dy + dx;
 
}
 

	
 
uint DistanceFromEdge(TileIndex tile)
 
{
 
	const uint xl = TileX(tile);
 
	const uint yl = TileY(tile);
 
	const uint xh = MapSizeX() - 1 - xl;
 
	const uint yh = MapSizeY() - 1 - yl;
 
	const uint minl = xl < yl ? xl : yl;
 
	const uint minh = xh < yh ? xh : yh;
 
	return minl < minh ? minl : minh;
 
}
 

	
 
/**
 
 * Function performing a search around a center tile and going outward, thus in circle.
 
 * Although it really is a square search...
 
 * Every tile will be tested by means of the callback function proc,
 
 * which will determine if yes or no the given tile meets criteria of search.
 
 * @param tile to start the search from
 
 * @param size: number of tiles per side of the desired search area
 
 * @param proc: callback testing function pointer.
 
 * @param data to be passed to the callback function. Depends on the implementation
 
 * @result of the search
 
 * @pre proc != NULL
 
 * @pre size > 0
 
 */
 
bool CircularTileSearch(TileIndex tile, uint size, TestTileOnSearchProc proc, uint32 data)
 
{
 
	uint n, x, y;
 
	DiagDirection dir;
 

	
 
	assert(proc != NULL);
 
	assert(size > 0);
 

	
 
	x = TileX(tile);
 
	y = TileY(tile);
 

	
 
	if (size % 2 == 1) {
 
		/* If the length of the side is uneven, the center has to be checked
 
		 * separately, as the pattern of uneven sides requires to go around the center */
 
		n = 2;
 
		if (proc(TileXY(x, y), data)) return true;
 

	
 
		/* If tile test is not successfull, get one tile down and left,
 
		 * ready for a test in first circle around center tile */
 
		x += _tileoffs_by_dir[DIR_W].x;
 
		y += _tileoffs_by_dir[DIR_W].y;
 
	} else {
 
		n = 1;
 
		/* To use _tileoffs_by_diagdir's order, we must relocate to
 
		 * another tile, as we now first go 'up', 'right', 'down', 'left'
 
		 * instead of 'right', 'down', 'left', 'up', which the calling
 
		 * function assume. */
 
		x++;
 
	}
 

	
 
	for (; n < size; n += 2) {
 
		for (dir = DIAGDIR_NE; dir < DIAGDIR_END; dir++) {
 
			uint j;
 
			for (j = n; j != 0; j--) {
 
				if (x <= MapMaxX() && y <= MapMaxY() && ///< Is the tile within the map?
 
						proc(TileXY(x, y), data)) {     ///< Is the callback successfulll?
 
					return true;                        ///< then stop the search
 
				}
 

	
 
				/* Step to the next 'neighbour' in the circular line */
 
				x += _tileoffs_by_diagdir[dir].x;
 
				y += _tileoffs_by_diagdir[dir].y;
 
			}
 
		}
 
		/* Jump to next circle to test */
 
		x += _tileoffs_by_dir[DIR_W].x;
 
		y += _tileoffs_by_dir[DIR_W].y;
 
	}
 
	return false;
 
}
src/md5.c
Show inline comments
 
deleted file
src/md5.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/*
 
  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
 

	
 
  This software is provided 'as-is', without any express or implied
 
  warranty.  In no event will the authors be held liable for any damages
 
  arising from the use of this software.
 

	
 
  Permission is granted to anyone to use this software for any purpose,
 
  including commercial applications, and to alter it and redistribute it
 
  freely, subject to the following restrictions:
 

	
 
  1. The origin of this software must not be misrepresented; you must not
 
     claim that you wrote the original software. If you use this software
 
     in a product, an acknowledgment in the product documentation would be
 
     appreciated but is not required.
 
  2. Altered source versions must be plainly marked as such, and must not be
 
     misrepresented as being the original software.
 
  3. This notice may not be removed or altered from any source distribution.
 

	
 
  L. Peter Deutsch
 
  ghost@aladdin.com
 

	
 
 */
 
/* $Id$ */
 
/*
 
  Independent implementation of MD5 (RFC 1321).
 

	
 
  This code implements the MD5 Algorithm defined in RFC 1321, whose
 
  text is available at
 
	http://www.ietf.org/rfc/rfc1321.txt
 
  The code is derived from the text of the RFC, including the test suite
 
  (section A.5) but excluding the rest of Appendix A.  It does not include
 
  any code or documentation that is identified in the RFC as being
 
  copyrighted.
 

	
 
  The original and principal author of md5.c is L. Peter Deutsch
 
  <ghost@aladdin.com>.  Other authors are noted in the change history
 
  that follows (in reverse chronological order):
 

	
 
  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
 
	either statically or dynamically; added missing #include <string.h>
 
	in library.
 
  2002-03-11 lpd Corrected argument list for main(), and added int return
 
	type, in test program and T value program.
 
  2002-02-21 lpd Added missing #include <stdio.h> in test program.
 
  2000-07-03 lpd Patched to eliminate warnings about "constant is
 
	unsigned in ANSI C, signed in traditional"; made test program
 
	self-checking.
 
  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
 
  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
 
  1999-05-03 lpd Original version.
 
 */
 

	
 
#include "stdafx.h"
 
#include "md5.h"
 
#include <string.h>
 

	
 
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
 
#if defined(TTD_BIG_ENDIAN)
 
#  define BYTE_ORDER 1
 
#else
 
#  define BYTE_ORDER -1
 
#endif
 

	
 
#define T_MASK ((md5_word_t)~0)
 
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
 
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
 
#define T3    0x242070db
 
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
 
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
 
#define T6    0x4787c62a
 
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
 
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
 
#define T9    0x698098d8
 
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
 
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
 
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
 
#define T13    0x6b901122
 
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
 
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
 
#define T16    0x49b40821
 
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
 
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
 
#define T19    0x265e5a51
 
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
 
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
 
#define T22    0x02441453
 
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
 
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
 
#define T25    0x21e1cde6
 
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
 
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
 
#define T28    0x455a14ed
 
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
 
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
 
#define T31    0x676f02d9
 
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
 
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
 
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
 
#define T35    0x6d9d6122
 
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
 
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
 
#define T38    0x4bdecfa9
 
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
 
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
 
#define T41    0x289b7ec6
 
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
 
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
 
#define T44    0x04881d05
 
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
 
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
 
#define T47    0x1fa27cf8
 
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
 
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
 
#define T50    0x432aff97
 
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
 
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
 
#define T53    0x655b59c3
 
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
 
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
 
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
 
#define T57    0x6fa87e4f
 
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
 
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
 
#define T60    0x4e0811a1
 
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
 
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
 
#define T63    0x2ad7d2bb
 
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
 

	
 

	
 
static void
 
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
 
{
 
    md5_word_t
 
	a = pms->abcd[0], b = pms->abcd[1],
 
	c = pms->abcd[2], d = pms->abcd[3];
 
    md5_word_t t;
 
#if BYTE_ORDER > 0
 
    /* Define storage only for big-endian CPUs. */
 
    md5_word_t X[16];
 
#else
 
    /* Define storage for little-endian or both types of CPUs. */
 
    md5_word_t xbuf[16];
 
    const md5_word_t *X;
 
#endif
 

	
 
    {
 
#if BYTE_ORDER == 0
 
	/*
 
	 * Determine dynamically whether this is a big-endian or
 
	 * little-endian machine, since we can use a more efficient
 
	 * algorithm on the latter.
 
	 */
 
	static const int w = 1;
 

	
 
	if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
 
#endif
 
#if BYTE_ORDER <= 0 /* little-endian */
 
	{
 
	    /*
 
	     * On little-endian machines, we can process properly aligned
 
	     * data without copying it.
 
	     */
 
	    if (!((data - (const md5_byte_t *)0) & 3)) {
 
		/* data are properly aligned */
 
		X = (const md5_word_t *)data;
 
	    } else {
 
		/* not aligned */
 
		memcpy(xbuf, data, 64);
 
		X = xbuf;
 
	    }
 
	}
 
#endif
 
#if BYTE_ORDER == 0
 
	else /* dynamic big-endian */
 
#endif
 
#if BYTE_ORDER >= 0 /* big-endian */
 
	{
 
	    /*
 
	     * On big-endian machines, we must arrange the bytes in the
 
	     * right order.
 
	     */
 
	    const md5_byte_t *xp = data;
 
	    int i;
 

	
 
#  if BYTE_ORDER == 0
 
	    X = xbuf; /* (dynamic only) */
 
#  else
 
#    define xbuf X /* (static only) */
 
#  endif
 
	    for (i = 0; i < 16; ++i, xp += 4)
 
		xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
 
	}
 
#endif
 
    }
 

	
 
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
 

	
 
    /* Round 1. */
 
    /* Let [abcd k s i] denote the operation
 
       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
 
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
 
#define SET(a, b, c, d, k, s, Ti)\
 
  t = a + F(b,c,d) + X[k] + Ti;\
 
  a = ROTATE_LEFT(t, s) + b
 
    /* Do the following 16 operations. */
 
    SET(a, b, c, d,  0,  7,  T1);
 
    SET(d, a, b, c,  1, 12,  T2);
 
    SET(c, d, a, b,  2, 17,  T3);
 
    SET(b, c, d, a,  3, 22,  T4);
 
    SET(a, b, c, d,  4,  7,  T5);
 
    SET(d, a, b, c,  5, 12,  T6);
 
    SET(c, d, a, b,  6, 17,  T7);
 
    SET(b, c, d, a,  7, 22,  T8);
 
    SET(a, b, c, d,  8,  7,  T9);
 
    SET(d, a, b, c,  9, 12, T10);
 
    SET(c, d, a, b, 10, 17, T11);
 
    SET(b, c, d, a, 11, 22, T12);
 
    SET(a, b, c, d, 12,  7, T13);
 
    SET(d, a, b, c, 13, 12, T14);
 
    SET(c, d, a, b, 14, 17, T15);
 
    SET(b, c, d, a, 15, 22, T16);
 
#undef SET
 

	
 
     /* Round 2. */
 
     /* Let [abcd k s i] denote the operation
 
          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
 
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
 
#define SET(a, b, c, d, k, s, Ti)\
 
  t = a + G(b,c,d) + X[k] + Ti;\
 
  a = ROTATE_LEFT(t, s) + b
 
     /* Do the following 16 operations. */
 
    SET(a, b, c, d,  1,  5, T17);
 
    SET(d, a, b, c,  6,  9, T18);
 
    SET(c, d, a, b, 11, 14, T19);
 
    SET(b, c, d, a,  0, 20, T20);
 
    SET(a, b, c, d,  5,  5, T21);
 
    SET(d, a, b, c, 10,  9, T22);
 
    SET(c, d, a, b, 15, 14, T23);
 
    SET(b, c, d, a,  4, 20, T24);
 
    SET(a, b, c, d,  9,  5, T25);
 
    SET(d, a, b, c, 14,  9, T26);
 
    SET(c, d, a, b,  3, 14, T27);
 
    SET(b, c, d, a,  8, 20, T28);
 
    SET(a, b, c, d, 13,  5, T29);
 
    SET(d, a, b, c,  2,  9, T30);
 
    SET(c, d, a, b,  7, 14, T31);
 
    SET(b, c, d, a, 12, 20, T32);
 
#undef SET
 

	
 
     /* Round 3. */
 
     /* Let [abcd k s t] denote the operation
 
          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
 
#define H(x, y, z) ((x) ^ (y) ^ (z))
 
#define SET(a, b, c, d, k, s, Ti)\
 
  t = a + H(b,c,d) + X[k] + Ti;\
 
  a = ROTATE_LEFT(t, s) + b
 
     /* Do the following 16 operations. */
 
    SET(a, b, c, d,  5,  4, T33);
 
    SET(d, a, b, c,  8, 11, T34);
 
    SET(c, d, a, b, 11, 16, T35);
 
    SET(b, c, d, a, 14, 23, T36);
 
    SET(a, b, c, d,  1,  4, T37);
 
    SET(d, a, b, c,  4, 11, T38);
 
    SET(c, d, a, b,  7, 16, T39);
 
    SET(b, c, d, a, 10, 23, T40);
 
    SET(a, b, c, d, 13,  4, T41);
 
    SET(d, a, b, c,  0, 11, T42);
 
    SET(c, d, a, b,  3, 16, T43);
 
    SET(b, c, d, a,  6, 23, T44);
 
    SET(a, b, c, d,  9,  4, T45);
 
    SET(d, a, b, c, 12, 11, T46);
 
    SET(c, d, a, b, 15, 16, T47);
 
    SET(b, c, d, a,  2, 23, T48);
 
#undef SET
 

	
 
     /* Round 4. */
 
     /* Let [abcd k s t] denote the operation
 
          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
 
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
 
#define SET(a, b, c, d, k, s, Ti)\
 
  t = a + I(b,c,d) + X[k] + Ti;\
 
  a = ROTATE_LEFT(t, s) + b
 
     /* Do the following 16 operations. */
 
    SET(a, b, c, d,  0,  6, T49);
 
    SET(d, a, b, c,  7, 10, T50);
 
    SET(c, d, a, b, 14, 15, T51);
 
    SET(b, c, d, a,  5, 21, T52);
 
    SET(a, b, c, d, 12,  6, T53);
 
    SET(d, a, b, c,  3, 10, T54);
 
    SET(c, d, a, b, 10, 15, T55);
 
    SET(b, c, d, a,  1, 21, T56);
 
    SET(a, b, c, d,  8,  6, T57);
 
    SET(d, a, b, c, 15, 10, T58);
 
    SET(c, d, a, b,  6, 15, T59);
 
    SET(b, c, d, a, 13, 21, T60);
 
    SET(a, b, c, d,  4,  6, T61);
 
    SET(d, a, b, c, 11, 10, T62);
 
    SET(c, d, a, b,  2, 15, T63);
 
    SET(b, c, d, a,  9, 21, T64);
 
#undef SET
 

	
 
     /* Then perform the following additions. (That is increment each
 
        of the four registers by the value it had before this block
 
        was started.) */
 
    pms->abcd[0] += a;
 
    pms->abcd[1] += b;
 
    pms->abcd[2] += c;
 
    pms->abcd[3] += d;
 
}
 

	
 
void
 
md5_init(md5_state_t *pms)
 
{
 
    pms->count[0] = pms->count[1] = 0;
 
    pms->abcd[0] = 0x67452301;
 
    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
 
    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
 
    pms->abcd[3] = 0x10325476;
 
}
 

	
 
void
 
md5_append(md5_state_t *pms, const void *data, size_t nbytes)
 
{
 
    const md5_byte_t *p = (const md5_byte_t *)data;
 
    size_t left = nbytes;
 
    size_t offset = (pms->count[0] >> 3) & 63;
 
    md5_word_t nbits = (md5_word_t)(nbytes << 3);
 

	
 
    if (nbytes <= 0)
 
	return;
 

	
 
    /* Update the message length. */
 
    pms->count[1] += (md5_word_t)(nbytes >> 29);
 
    pms->count[0] += nbits;
 
    if (pms->count[0] < nbits)
 
	pms->count[1]++;
 

	
 
    /* Process an initial partial block. */
 
    if (offset) {
 
	size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
 

	
 
	memcpy(pms->buf + offset, p, copy);
 
	if (offset + copy < 64)
 
	    return;
 
	p += copy;
 
	left -= copy;
 
	md5_process(pms, pms->buf);
 
    }
 

	
 
    /* Process full blocks. */
 
    for (; left >= 64; p += 64, left -= 64)
 
	md5_process(pms, p);
 

	
 
    /* Process a final partial block. */
 
    if (left)
 
	memcpy(pms->buf, p, left);
 
}
 

	
 
void
 
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
 
{
 
    static const md5_byte_t pad[64] = {
 
	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 
    };
 
    md5_byte_t data[8];
 
    int i;
 

	
 
    /* Save the length before padding. */
 
    for (i = 0; i < 8; ++i)
 
	data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
 
    /* Pad to 56 bytes mod 64. */
 
    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
 
    /* Append the length. */
 
    md5_append(pms, data, 8);
 
    for (i = 0; i < 16; ++i)
 
	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
 
}
src/mersenne.c
Show inline comments
 
deleted file
src/mersenne.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 

	
 
#ifdef MERSENNE_TWISTER
 

	
 
// Source code for Mersenne Twister.
 
// A Random number generator with much higher quality random numbers.
 

	
 
#define N              (624)                 // length of _mt_state vector
 
#define M              (397)                 // a period parameter
 
#define K              (0x9908B0DFU)         // a magic constant
 
#define hiBit(u)       ((u) & 0x80000000U)   // mask all but highest   bit of u
 
#define loBit(u)       ((u) & 0x00000001U)   // mask all but lowest    bit of u
 
#define loBits(u)      ((u) & 0x7FFFFFFFU)   // mask     the highest   bit of u
 
#define mixBits(u, v)  (hiBit(u)|loBits(v))  // move hi bit of u to hi bit of v
 

	
 
static uint32   _mt_state[N+1];     // _mt_state vector + 1 extra to not violate ANSI C
 
static uint32   *_mt_next;          // _mt_next random value is computed from here
 
static int      _mt_left = -1;      // can *_mt_next++ this many times before reloading
 

	
 
void SeedMT(uint32 seed)
 
{
 
    register uint32 x = (seed | 1U) & 0xFFFFFFFFU, *s = _mt_state;
 
    register int    j;
 

	
 
    for (_mt_left=0, *s++=x, j=N; --j;
 
        *s++ = (x*=69069U) & 0xFFFFFFFFU);
 
 }
 

	
 

	
 
static uint32 ReloadMT(void)
 
 {
 
    register uint32 *p0=_mt_state, *p2=_mt_state+2, *pM=_mt_state+M, s0, s1;
 
    register int    j;
 

	
 
    if (_mt_left < -1)
 
        SeedMT(4357U);
 

	
 
    _mt_left=N-1, _mt_next=_mt_state+1;
 

	
 
    for (s0=_mt_state[0], s1=_mt_state[1], j=N-M+1; --j; s0=s1, s1=*p2++)
 
        *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
 

	
 
    for (pM=_mt_state, j=M; --j; s0=s1, s1=*p2++)
 
        *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
 

	
 
    s1=_mt_state[0], *p0 = *pM ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
 
    s1 ^= (s1 >> 11);
 
    s1 ^= (s1 <<  7) & 0x9D2C5680U;
 
    s1 ^= (s1 << 15) & 0xEFC60000U;
 
    return(s1 ^ (s1 >> 18));
 
 }
 

	
 

	
 
uint32 RandomMT(void)
 
{
 
	uint32 y;
 

	
 
	if (--_mt_left < 0)
 
		return ReloadMT();
 

	
 
	y  = *_mt_next++;
 
	y ^= (y >> 11);
 
	y ^= (y <<  7) & 0x9D2C5680U;
 
	y ^= (y << 15) & 0xEFC60000U;
 
	return y ^ (y >> 18);
 
}
 
#else
 

	
 
void SeedMT(uint32 seed) {}
 

	
 
#endif /* MERSENNE_TWISTER */
src/minilzo.c
Show inline comments
 
deleted file
src/minilzo.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/* minilzo.c -- mini subset of the LZO real-time data compression library
 

	
 
   This file is part of the LZO real-time data compression library.
 

	
 
   Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
 
   Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
 
   Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
 
   Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
 
   Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
 
   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
 
   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
 
   All Rights Reserved.
 

	
 
   The LZO library 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; either version 2 of
 
   the License, or (at your option) any later version.
 

	
 
   The LZO library 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 the LZO library; see the file COPYING.
 
   If not, write to the Free Software Foundation, Inc.,
 
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 

	
 
   Markus F.X.J. Oberhumer
 
   <markus@oberhumer.com>
 
   http://www.oberhumer.com/opensource/lzo/
 
 */
 

	
 
/*
 
 * NOTE:
 
 *   the full LZO package can be found at
 
 *   http://www.oberhumer.com/opensource/lzo/
 
 */
 

	
 
#define LZO_DISABLE_CHECKS
 
#define LZO1X
 

	
 
#define __LZO_IN_MINILZO
 
#define LZO_BUILD
 

	
 
#ifdef MINILZO_HAVE_CONFIG_H
 
#  include <config.h>
 
#endif
 

	
 
#undef LZO_HAVE_CONFIG_H
 
#include "minilzo.h"
 

	
 
#if !defined(MINILZO_VERSION) || (MINILZO_VERSION != 0x1080)
 
#  error "version mismatch in miniLZO source files"
 
#endif
 

	
 
#ifdef MINILZO_HAVE_CONFIG_H
 
#  define LZO_HAVE_CONFIG_H
 
#endif
 

	
 
#if !defined(LZO_NO_SYS_TYPES_H)
 
#  include <sys/types.h>
 
#endif
 
#include <stdio.h>
 

	
 
#ifndef __LZO_CONF_H
 
#define __LZO_CONF_H
 

	
 
#if defined(__BOUNDS_CHECKING_ON)
 
#  include <unchecked.h>
 
#else
 
#  define BOUNDS_CHECKING_OFF_DURING(stmt)	  stmt
 
#  define BOUNDS_CHECKING_OFF_IN_EXPR(expr)	 (expr)
 
#endif
 

	
 
#if !defined(LZO_HAVE_CONFIG_H)
 
#  include <stddef.h>
 
#  include <string.h>
 
#  if !defined(NO_STDLIB_H)
 
#	include <stdlib.h>
 
#  endif
 
#  define HAVE_MEMCMP
 
#  define HAVE_MEMCPY
 
#  define HAVE_MEMMOVE
 
#  define HAVE_MEMSET
 
#else
 
#  include <sys/types.h>
 
#  if defined(HAVE_STDDEF_H)
 
#	include <stddef.h>
 
#  endif
 
#  if defined(STDC_HEADERS)
 
#	include <string.h>
 
#	include <stdlib.h>
 
#  endif
 
#endif
 

	
 
#if defined(__LZO_DOS16) || defined(__LZO_WIN16)
 
#  define HAVE_MALLOC_H
 
#  define HAVE_HALLOC
 
#endif
 

	
 
#undef NDEBUG
 
#if !defined(LZO_DEBUG)
 
#  define NDEBUG
 
#endif
 
#if defined(LZO_DEBUG) || !defined(NDEBUG)
 
#  if !defined(NO_STDIO_H)
 
#	include <stdio.h>
 
#  endif
 
#endif
 
#include <assert.h>
 

	
 
#if !defined(LZO_COMPILE_TIME_ASSERT)
 
#  define LZO_COMPILE_TIME_ASSERT(expr) \
 
		{ typedef int __lzo_compile_time_assert_fail[1 - 2 * !(expr)]; }
 
#endif
 

	
 
#if !defined(LZO_UNUSED)
 
#  if 1
 
#	define LZO_UNUSED(var)	 ((void)&var)
 
#  elif 0
 
#	define LZO_UNUSED(var)	 { typedef int __lzo_unused[sizeof(var) ? 2 : 1]; }
 
#  else
 
#	define LZO_UNUSED(parm)	(parm = parm)
 
#  endif
 
#endif
 

	
 
#if !defined(__inline__) && !defined(__GNUC__)
 
#  if defined(__cplusplus)
 
#	define __inline__	  inline
 
#  else
 
#	define __inline__
 
#  endif
 
#endif
 

	
 
#if defined(NO_MEMCMP)
 
#  undef HAVE_MEMCMP
 
#endif
 

	
 
#if 0
 
#  define LZO_BYTE(x)	   ((unsigned char) (x))
 
#else
 
#  define LZO_BYTE(x)	   ((unsigned char) ((x) & 0xff))
 
#endif
 

	
 
#define LZO_MAX(a,b)		((a) >= (b) ? (a) : (b))
 
#define LZO_MIN(a,b)		((a) <= (b) ? (a) : (b))
 
#define LZO_MAX3(a,b,c)	 ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c))
 
#define LZO_MIN3(a,b,c)	 ((a) <= (b) ? LZO_MIN(a,c) : LZO_MIN(b,c))
 

	
 
#define lzo_sizeof(type)	((lzo_uint) (sizeof(type)))
 

	
 
#define LZO_HIGH(array)	 ((lzo_uint) (sizeof(array)/sizeof(*(array))))
 

	
 
#define LZO_SIZE(bits)	  (1u << (bits))
 
#define LZO_MASK(bits)	  (LZO_SIZE(bits) - 1)
 

	
 
#define LZO_LSIZE(bits)	 (1ul << (bits))
 
#define LZO_LMASK(bits)	 (LZO_LSIZE(bits) - 1)
 

	
 
#define LZO_USIZE(bits)	 ((lzo_uint) 1 << (bits))
 
#define LZO_UMASK(bits)	 (LZO_USIZE(bits) - 1)
 

	
 
#define LZO_STYPE_MAX(b)	(((1l  << (8*(b)-2)) - 1l)  + (1l  << (8*(b)-2)))
 
#define LZO_UTYPE_MAX(b)	(((1ul << (8*(b)-1)) - 1ul) + (1ul << (8*(b)-1)))
 

	
 
#if !defined(SIZEOF_UNSIGNED)
 
#  if (UINT_MAX == 0xffff)
 
#	define SIZEOF_UNSIGNED		 2
 
#  elif (UINT_MAX == LZO_0xffffffffL)
 
#	define SIZEOF_UNSIGNED		 4
 
#  elif (UINT_MAX >= LZO_0xffffffffL)
 
#	define SIZEOF_UNSIGNED		 8
 
#  else
 
#	error "SIZEOF_UNSIGNED"
 
#  endif
 
#endif
 

	
 
#if !defined(SIZEOF_UNSIGNED_LONG)
 
#  if (ULONG_MAX == LZO_0xffffffffL)
 
#	define SIZEOF_UNSIGNED_LONG	4
 
#  elif (ULONG_MAX >= LZO_0xffffffffL)
 
#	define SIZEOF_UNSIGNED_LONG	8
 
#  else
 
#	error "SIZEOF_UNSIGNED_LONG"
 
#  endif
 
#endif
 

	
 
#if !defined(SIZEOF_SIZE_T)
 
#  define SIZEOF_SIZE_T			 SIZEOF_UNSIGNED
 
#endif
 
#if !defined(SIZE_T_MAX)
 
#  define SIZE_T_MAX				LZO_UTYPE_MAX(SIZEOF_SIZE_T)
 
#endif
 

	
 
#if 1 && defined(__LZO_i386) && (UINT_MAX == LZO_0xffffffffL)
 
#  if !defined(LZO_UNALIGNED_OK_2) && (USHRT_MAX == 0xffff)
 
#	define LZO_UNALIGNED_OK_2
 
#  endif
 
#  if !defined(LZO_UNALIGNED_OK_4) && (LZO_UINT32_MAX == LZO_0xffffffffL)
 
#	define LZO_UNALIGNED_OK_4
 
#  endif
 
#endif
 

	
 
#if defined(LZO_UNALIGNED_OK_2) || defined(LZO_UNALIGNED_OK_4)
 
#  if !defined(LZO_UNALIGNED_OK)
 
#	define LZO_UNALIGNED_OK
 
#  endif
 
#endif
 

	
 
#if defined(__LZO_NO_UNALIGNED)
 
#  undef LZO_UNALIGNED_OK
 
#  undef LZO_UNALIGNED_OK_2
 
#  undef LZO_UNALIGNED_OK_4
 
#endif
 

	
 
#if defined(LZO_UNALIGNED_OK_2) && (USHRT_MAX != 0xffff)
 
#  error "LZO_UNALIGNED_OK_2 must not be defined on this system"
 
#endif
 
#if defined(LZO_UNALIGNED_OK_4) && (LZO_UINT32_MAX != LZO_0xffffffffL)
 
#  error "LZO_UNALIGNED_OK_4 must not be defined on this system"
 
#endif
 

	
 
#if defined(__LZO_NO_ALIGNED)
 
#  undef LZO_ALIGNED_OK_4
 
#endif
 

	
 
#if defined(LZO_ALIGNED_OK_4) && (LZO_UINT32_MAX != LZO_0xffffffffL)
 
#  error "LZO_ALIGNED_OK_4 must not be defined on this system"
 
#endif
 

	
 
#define LZO_LITTLE_ENDIAN	   1234
 
#define LZO_BIG_ENDIAN		  4321
 
#define LZO_PDP_ENDIAN		  3412
 

	
 
#if !defined(LZO_BYTE_ORDER)
 
#  if defined(MFX_BYTE_ORDER)
 
#	define LZO_BYTE_ORDER	  MFX_BYTE_ORDER
 
#  elif defined(__LZO_i386)
 
#	define LZO_BYTE_ORDER	  LZO_LITTLE_ENDIAN
 
#  elif defined(BYTE_ORDER)
 
#	define LZO_BYTE_ORDER	  BYTE_ORDER
 
#  elif defined(__BYTE_ORDER)
 
#	define LZO_BYTE_ORDER	  __BYTE_ORDER
 
#  endif
 
#endif
 

	
 
#if defined(LZO_BYTE_ORDER)
 
#  if (LZO_BYTE_ORDER != LZO_LITTLE_ENDIAN) && \
 
	  (LZO_BYTE_ORDER != LZO_BIG_ENDIAN)
 
#	error "invalid LZO_BYTE_ORDER"
 
#  endif
 
#endif
 

	
 
#if defined(LZO_UNALIGNED_OK) && !defined(LZO_BYTE_ORDER)
 
#  error "LZO_BYTE_ORDER is not defined"
 
#endif
 

	
 
#define LZO_OPTIMIZE_GNUC_i386_IS_BUGGY
 

	
 
#if defined(NDEBUG) && !defined(LZO_DEBUG) && !defined(__LZO_CHECKER)
 
#  if defined(__GNUC__) && defined(__i386__)
 
#	if !defined(LZO_OPTIMIZE_GNUC_i386_IS_BUGGY)
 
#	  define LZO_OPTIMIZE_GNUC_i386
 
#	endif
 
#  endif
 
#endif
 

	
 
__LZO_EXTERN_C int __lzo_init_done;
 
__LZO_EXTERN_C const lzo_byte __lzo_copyright[];
 
LZO_EXTERN(const lzo_byte *) lzo_copyright(void);
 
__LZO_EXTERN_C const lzo_uint32 _lzo_crc32_table[256];
 

	
 
#define _LZO_STRINGIZE(x)		   #x
 
#define _LZO_MEXPAND(x)			 _LZO_STRINGIZE(x)
 

	
 
#define _LZO_CONCAT2(a,b)		   a ## b
 
#define _LZO_CONCAT3(a,b,c)		 a ## b ## c
 
#define _LZO_CONCAT4(a,b,c,d)	   a ## b ## c ## d
 
#define _LZO_CONCAT5(a,b,c,d,e)	 a ## b ## c ## d ## e
 

	
 
#define _LZO_ECONCAT2(a,b)		  _LZO_CONCAT2(a,b)
 
#define _LZO_ECONCAT3(a,b,c)		_LZO_CONCAT3(a,b,c)
 
#define _LZO_ECONCAT4(a,b,c,d)	  _LZO_CONCAT4(a,b,c,d)
 
#define _LZO_ECONCAT5(a,b,c,d,e)	_LZO_CONCAT5(a,b,c,d,e)
 

	
 
#ifndef __LZO_PTR_H
 
#define __LZO_PTR_H
 

	
 
#ifdef __cplusplus
 
extern "C" {
 
#endif
 

	
 
#if defined(__LZO_DOS16) || defined(__LZO_WIN16)
 
#  include <dos.h>
 
#  if 1 && defined(__WATCOMC__)
 
#	include <i86.h>
 
	 __LZO_EXTERN_C unsigned char _HShift;
 
#	define __LZO_HShift	_HShift
 
#  elif 1 && defined(_MSC_VER)
 
	 __LZO_EXTERN_C unsigned short __near _AHSHIFT;
 
#	define __LZO_HShift	((unsigned) &_AHSHIFT)
 
#  elif defined(__LZO_WIN16)
 
#	define __LZO_HShift	3
 
#  else
 
#	define __LZO_HShift	12
 
#  endif
 
#  if !defined(_FP_SEG) && defined(FP_SEG)
 
#	define _FP_SEG		 FP_SEG
 
#  endif
 
#  if !defined(_FP_OFF) && defined(FP_OFF)
 
#	define _FP_OFF		 FP_OFF
 
#  endif
 
#endif
 

	
 
#if !defined(lzo_ptrdiff_t)
 
#  if (UINT_MAX >= LZO_0xffffffffL)
 
	 typedef ptrdiff_t		  lzo_ptrdiff_t;
 
#  else
 
	 typedef long			   lzo_ptrdiff_t;
 
#  endif
 
#endif
 

	
 
#if !defined(__LZO_HAVE_PTR_T)
 
#  if defined(lzo_ptr_t)
 
#	define __LZO_HAVE_PTR_T
 
#  endif
 
#endif
 
#if !defined(__LZO_HAVE_PTR_T)
 
#	if defined(_WIN64)
 
		typedef unsigned __int64 lzo_ptr_t;
 
		typedef signed __int64   lzo_sptr_r;
 
#		define __LZO_HAVE_PTR_T
 
#	endif
 
#endif
 
#if !defined(__LZO_HAVE_PTR_T)
 
#  if defined(SIZEOF_CHAR_P) && defined(SIZEOF_UNSIGNED_LONG)
 
#	if (SIZEOF_CHAR_P == SIZEOF_UNSIGNED_LONG)
 
	   typedef unsigned long	lzo_ptr_t;
 
	   typedef long			 lzo_sptr_t;
 
#	  define __LZO_HAVE_PTR_T
 
#	endif
 
#  endif
 
#endif
 
#if !defined(__LZO_HAVE_PTR_T)
 
#  if defined(SIZEOF_CHAR_P) && defined(SIZEOF_UNSIGNED)
 
#	if (SIZEOF_CHAR_P == SIZEOF_UNSIGNED)
 
	   typedef unsigned int	 lzo_ptr_t;
 
	   typedef int			  lzo_sptr_t;
 
#	  define __LZO_HAVE_PTR_T
 
#	endif
 
#  endif
 
#endif
 
#if !defined(__LZO_HAVE_PTR_T)
 
#  if defined(SIZEOF_CHAR_P) && defined(SIZEOF_UNSIGNED_SHORT)
 
#	if (SIZEOF_CHAR_P == SIZEOF_UNSIGNED_SHORT)
 
	   typedef unsigned short   lzo_ptr_t;
 
	   typedef short			lzo_sptr_t;
 
#	  define __LZO_HAVE_PTR_T
 
#	endif
 
#  endif
 
#endif
 
#if !defined(__LZO_HAVE_PTR_T)
 
#  if defined(LZO_HAVE_CONFIG_H) || defined(SIZEOF_CHAR_P)
 
#	error "no suitable type for lzo_ptr_t"
 
#  else
 
	 typedef unsigned long	  lzo_ptr_t;
 
	 typedef long			   lzo_sptr_t;
 
#	define __LZO_HAVE_PTR_T
 
#  endif
 
#endif
 

	
 
#if defined(__LZO_DOS16) || defined(__LZO_WIN16)
 
#define PTR(a)			  ((lzo_bytep) (a))
 
#define PTR_ALIGNED_4(a)	((_FP_OFF(a) & 3) == 0)
 
#define PTR_ALIGNED2_4(a,b) (((_FP_OFF(a) | _FP_OFF(b)) & 3) == 0)
 
#else
 
#define PTR(a)			  ((lzo_ptr_t) (a))
 
#define PTR_LINEAR(a)	   PTR(a)
 
#define PTR_ALIGNED_4(a)	((PTR_LINEAR(a) & 3) == 0)
 
#define PTR_ALIGNED_8(a)	((PTR_LINEAR(a) & 7) == 0)
 
#define PTR_ALIGNED2_4(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0)
 
#define PTR_ALIGNED2_8(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0)
 
#endif
 

	
 
#define PTR_LT(a,b)		 (PTR(a) < PTR(b))
 
#define PTR_GE(a,b)		 (PTR(a) >= PTR(b))
 
#define PTR_DIFF(a,b)	   ((lzo_ptrdiff_t) (PTR(a) - PTR(b)))
 
#define pd(a,b)			 ((lzo_uint) ((a)-(b)))
 

	
 
LZO_EXTERN(lzo_ptr_t)
 
__lzo_ptr_linear(const lzo_voidp ptr);
 

	
 
typedef union
 
{
 
	char			a_char;
 
	unsigned char   a_uchar;
 
	short		   a_short;
 
	unsigned short  a_ushort;
 
	int			 a_int;
 
	unsigned int	a_uint;
 
	long			a_long;
 
	unsigned long   a_ulong;
 
	lzo_int		 a_lzo_int;
 
	lzo_uint		a_lzo_uint;
 
	lzo_int32	   a_lzo_int32;
 
	lzo_uint32	  a_lzo_uint32;
 
	ptrdiff_t	   a_ptrdiff_t;
 
	lzo_ptrdiff_t   a_lzo_ptrdiff_t;
 
	lzo_ptr_t	   a_lzo_ptr_t;
 
	lzo_voidp	   a_lzo_voidp;
 
	void *		  a_void_p;
 
	lzo_bytep	   a_lzo_bytep;
 
	lzo_bytepp	  a_lzo_bytepp;
 
	lzo_uintp	   a_lzo_uintp;
 
	lzo_uint *	  a_lzo_uint_p;
 
	lzo_uint32p	 a_lzo_uint32p;
 
	lzo_uint32 *	a_lzo_uint32_p;
 
	unsigned char * a_uchar_p;
 
	char *		  a_char_p;
 
}
 
lzo_full_align_t;
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
 
#define LZO_DETERMINISTIC
 

	
 
#define LZO_DICT_USE_PTR
 
#if defined(__LZO_DOS16) || defined(__LZO_WIN16) || defined(__LZO_STRICT_16BIT)
 
#  undef LZO_DICT_USE_PTR
 
#endif
 

	
 
#if defined(LZO_DICT_USE_PTR)
 
#  define lzo_dict_t	const lzo_bytep
 
#  define lzo_dict_p	lzo_dict_t __LZO_MMODEL *
 
#else
 
#  define lzo_dict_t	lzo_uint
 
#  define lzo_dict_p	lzo_dict_t __LZO_MMODEL *
 
#endif
 

	
 
#if !defined(lzo_moff_t)
 
#define lzo_moff_t	  lzo_uint
 
#endif
 

	
 
#endif
 

	
 

	
 
#ifndef __LZO_UTIL_H
 
#define __LZO_UTIL_H
 

	
 
#ifndef __LZO_CONF_H
 
#endif
 

	
 
#ifdef __cplusplus
 
extern "C" {
 
#endif
 

	
 
#if 1 && defined(HAVE_MEMCPY)
 
#if !defined(__LZO_DOS16) && !defined(__LZO_WIN16)
 

	
 
#define MEMCPY8_DS(dest,src,len) \
 
	memcpy(dest,src,len); \
 
	dest += len; \
 
	src += len
 

	
 
#endif
 
#endif
 

	
 
#if 0 && !defined(MEMCPY8_DS)
 

	
 
#define MEMCPY8_DS(dest,src,len) \
 
	{ do { \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		len -= 8; \
 
	} while (len > 0); }
 

	
 
#endif
 

	
 
#if !defined(MEMCPY8_DS)
 

	
 
#define MEMCPY8_DS(dest,src,len) \
 
	{ register lzo_uint __l = (len) / 8; \
 
	do { \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
		*dest++ = *src++; \
 
	} while (--__l > 0); }
 

	
 
#endif
 

	
 
#define MEMCPY_DS(dest,src,len) \
 
	do *dest++ = *src++; \
 
	while (--len > 0)
 

	
 
#define MEMMOVE_DS(dest,src,len) \
 
	do *dest++ = *src++; \
 
	while (--len > 0)
 

	
 
#if 0 && defined(LZO_OPTIMIZE_GNUC_i386)
 

	
 
#define BZERO8_PTR(s,l,n) \
 
__asm__ __volatile__( \
 
	"movl  %0,%%eax \n"			 \
 
	"movl  %1,%%edi \n"			 \
 
	"movl  %2,%%ecx \n"			 \
 
	"cld \n"						\
 
	"rep \n"						\
 
	"stosl %%eax,(%%edi) \n"		\
 
	:			   \
 
	:"g" (0),"g" (s),"g" (n)		\
 
	:"eax","edi","ecx", "memory", "cc" \
 
)
 

	
 
#elif (LZO_UINT_MAX <= SIZE_T_MAX) && defined(HAVE_MEMSET)
 

	
 
#if 1
 
#define BZERO8_PTR(s,l,n)   memset((s),0,(lzo_uint)(l)*(n))
 
#else
 
#define BZERO8_PTR(s,l,n)   memset((lzo_voidp)(s),0,(lzo_uint)(l)*(n))
 
#endif
 

	
 
#else
 

	
 
#define BZERO8_PTR(s,l,n) \
 
	lzo_memset((lzo_voidp)(s),0,(lzo_uint)(l)*(n))
 

	
 
#endif
 

	
 
#if 0
 
#if defined(__GNUC__) && defined(__i386__)
 

	
 
unsigned char lzo_rotr8(unsigned char value, int shift);
 
extern __inline__ unsigned char lzo_rotr8(unsigned char value, int shift)
 
{
 
	unsigned char result;
 

	
 
	__asm__ __volatile__ ("movb %b1, %b0; rorb %b2, %b0"
 
						: "=a"(result) : "g"(value), "c"(shift));
 
	return result;
 
}
 

	
 
unsigned short lzo_rotr16(unsigned short value, int shift);
 
extern __inline__ unsigned short lzo_rotr16(unsigned short value, int shift)
 
{
 
	unsigned short result;
 

	
 
	__asm__ __volatile__ ("movw %b1, %b0; rorw %b2, %b0"
 
						: "=a"(result) : "g"(value), "c"(shift));
 
	return result;
 
}
 

	
 
#endif
 
#endif
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
 

	
 
/* If you use the LZO library in a product, you *must* keep this
 
 * copyright string in the executable of your product.
 
 */
 

	
 
const lzo_byte __lzo_copyright[] =
 
#if !defined(__LZO_IN_MINLZO)
 
	LZO_VERSION_STRING;
 
#else
 
	"\n\n\n"
 
	"LZO real-time data compression library.\n"
 
	"Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002 Markus Franz Xaver Johannes Oberhumer\n"
 
	"<markus.oberhumer@jk.uni-linz.ac.at>\n"
 
	"http://www.oberhumer.com/opensource/lzo/\n"
 
	"\n"
 
	"LZO version: v" LZO_VERSION_STRING ", " LZO_VERSION_DATE "\n"
 
	"LZO build date: " __DATE__ " " __TIME__ "\n\n"
 
	"LZO special compilation options:\n"
 
#ifdef __cplusplus
 
	" __cplusplus\n"
 
#endif
 
#if defined(__PIC__)
 
	" __PIC__\n"
 
#elif defined(__pic__)
 
	" __pic__\n"
 
#endif
 
#if (UINT_MAX < LZO_0xffffffffL)
 
	" 16BIT\n"
 
#endif
 
#if defined(__LZO_STRICT_16BIT)
 
	" __LZO_STRICT_16BIT\n"
 
#endif
 
#if (UINT_MAX > LZO_0xffffffffL)
 
	" UINT_MAX=" _LZO_MEXPAND(UINT_MAX) "\n"
 
#endif
 
#if (ULONG_MAX > LZO_0xffffffffL)
 
	" ULONG_MAX=" _LZO_MEXPAND(ULONG_MAX) "\n"
 
#endif
 
#if defined(LZO_BYTE_ORDER)
 
	" LZO_BYTE_ORDER=" _LZO_MEXPAND(LZO_BYTE_ORDER) "\n"
 
#endif
 
#if defined(LZO_UNALIGNED_OK_2)
 
	" LZO_UNALIGNED_OK_2\n"
 
#endif
 
#if defined(LZO_UNALIGNED_OK_4)
 
	" LZO_UNALIGNED_OK_4\n"
 
#endif
 
#if defined(LZO_ALIGNED_OK_4)
 
	" LZO_ALIGNED_OK_4\n"
 
#endif
 
#if defined(LZO_DICT_USE_PTR)
 
	" LZO_DICT_USE_PTR\n"
 
#endif
 
#if defined(__LZO_QUERY_COMPRESS)
 
	" __LZO_QUERY_COMPRESS\n"
 
#endif
 
#if defined(__LZO_QUERY_DECOMPRESS)
 
	" __LZO_QUERY_DECOMPRESS\n"
 
#endif
 
#if defined(__LZO_IN_MINILZO)
 
	" __LZO_IN_MINILZO\n"
 
#endif
 
	"\n\n"
 
	"$Id: LZO " LZO_VERSION_STRING " built " __DATE__ " " __TIME__
 
#if defined(__GNUC__) && defined(__VERSION__)
 
	" by gcc " __VERSION__
 
#elif defined(__BORLANDC__)
 
	" by Borland C " _LZO_MEXPAND(__BORLANDC__)
 
#elif defined(_MSC_VER)
 
	" by Microsoft C " _LZO_MEXPAND(_MSC_VER)
 
#elif defined(__PUREC__)
 
	" by Pure C " _LZO_MEXPAND(__PUREC__)
 
#elif defined(__SC__)
 
	" by Symantec C " _LZO_MEXPAND(__SC__)
 
#elif defined(__TURBOC__)
 
	" by Turbo C " _LZO_MEXPAND(__TURBOC__)
 
#elif defined(__WATCOMC__)
 
	" by Watcom C " _LZO_MEXPAND(__WATCOMC__)
 
#endif
 
	" $\n"
 
	"$Copyright: LZO (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002 Markus Franz Xaver Johannes Oberhumer $\n";
 
#endif
 

	
 
#define LZO_BASE 65521u
 
#define LZO_NMAX 5552
 

	
 
#define LZO_DO1(buf,i)  {s1 += buf[i]; s2 += s1;}
 
#define LZO_DO2(buf,i)  LZO_DO1(buf,i); LZO_DO1(buf,i+1);
 
#define LZO_DO4(buf,i)  LZO_DO2(buf,i); LZO_DO2(buf,i+2);
 
#define LZO_DO8(buf,i)  LZO_DO4(buf,i); LZO_DO4(buf,i+4);
 
#define LZO_DO16(buf,i) LZO_DO8(buf,i); LZO_DO8(buf,i+8);
 

	
 
LZO_PUBLIC(lzo_uint32)
 
lzo_adler32(lzo_uint32 adler, const lzo_byte *buf, lzo_uint len)
 
{
 
	lzo_uint32 s1 = adler & 0xffff;
 
	lzo_uint32 s2 = (adler >> 16) & 0xffff;
 
	int k;
 

	
 
	if (buf == NULL)
 
		return 1;
 

	
 
	while (len > 0)
 
	{
 
		k = len < LZO_NMAX ? (int) len : LZO_NMAX;
 
		len -= k;
 
		if (k >= 16) do
 
		{
 
			LZO_DO16(buf,0);
 
			buf += 16;
 
			k -= 16;
 
		} while (k >= 16);
 
		if (k != 0) do
 
		{
 
			s1 += *buf++;
 
			s2 += s1;
 
		} while (--k > 0);
 
		s1 %= LZO_BASE;
 
		s2 %= LZO_BASE;
 
		}
 
	return (s2 << 16) | s1;
 
}
 

	
 
#if 0
 
#  define IS_SIGNED(type)	   (((type) (1ul << (8 * sizeof(type) - 1))) < 0)
 
#  define IS_UNSIGNED(type)	 (((type) (1ul << (8 * sizeof(type) - 1))) > 0)
 
#else
 
#  define IS_SIGNED(type)	   (((type) (-1)) < ((type) 0))
 
#  define IS_UNSIGNED(type)	 (((type) (-1)) > ((type) 0))
 
#endif
 

	
 
#define IS_POWER_OF_2(x)		(((x) & ((x) - 1)) == 0)
 

	
 
// static lzo_bool schedule_insns_bug(void);
 
// static lzo_bool strength_reduce_bug(int *);
 

	
 
#if 0 || defined(LZO_DEBUG)
 
#include <stdio.h>
 
static lzo_bool __lzo_assert_fail(const char *s, unsigned line)
 
{
 
#if defined(__palmos__)
 
	printf("LZO assertion failed in line %u: '%s'\n",line,s);
 
#else
 
	fprintf(stderr,"LZO assertion failed in line %u: '%s'\n",line,s);
 
#endif
 
	return 0;
 
}
 
#  define __lzo_assert(x)   ((x) ? 1 : __lzo_assert_fail(#x,__LINE__))
 
#else
 
#  define __lzo_assert(x)   ((x) ? 1 : 0)
 
#endif
 

	
 
#undef COMPILE_TIME_ASSERT
 
#if 0
 
#  define COMPILE_TIME_ASSERT(expr)	 r &= __lzo_assert(expr)
 
#else
 
#  define COMPILE_TIME_ASSERT(expr)	 LZO_COMPILE_TIME_ASSERT(expr)
 
#endif
 

	
 

	
 
#define do_compress		 _lzo1x_1_do_compress
 

	
 
#define LZO_NEED_DICT_H
 
#define D_BITS		  12
 
#define D_INDEX1(d,p)	   d = DM((0x21*DX3(p,5,5,6)) >> 5)
 
#define D_INDEX2(d,p)	   d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f)
 

	
 
#ifndef __LZO_CONFIG1X_H
 
#define __LZO_CONFIG1X_H
 

	
 
#define LZO_EOF_CODE
 
#undef LZO_DETERMINISTIC
 

	
 
#define M1_MAX_OFFSET   0x0400
 
#ifndef M2_MAX_OFFSET
 
#define M2_MAX_OFFSET   0x0800
 
#endif
 
#define M3_MAX_OFFSET   0x4000
 
#define M4_MAX_OFFSET   0xbfff
 

	
 
#define MX_MAX_OFFSET   (M1_MAX_OFFSET + M2_MAX_OFFSET)
 

	
 
#define M1_MIN_LEN	  2
 
#define M1_MAX_LEN	  2
 
#define M2_MIN_LEN	  3
 
#ifndef M2_MAX_LEN
 
#define M2_MAX_LEN	  8
 
#endif
 
#define M3_MIN_LEN	  3
 
#define M3_MAX_LEN	  33
 
#define M4_MIN_LEN	  3
 
#define M4_MAX_LEN	  9
 

	
 
#define M1_MARKER	   0
 
#define M2_MARKER	   64
 
#define M3_MARKER	   32
 
#define M4_MARKER	   16
 

	
 
#ifndef MIN_LOOKAHEAD
 
#define MIN_LOOKAHEAD	   (M2_MAX_LEN + 1)
 
#endif
 

	
 
#if defined(LZO_NEED_DICT_H)
 

	
 
#ifndef LZO_HASH
 
#define LZO_HASH			LZO_HASH_LZO_INCREMENTAL_B
 
#endif
 
#define DL_MIN_LEN		  M2_MIN_LEN
 

	
 
#ifndef __LZO_DICT_H
 
#define __LZO_DICT_H
 

	
 
#ifdef __cplusplus
 
extern "C" {
 
#endif
 

	
 
#if !defined(D_BITS) && defined(DBITS)
 
#  define D_BITS		DBITS
 
#endif
 
#if !defined(D_BITS)
 
#  error "D_BITS is not defined"
 
#endif
 
#if (D_BITS < 16)
 
#  define D_SIZE		LZO_SIZE(D_BITS)
 
#  define D_MASK		LZO_MASK(D_BITS)
 
#else
 
#  define D_SIZE		LZO_USIZE(D_BITS)
 
#  define D_MASK		LZO_UMASK(D_BITS)
 
#endif
 
#define D_HIGH		  ((D_MASK >> 1) + 1)
 

	
 
#if !defined(DD_BITS)
 
#  define DD_BITS	   0
 
#endif
 
#define DD_SIZE		 LZO_SIZE(DD_BITS)
 
#define DD_MASK		 LZO_MASK(DD_BITS)
 

	
 
#if !defined(DL_BITS)
 
#  define DL_BITS	   (D_BITS - DD_BITS)
 
#endif
 
#if (DL_BITS < 16)
 
#  define DL_SIZE	   LZO_SIZE(DL_BITS)
 
#  define DL_MASK	   LZO_MASK(DL_BITS)
 
#else
 
#  define DL_SIZE	   LZO_USIZE(DL_BITS)
 
#  define DL_MASK	   LZO_UMASK(DL_BITS)
 
#endif
 

	
 
#if (D_BITS != DL_BITS + DD_BITS)
 
#  error "D_BITS does not match"
 
#endif
 
#if (D_BITS < 8 || D_BITS > 18)
 
#  error "invalid D_BITS"
 
#endif
 
#if (DL_BITS < 8 || DL_BITS > 20)
 
#  error "invalid DL_BITS"
 
#endif
 
#if (DD_BITS < 0 || DD_BITS > 6)
 
#  error "invalid DD_BITS"
 
#endif
 

	
 
#if !defined(DL_MIN_LEN)
 
#  define DL_MIN_LEN	3
 
#endif
 
#if !defined(DL_SHIFT)
 
#  define DL_SHIFT	  ((DL_BITS + (DL_MIN_LEN - 1)) / DL_MIN_LEN)
 
#endif
 

	
 
#define LZO_HASH_GZIP				   1
 
#define LZO_HASH_GZIP_INCREMENTAL	   2
 
#define LZO_HASH_LZO_INCREMENTAL_A	  3
 
#define LZO_HASH_LZO_INCREMENTAL_B	  4
 

	
 
#if !defined(LZO_HASH)
 
#  error "choose a hashing strategy"
 
#endif
 

	
 
#if (DL_MIN_LEN == 3)
 
#  define _DV2_A(p,shift1,shift2) \
 
		(((( (lzo_uint32)((p)[0]) << shift1) ^ (p)[1]) << shift2) ^ (p)[2])
 
#  define _DV2_B(p,shift1,shift2) \
 
		(((( (lzo_uint32)((p)[2]) << shift1) ^ (p)[1]) << shift2) ^ (p)[0])
 
#  define _DV3_B(p,shift1,shift2,shift3) \
 
		((_DV2_B((p)+1,shift1,shift2) << (shift3)) ^ (p)[0])
 
#elif (DL_MIN_LEN == 2)
 
#  define _DV2_A(p,shift1,shift2) \
 
		(( (lzo_uint32)(p[0]) << shift1) ^ p[1])
 
#  define _DV2_B(p,shift1,shift2) \
 
		(( (lzo_uint32)(p[1]) << shift1) ^ p[2])
 
#else
 
#  error "invalid DL_MIN_LEN"
 
#endif
 
#define _DV_A(p,shift)	  _DV2_A(p,shift,shift)
 
#define _DV_B(p,shift)	  _DV2_B(p,shift,shift)
 
#define DA2(p,s1,s2) \
 
		(((((lzo_uint32)((p)[2]) << (s2)) + (p)[1]) << (s1)) + (p)[0])
 
#define DS2(p,s1,s2) \
 
		(((((lzo_uint32)((p)[2]) << (s2)) - (p)[1]) << (s1)) - (p)[0])
 
#define DX2(p,s1,s2) \
 
		(((((lzo_uint32)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0])
 
#define DA3(p,s1,s2,s3) ((DA2((p)+1,s2,s3) << (s1)) + (p)[0])
 
#define DS3(p,s1,s2,s3) ((DS2((p)+1,s2,s3) << (s1)) - (p)[0])
 
#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0])
 
#define DMS(v,s)		((lzo_uint) (((v) & (D_MASK >> (s))) << (s)))
 
#define DM(v)		   DMS(v,0)
 

	
 
#if (LZO_HASH == LZO_HASH_GZIP)
 
#  define _DINDEX(dv,p)	 (_DV_A((p),DL_SHIFT))
 

	
 
#elif (LZO_HASH == LZO_HASH_GZIP_INCREMENTAL)
 
#  define __LZO_HASH_INCREMENTAL
 
#  define DVAL_FIRST(dv,p)  dv = _DV_A((p),DL_SHIFT)
 
#  define DVAL_NEXT(dv,p)   dv = (((dv) << DL_SHIFT) ^ p[2])
 
#  define _DINDEX(dv,p)	 (dv)
 
#  define DVAL_LOOKAHEAD	DL_MIN_LEN
 

	
 
#elif (LZO_HASH == LZO_HASH_LZO_INCREMENTAL_A)
 
#  define __LZO_HASH_INCREMENTAL
 
#  define DVAL_FIRST(dv,p)  dv = _DV_A((p),5)
 
#  define DVAL_NEXT(dv,p) \
 
				dv ^= (lzo_uint32)(p[-1]) << (2*5); dv = (((dv) << 5) ^ p[2])
 
#  define _DINDEX(dv,p)	 ((0x9f5f * (dv)) >> 5)
 
#  define DVAL_LOOKAHEAD	DL_MIN_LEN
 

	
 
#elif (LZO_HASH == LZO_HASH_LZO_INCREMENTAL_B)
 
#  define __LZO_HASH_INCREMENTAL
 
#  define DVAL_FIRST(dv,p)  dv = _DV_B((p),5)
 
#  define DVAL_NEXT(dv,p) \
 
				dv ^= p[-1]; dv = (((dv) >> 5) ^ ((lzo_uint32)(p[2]) << (2*5)))
 
#  define _DINDEX(dv,p)	 ((0x9f5f * (dv)) >> 5)
 
#  define DVAL_LOOKAHEAD	DL_MIN_LEN
 

	
 
#else
 
#  error "choose a hashing strategy"
 
#endif
 

	
 
#ifndef DINDEX
 
#define DINDEX(dv,p)		((lzo_uint)((_DINDEX(dv,p)) & DL_MASK) << DD_BITS)
 
#endif
 
#if !defined(DINDEX1) && defined(D_INDEX1)
 
#define DINDEX1			 D_INDEX1
 
#endif
 
#if !defined(DINDEX2) && defined(D_INDEX2)
 
#define DINDEX2			 D_INDEX2
 
#endif
 

	
 
#if !defined(__LZO_HASH_INCREMENTAL)
 
#  define DVAL_FIRST(dv,p)  ((void) 0)
 
#  define DVAL_NEXT(dv,p)   ((void) 0)
 
#  define DVAL_LOOKAHEAD	0
 
#endif
 

	
 
#if !defined(DVAL_ASSERT)
 
#if defined(__LZO_HASH_INCREMENTAL) && !defined(NDEBUG)
 
static void DVAL_ASSERT(lzo_uint32 dv, const lzo_byte *p)
 
{
 
	lzo_uint32 df;
 
	DVAL_FIRST(df,(p));
 
	assert(DINDEX(dv,p) == DINDEX(df,p));
 
}
 
#else
 
#  define DVAL_ASSERT(dv,p) ((void) 0)
 
#endif
 
#endif
 

	
 
#if defined(LZO_DICT_USE_PTR)
 
#  define DENTRY(p,in)						  (p)
 
#  define GINDEX(m_pos,m_off,dict,dindex,in)	m_pos = dict[dindex]
 
#else
 
#  define DENTRY(p,in)						  ((lzo_uint) ((p)-(in)))
 
#  define GINDEX(m_pos,m_off,dict,dindex,in)	m_off = dict[dindex]
 
#endif
 

	
 
#if (DD_BITS == 0)
 

	
 
#  define UPDATE_D(dict,drun,dv,p,in)	   dict[ DINDEX(dv,p) ] = DENTRY(p,in)
 
#  define UPDATE_I(dict,drun,index,p,in)	dict[index] = DENTRY(p,in)
 
#  define UPDATE_P(ptr,drun,p,in)		   (ptr)[0] = DENTRY(p,in)
 

	
 
#else
 

	
 
#  define UPDATE_D(dict,drun,dv,p,in)   \
 
		dict[ DINDEX(dv,p) + drun++ ] = DENTRY(p,in); drun &= DD_MASK
 
#  define UPDATE_I(dict,drun,index,p,in)	\
 
		dict[ (index) + drun++ ] = DENTRY(p,in); drun &= DD_MASK
 
#  define UPDATE_P(ptr,drun,p,in)   \
 
		(ptr) [ drun++ ] = DENTRY(p,in); drun &= DD_MASK
 

	
 
#endif
 

	
 
#if defined(LZO_DICT_USE_PTR)
 

	
 
#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \
 
		(m_pos == NULL || (m_off = (lzo_moff_t) (ip - m_pos)) > max_offset)
 

	
 
#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \
 
	(BOUNDS_CHECKING_OFF_IN_EXPR( \
 
		(PTR_LT(m_pos,in) || \
 
		 (m_off = (lzo_moff_t) PTR_DIFF(ip,m_pos)) <= 0 || \
 
		  m_off > max_offset) ))
 

	
 
#else
 

	
 
#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \
 
		(m_off == 0 || \
 
		 ((m_off = (lzo_moff_t) ((ip)-(in)) - m_off) > max_offset) || \
 
		 (m_pos = (ip) - (m_off), 0) )
 

	
 
#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \
 
		((lzo_moff_t) ((ip)-(in)) <= m_off || \
 
		 ((m_off = (lzo_moff_t) ((ip)-(in)) - m_off) > max_offset) || \
 
		 (m_pos = (ip) - (m_off), 0) )
 

	
 
#endif
 

	
 
#if defined(LZO_DETERMINISTIC)
 
#  define LZO_CHECK_MPOS	LZO_CHECK_MPOS_DET
 
#else
 
#  define LZO_CHECK_MPOS	LZO_CHECK_MPOS_NON_DET
 
#endif
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif
 

	
 
#endif
 

	
 
#endif
 

	
 
#define DO_COMPRESS	 lzo1x_1_compress
 

	
 
static
 
lzo_uint do_compress	 ( const lzo_byte *in , lzo_uint  in_len,
 
								 lzo_byte *out, lzo_uintp out_len,
 
								 lzo_voidp wrkmem )
 
{
 
		register const lzo_byte *ip;
 
		lzo_byte *op;
 
		const lzo_byte * const in_end = in + in_len;
 
		const lzo_byte * const ip_end = in + in_len - M2_MAX_LEN - 5;
 
		const lzo_byte *ii;
 
		lzo_dict_p const dict = (lzo_dict_p) wrkmem;
 

	
 
		op = out;
 
		ip = in;
 
		ii = ip;
 

	
 
		ip += 4;
 
		for (;;)
 
		{
 
		register const lzo_byte *m_pos;
 
		lzo_moff_t m_off;
 
		lzo_uint m_len;
 
		lzo_uint dindex;
 

	
 
		DINDEX1(dindex,ip);
 
		GINDEX(m_pos,m_off,dict,dindex,in);
 
		if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET))
 
			goto literal;
 
		if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
 
			goto try_match;
 
		DINDEX2(dindex,ip);
 
		GINDEX(m_pos,m_off,dict,dindex,in);
 
		if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET))
 
			goto literal;
 
		if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
 
			goto try_match;
 
		goto literal;
 

	
 
try_match:
 
#if 1 && defined(LZO_UNALIGNED_OK_2)
 
		if (* (const lzo_ushortp) m_pos != * (const lzo_ushortp) ip)
 
#else
 
		if (m_pos[0] != ip[0] || m_pos[1] != ip[1])
 
#endif
 
		{
 
		}
 
		else
 
		{
 
			if (m_pos[2] == ip[2])
 
			{
 
					goto match;
 
			}
 
		}
 

	
 
literal:
 
		UPDATE_I(dict,0,dindex,ip,in);
 
		++ip;
 
		if (ip >= ip_end)
 
			break;
 
		continue;
 

	
 
match:
 
		UPDATE_I(dict,0,dindex,ip,in);
 
		if (pd(ip,ii) > 0)
 
		{
 
			register lzo_uint t = pd(ip,ii);
 

	
 
			if (t <= 3)
 
			{
 
				assert(op - 2 > out);
 
				op[-2] |= LZO_BYTE(t);
 
			}
 
			else if (t <= 18)
 
				*op++ = LZO_BYTE(t - 3);
 
			else
 
			{
 
				register lzo_uint tt = t - 18;
 

	
 
				*op++ = 0;
 
				while (tt > 255)
 
				{
 
					tt -= 255;
 
					*op++ = 0;
 
				}
 
				assert(tt > 0);
 
				*op++ = LZO_BYTE(tt);
 
			}
 
			do *op++ = *ii++; while (--t > 0);
 
		}
 

	
 
		assert(ii == ip);
 
		ip += 3;
 
		if (m_pos[3] != *ip++ || m_pos[4] != *ip++ || m_pos[5] != *ip++ ||
 
			m_pos[6] != *ip++ || m_pos[7] != *ip++ || m_pos[8] != *ip++) {
 

	
 
			--ip;
 
			m_len = ip - ii;
 
			assert(m_len >= 3); assert(m_len <= M2_MAX_LEN);
 

	
 
			if (m_off <= M2_MAX_OFFSET)
 
			{
 
				m_off -= 1;
 
				*op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2));
 
				*op++ = LZO_BYTE(m_off >> 3);
 
			}
 
			else if (m_off <= M3_MAX_OFFSET)
 
			{
 
				m_off -= 1;
 
				*op++ = LZO_BYTE(M3_MARKER | (m_len - 2));
 
				goto m3_m4_offset;
 
			}
 
			else
 
			{
 
				m_off -= 0x4000;
 
				assert(m_off > 0); assert(m_off <= 0x7fff);
 
				*op++ = LZO_BYTE(M4_MARKER |
 
								 ((m_off & 0x4000) >> 11) | (m_len - 2));
 
				goto m3_m4_offset;
 
			}
 
		}
 
		else
 
		{
 
			{
 
				const lzo_byte *end = in_end;
 
				const lzo_byte *m = m_pos + M2_MAX_LEN + 1;
 
				while (ip < end && *m == *ip)
 
					m++, ip++;
 
				m_len = (ip - ii);
 
			}
 
			assert(m_len > M2_MAX_LEN);
 

	
 
			if (m_off <= M3_MAX_OFFSET)
 
			{
 
				m_off -= 1;
 
				if (m_len <= 33)
 
					*op++ = LZO_BYTE(M3_MARKER | (m_len - 2));
 
				else
 
				{
 
					m_len -= 33;
 
					*op++ = M3_MARKER | 0;
 
					goto m3_m4_len;
 
				}
 
			}
 
			else
 
			{
 
				m_off -= 0x4000;
 
				assert(m_off > 0); assert(m_off <= 0x7fff);
 
				if (m_len <= M4_MAX_LEN)
 
					*op++ = LZO_BYTE(M4_MARKER |
 
									 ((m_off & 0x4000) >> 11) | (m_len - 2));
 
				else
 
				{
 
					m_len -= M4_MAX_LEN;
 
					*op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11));
 
m3_m4_len:
 
					while (m_len > 255)
 
					{
 
						m_len -= 255;
 
						*op++ = 0;
 
					}
 
					assert(m_len > 0);
 
					*op++ = LZO_BYTE(m_len);
 
				}
 
			}
 

	
 
m3_m4_offset:
 
			*op++ = LZO_BYTE((m_off & 63) << 2);
 
			*op++ = LZO_BYTE(m_off >> 6);
 
		}
 

	
 
		ii = ip;
 
		if (ip >= ip_end)
 
			break;
 
	}
 

	
 
	*out_len = op - out;
 
	return pd(in_end,ii);
 
}
 

	
 
LZO_PUBLIC(int)
 
DO_COMPRESS	  ( const lzo_byte *in , lzo_uint  in_len,
 
						 lzo_byte *out, lzo_uintp out_len,
 
						 lzo_voidp wrkmem )
 
{
 
	lzo_byte *op = out;
 
	lzo_uint t;
 

	
 
	if (in_len <= M2_MAX_LEN + 5)
 
		t = in_len;
 
	else
 
	{
 
		t = do_compress(in,in_len,op,out_len,wrkmem);
 
		op += *out_len;
 
	}
 

	
 
	if (t > 0)
 
	{
 
		const lzo_byte *ii = in + in_len - t;
 

	
 
		if (op == out && t <= 238)
 
			*op++ = LZO_BYTE(17 + t);
 
		else if (t <= 3)
 
			op[-2] |= LZO_BYTE(t);
 
		else if (t <= 18)
 
			*op++ = LZO_BYTE(t - 3);
 
		else
 
		{
 
			lzo_uint tt = t - 18;
 

	
 
			*op++ = 0;
 
			while (tt > 255)
 
			{
 
				tt -= 255;
 
				*op++ = 0;
 
			}
 
			assert(tt > 0);
 
			*op++ = LZO_BYTE(tt);
 
		}
 
		do *op++ = *ii++; while (--t > 0);
 
	}
 

	
 
	*op++ = M4_MARKER | 1;
 
	*op++ = 0;
 
	*op++ = 0;
 

	
 
	*out_len = op - out;
 
	return LZO_E_OK;
 
}
 

	
 
#undef do_compress
 
#undef DO_COMPRESS
 
#undef LZO_HASH
 

	
 
#undef LZO_TEST_DECOMPRESS_OVERRUN
 
#undef LZO_TEST_DECOMPRESS_OVERRUN_INPUT
 
#undef LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT
 
#undef LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND
 
#undef DO_DECOMPRESS
 
#define DO_DECOMPRESS	   lzo1x_decompress
 

	
 
#if defined(LZO_TEST_DECOMPRESS_OVERRUN)
 
#  if !defined(LZO_TEST_DECOMPRESS_OVERRUN_INPUT)
 
#	define LZO_TEST_DECOMPRESS_OVERRUN_INPUT	   2
 
#  endif
 
#  if !defined(LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT)
 
#	define LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT	  2
 
#  endif
 
#  if !defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND)
 
#	define LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND
 
#  endif
 
#endif
 

	
 
#undef TEST_IP
 
#undef TEST_OP
 
#undef TEST_LOOKBEHIND
 
#undef NEED_IP
 
#undef NEED_OP
 
#undef HAVE_TEST_IP
 
#undef HAVE_TEST_OP
 
#undef HAVE_NEED_IP
 
#undef HAVE_NEED_OP
 
#undef HAVE_ANY_IP
 
#undef HAVE_ANY_OP
 

	
 
#if defined(LZO_TEST_DECOMPRESS_OVERRUN_INPUT)
 
#  if (LZO_TEST_DECOMPRESS_OVERRUN_INPUT >= 1)
 
#	define TEST_IP			 (ip < ip_end)
 
#  endif
 
#  if (LZO_TEST_DECOMPRESS_OVERRUN_INPUT >= 2)
 
#	define NEED_IP(x) \
 
			if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x))  goto input_overrun
 
#  endif
 
#endif
 

	
 
#if defined(LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT)
 
#  if (LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT >= 1)
 
#	define TEST_OP			 (op <= op_end)
 
#  endif
 
#  if (LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT >= 2)
 
#	undef TEST_OP
 
#	define NEED_OP(x) \
 
			if ((lzo_uint)(op_end - op) < (lzo_uint)(x))  goto output_overrun
 
#  endif
 
#endif
 

	
 
#if defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND)
 
#  define TEST_LOOKBEHIND(m_pos,out)	if (m_pos < out) goto lookbehind_overrun
 
#else
 
#  define TEST_LOOKBEHIND(m_pos,op)	 ((void) 0)
 
#endif
 

	
 
#if !defined(LZO_EOF_CODE) && !defined(TEST_IP)
 
#  define TEST_IP			   (ip < ip_end)
 
#endif
 

	
 
#if defined(TEST_IP)
 
#  define HAVE_TEST_IP
 
#else
 
#  define TEST_IP			   1
 
#endif
 
#if defined(TEST_OP)
 
#  define HAVE_TEST_OP
 
#else
 
#  define TEST_OP			   1
 
#endif
 

	
 
#if defined(NEED_IP)
 
#  define HAVE_NEED_IP
 
#else
 
#  define NEED_IP(x)			((void) 0)
 
#endif
 
#if defined(NEED_OP)
 
#  define HAVE_NEED_OP
 
#else
 
#  define NEED_OP(x)			((void) 0)
 
#endif
 

	
 
#if defined(HAVE_TEST_IP) || defined(HAVE_NEED_IP)
 
#  define HAVE_ANY_IP
 
#endif
 
#if defined(HAVE_TEST_OP) || defined(HAVE_NEED_OP)
 
#  define HAVE_ANY_OP
 
#endif
 

	
 
#undef __COPY4
 
#define __COPY4(dst,src)	* (lzo_uint32p)(dst) = * (const lzo_uint32p)(src)
 

	
 
#undef COPY4
 
#if defined(LZO_UNALIGNED_OK_4)
 
#  define COPY4(dst,src)	__COPY4(dst,src)
 
#elif defined(LZO_ALIGNED_OK_4)
 
#  define COPY4(dst,src)	__COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src))
 
#endif
 

	
 
#if defined(DO_DECOMPRESS)
 
LZO_PUBLIC(int)
 
DO_DECOMPRESS  ( const lzo_byte *in , lzo_uint  in_len,
 
					   lzo_byte *out, lzo_uintp out_len,
 
					   lzo_voidp wrkmem )
 
#endif
 
{
 
	register lzo_byte *op;
 
	register const lzo_byte *ip;
 
	register lzo_uint t;
 
	register const lzo_byte *m_pos;
 

	
 
	const lzo_byte * const ip_end = in + in_len;
 
#if defined(HAVE_ANY_OP)
 
	lzo_byte * const op_end = out + *out_len;
 
#endif
 

	
 
	LZO_UNUSED(wrkmem);
 

	
 
	*out_len = 0;
 

	
 
	op = out;
 
	ip = in;
 

	
 
	if (*ip > 17)
 
	{
 
		t = *ip++ - 17;
 
		if (t < 4)
 
			goto match_next;
 
		assert(t > 0); NEED_OP(t); NEED_IP(t+1);
 
		do *op++ = *ip++; while (--t > 0);
 
		goto first_literal_run;
 
	}
 

	
 
	while (TEST_IP && TEST_OP)
 
	{
 
		t = *ip++;
 
		if (t >= 16)
 
			goto match;
 
		if (t == 0)
 
		{
 
			NEED_IP(1);
 
			while (*ip == 0)
 
			{
 
				t += 255;
 
				ip++;
 
				NEED_IP(1);
 
			}
 
			t += 15 + *ip++;
 
		}
 
		assert(t > 0); NEED_OP(t+3); NEED_IP(t+4);
 
#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
 
#if !defined(LZO_UNALIGNED_OK_4)
 
		if (PTR_ALIGNED2_4(op,ip))
 
		{
 
#endif
 
		COPY4(op,ip);
 
		op += 4; ip += 4;
 
		if (--t > 0)
 
		{
 
			if (t >= 4)
 
			{
 
				do {
 
					COPY4(op,ip);
 
					op += 4; ip += 4; t -= 4;
 
				} while (t >= 4);
 
				if (t > 0) do *op++ = *ip++; while (--t > 0);
 
			}
 
			else
 
				do *op++ = *ip++; while (--t > 0);
 
		}
 
#if !defined(LZO_UNALIGNED_OK_4)
 
		}
 
		else
 
#endif
 
#endif
 
#if !defined(LZO_UNALIGNED_OK_4)
 
		{
 
			*op++ = *ip++; *op++ = *ip++; *op++ = *ip++;
 
			do *op++ = *ip++; while (--t > 0);
 
		}
 
#endif
 

	
 
first_literal_run:
 

	
 
		t = *ip++;
 
		if (t >= 16)
 
			goto match;
 
		m_pos = op - (1 + M2_MAX_OFFSET);
 
		m_pos -= t >> 2;
 
		m_pos -= *ip++ << 2;
 
		TEST_LOOKBEHIND(m_pos,out); NEED_OP(3);
 
		*op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos;
 
		goto match_done;
 

	
 
		while (TEST_IP && TEST_OP)
 
		{
 
match:
 
			if (t >= 64)
 
			{
 
#if defined(LZO1X)
 
				m_pos = op - 1;
 
				m_pos -= (t >> 2) & 7;
 
				m_pos -= *ip++ << 3;
 
				t = (t >> 5) - 1;
 
#endif
 
				TEST_LOOKBEHIND(m_pos,out); assert(t > 0); NEED_OP(t+3-1);
 
				goto copy_match;
 
			}
 
			else if (t >= 32)
 
			{
 
				t &= 31;
 
				if (t == 0)
 
				{
 
					NEED_IP(1);
 
					while (*ip == 0)
 
					{
 
						t += 255;
 
						ip++;
 
						NEED_IP(1);
 
					}
 
					t += 31 + *ip++;
 
				}
 
#if defined(LZO_UNALIGNED_OK_2) && (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN)
 
				m_pos = op - 1;
 
				m_pos -= (* (const lzo_ushortp) ip) >> 2;
 
#else
 
				m_pos = op - 1;
 
				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
 
#endif
 
				ip += 2;
 
			}
 
			else if (t >= 16)
 
			{
 
				m_pos = op;
 
				m_pos -= (t & 8) << 11;
 
				t &= 7;
 
				if (t == 0)
 
				{
 
					NEED_IP(1);
 
					while (*ip == 0)
 
					{
 
						t += 255;
 
						ip++;
 
						NEED_IP(1);
 
					}
 
					t += 7 + *ip++;
 
				}
 
#if defined(LZO_UNALIGNED_OK_2) && (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN)
 
				m_pos -= (* (const lzo_ushortp) ip) >> 2;
 
#else
 
				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
 
#endif
 
				ip += 2;
 
				if (m_pos == op)
 
					goto eof_found;
 
				m_pos -= 0x4000;
 
			}
 
			else
 
			{
 
				m_pos = op - 1;
 
				m_pos -= t >> 2;
 
				m_pos -= *ip++ << 2;
 
				TEST_LOOKBEHIND(m_pos,out); NEED_OP(2);
 
				*op++ = *m_pos++; *op++ = *m_pos;
 
				goto match_done;
 
			}
 

	
 
			TEST_LOOKBEHIND(m_pos,out); assert(t > 0); NEED_OP(t+3-1);
 
#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
 
#if !defined(LZO_UNALIGNED_OK_4)
 
			if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos))
 
			{
 
				assert((op - m_pos) >= 4);
 
#else
 
			if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4)
 
			{
 
#endif
 
				COPY4(op,m_pos);
 
				op += 4; m_pos += 4; t -= 4 - (3 - 1);
 
				do {
 
					COPY4(op,m_pos);
 
					op += 4; m_pos += 4; t -= 4;
 
				} while (t >= 4);
 
				if (t > 0) do *op++ = *m_pos++; while (--t > 0);
 
			}
 
			else
 
#endif
 
			{
 
copy_match:
 
				*op++ = *m_pos++; *op++ = *m_pos++;
 
				do *op++ = *m_pos++; while (--t > 0);
 
			}
 

	
 
match_done:
 
			t = ip[-2] & 3;
 
			if (t == 0)
 
				break;
 

	
 
match_next:
 
			assert(t > 0); NEED_OP(t); NEED_IP(t+1);
 
			do *op++ = *ip++; while (--t > 0);
 
			t = *ip++;
 
		}
 
	}
 

	
 
#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP)
 
	*out_len = op - out;
 
	return LZO_E_EOF_NOT_FOUND;
 
#endif
 

	
 
eof_found:
 
	assert(t == 1);
 
	*out_len = op - out;
 
	return (ip == ip_end ? LZO_E_OK :
 
		   (ip < ip_end  ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
 

	
 
#if defined(HAVE_NEED_IP)
 
input_overrun:
 
	*out_len = op - out;
 
	return LZO_E_INPUT_OVERRUN;
 
#endif
 

	
 
#if defined(HAVE_NEED_OP)
 
output_overrun:
 
	*out_len = op - out;
 
	return LZO_E_OUTPUT_OVERRUN;
 
#endif
 

	
 
#if defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND)
 
lookbehind_overrun:
 
	*out_len = op - out;
 
	return LZO_E_LOOKBEHIND_OVERRUN;
 
#endif
 
}
 

	
 
#define LZO_TEST_DECOMPRESS_OVERRUN
 
#undef DO_DECOMPRESS
 
#define DO_DECOMPRESS	   lzo1x_decompress_safe
 

	
 
#if defined(LZO_TEST_DECOMPRESS_OVERRUN)
 
#  if !defined(LZO_TEST_DECOMPRESS_OVERRUN_INPUT)
 
#	define LZO_TEST_DECOMPRESS_OVERRUN_INPUT	   2
 
#  endif
 
#  if !defined(LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT)
 
#	define LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT	  2
 
#  endif
 
#  if !defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND)
 
#	define LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND
 
#  endif
 
#endif
 

	
 
#undef TEST_IP
 
#undef TEST_OP
 
#undef TEST_LOOKBEHIND
 
#undef NEED_IP
 
#undef NEED_OP
 
#undef HAVE_TEST_IP
 
#undef HAVE_TEST_OP
 
#undef HAVE_NEED_IP
 
#undef HAVE_NEED_OP
 
#undef HAVE_ANY_IP
 
#undef HAVE_ANY_OP
 

	
 

	
 
#if 0
 

	
 
#if defined(LZO_TEST_DECOMPRESS_OVERRUN_INPUT)
 
#  if (LZO_TEST_DECOMPRESS_OVERRUN_INPUT >= 1)
 
#	define TEST_IP			 (ip < ip_end)
 
#  endif
 
#  if (LZO_TEST_DECOMPRESS_OVERRUN_INPUT >= 2)
 
#	define NEED_IP(x) \
 
			if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x))  goto input_overrun
 
#  endif
 
#endif
 

	
 
#if defined(LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT)
 
#  if (LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT >= 1)
 
#	define TEST_OP			 (op <= op_end)
 
#  endif
 
#  if (LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT >= 2)
 
#	undef TEST_OP
 
#	define NEED_OP(x) \
 
			if ((lzo_uint)(op_end - op) < (lzo_uint)(x))  goto output_overrun
 
#  endif
 
#endif
 

	
 
#if defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND)
 
#  define TEST_LOOKBEHIND(m_pos,out)	if (m_pos < out) goto lookbehind_overrun
 
#else
 
#  define TEST_LOOKBEHIND(m_pos,op)	 ((void) 0)
 
#endif
 

	
 
#if !defined(LZO_EOF_CODE) && !defined(TEST_IP)
 
#  define TEST_IP			   (ip < ip_end)
 
#endif
 

	
 
#if defined(TEST_IP)
 
#  define HAVE_TEST_IP
 
#else
 
#  define TEST_IP			   1
 
#endif
 
#if defined(TEST_OP)
 
#  define HAVE_TEST_OP
 
#else
 
#  define TEST_OP			   1
 
#endif
 

	
 
#if defined(NEED_IP)
 
#  define HAVE_NEED_IP
 
#else
 
#  define NEED_IP(x)			((void) 0)
 
#endif
 
#if defined(NEED_OP)
 
#  define HAVE_NEED_OP
 
#else
 
#  define NEED_OP(x)			((void) 0)
 
#endif
 

	
 
#if defined(HAVE_TEST_IP) || defined(HAVE_NEED_IP)
 
#  define HAVE_ANY_IP
 
#endif
 
#if defined(HAVE_TEST_OP) || defined(HAVE_NEED_OP)
 
#  define HAVE_ANY_OP
 
#endif
 

	
 
#undef __COPY4
 
#define __COPY4(dst,src)	* (lzo_uint32p)(dst) = * (const lzo_uint32p)(src)
 

	
 
#undef COPY4
 
#if defined(LZO_UNALIGNED_OK_4)
 
#  define COPY4(dst,src)	__COPY4(dst,src)
 
#elif defined(LZO_ALIGNED_OK_4)
 
#  define COPY4(dst,src)	__COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src))
 
#endif
 

	
 
#if defined(DO_DECOMPRESS)
 
LZO_PUBLIC(int)
 
DO_DECOMPRESS  ( const lzo_byte *in , lzo_uint  in_len,
 
					   lzo_byte *out, lzo_uintp out_len,
 
					   lzo_voidp wrkmem )
 
#endif
 
{
 
	register lzo_byte *op;
 
	register const lzo_byte *ip;
 
	register lzo_uint t;
 
	register const lzo_byte *m_pos;
 

	
 
	const lzo_byte * const ip_end = in + in_len;
 
#if defined(HAVE_ANY_OP)
 
	lzo_byte * const op_end = out + *out_len;
 
#endif
 

	
 
	LZO_UNUSED(wrkmem);
 

	
 
	*out_len = 0;
 

	
 
	op = out;
 
	ip = in;
 

	
 
	if (*ip > 17)
 
	{
 
		t = *ip++ - 17;
 
		if (t < 4)
 
			goto match_next;
 
		assert(t > 0); NEED_OP(t); NEED_IP(t+1);
 
		do *op++ = *ip++; while (--t > 0);
 
		goto first_literal_run;
 
	}
 

	
 
	while (TEST_IP && TEST_OP)
 
	{
 
		t = *ip++;
 
		if (t >= 16)
 
			goto match;
 
		if (t == 0)
 
		{
 
			NEED_IP(1);
 
			while (*ip == 0)
 
			{
 
				t += 255;
 
				ip++;
 
				NEED_IP(1);
 
			}
 
			t += 15 + *ip++;
 
		}
 
		assert(t > 0); NEED_OP(t+3); NEED_IP(t+4);
 
#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
 
#if !defined(LZO_UNALIGNED_OK_4)
 
		if (PTR_ALIGNED2_4(op,ip))
 
		{
 
#endif
 
		COPY4(op,ip);
 
		op += 4; ip += 4;
 
		if (--t > 0)
 
		{
 
			if (t >= 4)
 
			{
 
				do {
 
					COPY4(op,ip);
 
					op += 4; ip += 4; t -= 4;
 
				} while (t >= 4);
 
				if (t > 0) do *op++ = *ip++; while (--t > 0);
 
			}
 
			else
 
				do *op++ = *ip++; while (--t > 0);
 
		}
 
#if !defined(LZO_UNALIGNED_OK_4)
 
		}
 
		else
 
#endif
 
#endif
 
#if !defined(LZO_UNALIGNED_OK_4)
 
		{
 
			*op++ = *ip++; *op++ = *ip++; *op++ = *ip++;
 
			do *op++ = *ip++; while (--t > 0);
 
		}
 
#endif
 

	
 
first_literal_run:
 

	
 
		t = *ip++;
 
		if (t >= 16)
 
			goto match;
 
#if defined(LZO1Z)
 
		t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
 
		m_pos = op - t;
 
		last_m_off = t;
 
#else
 
		m_pos = op - (1 + M2_MAX_OFFSET);
 
		m_pos -= t >> 2;
 
		m_pos -= *ip++ << 2;
 
#endif
 
		TEST_LOOKBEHIND(m_pos,out); NEED_OP(3);
 
		*op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos;
 
		goto match_done;
 

	
 
		while (TEST_IP && TEST_OP)
 
		{
 
match:
 
			if (t >= 64)
 
			{
 
#if defined(LZO1X)
 
				m_pos = op - 1;
 
				m_pos -= (t >> 2) & 7;
 
				m_pos -= *ip++ << 3;
 
				t = (t >> 5) - 1;
 
#elif defined(LZO1Y)
 
				m_pos = op - 1;
 
				m_pos -= (t >> 2) & 3;
 
				m_pos -= *ip++ << 2;
 
				t = (t >> 4) - 3;
 
#elif defined(LZO1Z)
 
				{
 
					lzo_uint off = t & 0x1f;
 
					m_pos = op;
 
					if (off >= 0x1c)
 
					{
 
						assert(last_m_off > 0);
 
						m_pos -= last_m_off;
 
					}
 
					else
 
					{
 
						off = 1 + (off << 6) + (*ip++ >> 2);
 
						m_pos -= off;
 
						last_m_off = off;
 
					}
 
				}
 
				t = (t >> 5) - 1;
 
#endif
 
				TEST_LOOKBEHIND(m_pos,out); assert(t > 0); NEED_OP(t+3-1);
 
				goto copy_match;
 
			}
 
			else if (t >= 32)
 
			{
 
				t &= 31;
 
				if (t == 0)
 
				{
 
					NEED_IP(1);
 
					while (*ip == 0)
 
					{
 
						t += 255;
 
						ip++;
 
						NEED_IP(1);
 
					}
 
					t += 31 + *ip++;
 
				}
 
#if defined(LZO1Z)
 
				{
 
					lzo_uint off = 1 + (ip[0] << 6) + (ip[1] >> 2);
 
					m_pos = op - off;
 
					last_m_off = off;
 
				}
 
#elif defined(LZO_UNALIGNED_OK_2) && (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN)
 
				m_pos = op - 1;
 
				m_pos -= (* (const lzo_ushortp) ip) >> 2;
 
#else
 
				m_pos = op - 1;
 
				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
 
#endif
 
				ip += 2;
 
			}
 
			else if (t >= 16)
 
			{
 
				m_pos = op;
 
				m_pos -= (t & 8) << 11;
 
				t &= 7;
 
				if (t == 0)
 
				{
 
					NEED_IP(1);
 
					while (*ip == 0)
 
					{
 
						t += 255;
 
						ip++;
 
						NEED_IP(1);
 
					}
 
					t += 7 + *ip++;
 
				}
 
#if defined(LZO1Z)
 
				m_pos -= (ip[0] << 6) + (ip[1] >> 2);
 
#elif defined(LZO_UNALIGNED_OK_2) && (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN)
 
				m_pos -= (* (const lzo_ushortp) ip) >> 2;
 
#else
 
				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
 
#endif
 
				ip += 2;
 
				if (m_pos == op)
 
					goto eof_found;
 
				m_pos -= 0x4000;
 
#if defined(LZO1Z)
 
				last_m_off = op - m_pos;
 
#endif
 
			}
 
			else
 
			{
 
#if defined(LZO1Z)
 
				t = 1 + (t << 6) + (*ip++ >> 2);
 
				m_pos = op - t;
 
				last_m_off = t;
 
#else
 
				m_pos = op - 1;
 
				m_pos -= t >> 2;
 
				m_pos -= *ip++ << 2;
 
#endif
 
				TEST_LOOKBEHIND(m_pos,out); NEED_OP(2);
 
				*op++ = *m_pos++; *op++ = *m_pos;
 
				goto match_done;
 
			}
 

	
 
			TEST_LOOKBEHIND(m_pos,out); assert(t > 0); NEED_OP(t+3-1);
 
#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
 
#if !defined(LZO_UNALIGNED_OK_4)
 
			if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos))
 
			{
 
				assert((op - m_pos) >= 4);
 
#else
 
			if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4)
 
			{
 
#endif
 
				COPY4(op,m_pos);
 
				op += 4; m_pos += 4; t -= 4 - (3 - 1);
 
				do {
 
					COPY4(op,m_pos);
 
					op += 4; m_pos += 4; t -= 4;
 
				} while (t >= 4);
 
				if (t > 0) do *op++ = *m_pos++; while (--t > 0);
 
			}
 
			else
 
#endif
 
			{
 
copy_match:
 
				*op++ = *m_pos++; *op++ = *m_pos++;
 
				do *op++ = *m_pos++; while (--t > 0);
 
			}
 

	
 

	
 
match_done:
 
#if defined(LZO1Z)
 
			t = ip[-1] & 3;
 
#else
 
			t = ip[-2] & 3;
 
#endif
 
			if (t == 0)
 
				break;
 

	
 
match_next:
 
			assert(t > 0); NEED_OP(t); NEED_IP(t+1);
 
			do *op++ = *ip++; while (--t > 0);
 
			t = *ip++;
 
		}
 
	}
 

	
 
#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP)
 
	*out_len = op - out;
 
	return LZO_E_EOF_NOT_FOUND;
 
#endif
 

	
 
eof_found:
 
	assert(t == 1);
 
	*out_len = op - out;
 
	return (ip == ip_end ? LZO_E_OK :
 
		   (ip < ip_end  ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
 

	
 
#if defined(HAVE_NEED_IP)
 
input_overrun:
 
	*out_len = op - out;
 
	return LZO_E_INPUT_OVERRUN;
 
#endif
 

	
 
#if defined(HAVE_NEED_OP)
 
output_overrun:
 
	*out_len = op - out;
 
	return LZO_E_OUTPUT_OVERRUN;
 
#endif
 

	
 
#if defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND)
 
lookbehind_overrun:
 
	*out_len = op - out;
 
	return LZO_E_LOOKBEHIND_OVERRUN;
 
#endif
 
}
 

	
 
#endif
 

	
 
/***** End of minilzo.c *****/
src/misc.c
Show inline comments
 
deleted file
src/misc.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "currency.h"
 
#include "functions.h"
 
#include "news.h"
 
#include "player.h"
 
#include "string.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "map.h"
 
#include "vehicle.h"
 
#include "saveload.h"
 
#include "engine.h"
 
#include "vehicle_gui.h"
 
#include "variables.h"
 
#include "ai/ai.h"
 
#include "table/landscape_const.h"
 
#include "date.h"
 

	
 
char _name_array[512][32];
 

	
 
#ifndef MERSENNE_TWISTER
 

	
 
#ifdef RANDOM_DEBUG
 
#include "network/network_data.h"
 
uint32 DoRandom(int line, const char *file)
 
#else // RANDOM_DEBUG
 
uint32 Random(void)
 
#endif // RANDOM_DEBUG
 
{
 

	
 
uint32 s;
 
uint32 t;
 

	
 
#ifdef RANDOM_DEBUG
 
	if (_networking && (DEREF_CLIENT(0)->status != STATUS_INACTIVE || !_network_server))
 
		printf("Random [%d/%d] %s:%d\n",_frame_counter, _current_player, file, line);
 
#endif
 

	
 
	s = _random_seeds[0][0];
 
	t = _random_seeds[0][1];
 
	_random_seeds[0][0] = s + ROR(t ^ 0x1234567F, 7) + 1;
 
	return _random_seeds[0][1] = ROR(s, 3) - 1;
 
}
 
#endif // MERSENNE_TWISTER
 

	
 
#if defined(RANDOM_DEBUG) && !defined(MERSENNE_TWISTER)
 
uint DoRandomRange(uint max, int line, const char *file)
 
{
 
	return GB(DoRandom(line, file), 0, 16) * max >> 16;
 
}
 
#else
 
uint RandomRange(uint max)
 
{
 
	return GB(Random(), 0, 16) * max >> 16;
 
}
 
#endif
 

	
 

	
 
uint32 InteractiveRandom(void)
 
{
 
	uint32 t = _random_seeds[1][1];
 
	uint32 s = _random_seeds[1][0];
 
	_random_seeds[1][0] = s + ROR(t ^ 0x1234567F, 7) + 1;
 
	return _random_seeds[1][1] = ROR(s, 3) - 1;
 
}
 

	
 
uint InteractiveRandomRange(uint max)
 
{
 
	return GB(InteractiveRandom(), 0, 16) * max >> 16;
 
}
 

	
 
void InitializeVehicles(void);
 
void InitializeWaypoints(void);
 
void InitializeDepots(void);
 
void InitializeEngines(void);
 
void InitializeOrders(void);
 
void InitializeClearLand(void);
 
void InitializeRailGui(void);
 
void InitializeRoadGui(void);
 
void InitializeAirportGui(void);
 
void InitializeDockGui(void);
 
void InitializeIndustries(void);
 
void InitializeMainGui(void);
 
void InitializeLandscape(void);
 
void InitializeTowns(void);
 
void InitializeTrees(void);
 
void InitializeSigns(void);
 
void InitializeStations(void);
 
static void InitializeNameMgr(void);
 
void InitializePlayers(void);
 
static void InitializeCheats(void);
 
void InitializeNPF(void);
 

	
 
void InitializeGame(int mode, uint size_x, uint size_y)
 
{
 
	AllocateMap(size_x, size_y);
 

	
 
	AddTypeToEngines(); // make sure all engines have a type
 

	
 
	SetObjectToPlace(SPR_CURSOR_ZZZ, 0, 0, 0);
 

	
 
	_pause = 0;
 
	_fast_forward = 0;
 
	_tick_counter = 0;
 
	_date_fract = 0;
 
	_cur_tileloop_tile = 0;
 

	
 
	if ((mode & IG_DATE_RESET) == IG_DATE_RESET) {
 
		SetDate(ConvertYMDToDate(_patches.starting_year, 0, 1));
 
	}
 

	
 
	InitializeEngines();
 
	InitializeVehicles();
 
	InitializeWaypoints();
 
	InitializeDepots();
 
	InitializeOrders();
 

	
 
	InitNewsItemStructs();
 
	InitializeLandscape();
 
	InitializeClearLand();
 
	InitializeRailGui();
 
	InitializeRoadGui();
 
	InitializeAirportGui();
 
	InitializeDockGui();
 
	InitializeTowns();
 
	InitializeTrees();
 
	InitializeSigns();
 
	InitializeStations();
 
	InitializeIndustries();
 
	InitializeMainGui();
 

	
 
	InitializeNameMgr();
 
	InitializeVehiclesGuiList();
 
	InitializeTrains();
 
	InitializeNPF();
 

	
 
	AI_Initialize();
 
	InitializePlayers();
 
	InitializeCheats();
 

	
 
	InitTextEffects();
 
	InitTextMessage();
 
	InitializeAnimatedTiles();
 

	
 
	InitializeLandscapeVariables(false);
 

	
 
	ResetObjectToPlace();
 
}
 

	
 
bool IsCustomName(StringID id)
 
{
 
	return GB(id, 11, 5) == 15;
 
}
 

	
 
void DeleteName(StringID id)
 
{
 
	if (IsCustomName(id)) {
 
		memset(_name_array[id & 0x1FF], 0, sizeof(_name_array[id & 0x1FF]));
 
	}
 
}
 

	
 
char *GetName(char *buff, StringID id, const char* last)
 
{
 
	return strecpy(buff, _name_array[id & ~0x600], last);
 
}
 

	
 

	
 
static void InitializeCheats(void)
 
{
 
	memset(&_cheats, 0, sizeof(Cheats));
 
}
 

	
 

	
 
static void InitializeNameMgr(void)
 
{
 
	memset(_name_array, 0, sizeof(_name_array));
 
}
 

	
 
StringID RealAllocateName(const char *name, byte skip, bool check_double)
 
{
 
	char (*free_item)[lengthof(*_name_array)] = NULL;
 
	char (*i)[lengthof(*_name_array)];
 

	
 
	for (i = _name_array; i != endof(_name_array); ++i) {
 
		if ((*i)[0] == '\0') {
 
			if (free_item == NULL) free_item = i;
 
		} else if (check_double && strncmp(*i, name, lengthof(*i) - 1) == 0) {
 
			_error_message = STR_0132_CHOSEN_NAME_IN_USE_ALREADY;
 
			return 0;
 
		}
 
	}
 

	
 
	if (free_item != NULL) {
 
		ttd_strlcpy(*free_item, name, lengthof(*free_item));
 
		return (free_item - _name_array) | 0x7800 | (skip << 8);
 
	} else {
 
		_error_message = STR_0131_TOO_MANY_NAMES_DEFINED;
 
		return 0;
 
	}
 
}
 

	
 
void ConvertNameArray(void)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < lengthof(_name_array); i++) {
 
		const char *strfrom = _name_array[i];
 
		char tmp[sizeof(*_name_array)];
 
		char *strto = tmp;
 

	
 
		for (; *strfrom != '\0'; strfrom++) {
 
			WChar c = (byte)*strfrom;
 
			switch (c) {
 
				case 0xA4: c = 0x20AC; break; // Euro
 
				case 0xA6: c = 0x0160; break; // S with caron
 
				case 0xA8: c = 0x0161; break; // s with caron
 
				case 0xB4: c = 0x017D; break; // Z with caron
 
				case 0xB8: c = 0x017E; break; // z with caron
 
				case 0xBC: c = 0x0152; break; // OE ligature
 
				case 0xBD: c = 0x0153; break; // oe ligature
 
				case 0xBE: c = 0x0178; break; // Y with diaresis
 
				default: break;
 
			}
 
			if (strto + Utf8CharLen(c) > lastof(tmp)) break;
 
			strto += Utf8Encode(strto, c);
 
		}
 

	
 
		/* Terminate the new string and copy it back to the name array */
 
		*strto = '\0';
 
		memcpy(_name_array[i], tmp, sizeof(*_name_array));
 
	}
 
}
 

	
 
// Calculate constants that depend on the landscape type.
 
void InitializeLandscapeVariables(bool only_constants)
 
{
 
	const CargoTypesValues *lpd;
 
	uint i;
 
	StringID str;
 

	
 
	lpd = &_cargo_types_base_values[_opt.landscape];
 

	
 
	for (i = 0; i != NUM_CARGO; i++) {
 
		_cargoc.sprites[i] = lpd->sprites[i];
 

	
 
		str = lpd->names[i];
 
		_cargoc.names_s[i] = str;
 
		_cargoc.names_long[i] = (str += 0x40);
 
		_cargoc.names_short[i] = (str += 0x20);
 
		_cargoc.weights[i] = lpd->weights[i];
 

	
 
		if (!only_constants) {
 
			_cargo_payment_rates[i] = lpd->initial_cargo_payment[i];
 
			_cargo_payment_rates_frac[i] = 0;
 
		}
 

	
 
		_cargoc.transit_days_1[i] = lpd->transit_days_table_1[i];
 
		_cargoc.transit_days_2[i] = lpd->transit_days_table_2[i];
 
	}
 
}
 

	
 

	
 

	
 
int FindFirstBit(uint32 value)
 
{
 
	// This is much faster than the one that was before here.
 
	//  Created by Darkvater.. blame him if it is wrong ;)
 
	// Btw, the macro FINDFIRSTBIT is better to use when your value is
 
	//  not more than 128.
 
	byte i = 0;
 
	if (value & 0xffff0000) { value >>= 16; i += 16; }
 
	if (value & 0x0000ff00) { value >>= 8;  i +=  8; }
 
	if (value & 0x000000f0) { value >>= 4;  i +=  4; }
 
	if (value & 0x0000000c) { value >>= 2;  i +=  2; }
 
	if (value & 0x00000002) { i += 1; }
 
	return i;
 
}
 

	
 

	
 
static void Save_NAME(void)
 
{
 
	int i;
 

	
 
	for (i = 0; i != lengthof(_name_array); ++i) {
 
		if (_name_array[i][0] != '\0') {
 
			SlSetArrayIndex(i);
 
			SlArray(_name_array[i], (uint)strlen(_name_array[i]), SLE_UINT8);
 
		}
 
	}
 
}
 

	
 
static void Load_NAME(void)
 
{
 
	int index;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		SlArray(_name_array[index],SlGetFieldLength(),SLE_UINT8);
 
	}
 
}
 

	
 
static const SaveLoadGlobVarList _date_desc[] = {
 
	SLEG_CONDVAR(_date,                   SLE_FILE_U16 | SLE_VAR_I32,  0,  30),
 
	SLEG_CONDVAR(_date,                   SLE_INT32,                  31, SL_MAX_VERSION),
 
	    SLEG_VAR(_date_fract,             SLE_UINT16),
 
	    SLEG_VAR(_tick_counter,           SLE_UINT16),
 
	    SLEG_VAR(_vehicle_id_ctr_day,     SLE_UINT16),
 
	    SLEG_VAR(_age_cargo_skip_counter, SLE_UINT8),
 
	    SLEG_VAR(_avail_aircraft,         SLE_UINT8),
 
	SLEG_CONDVAR(_cur_tileloop_tile,      SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLEG_CONDVAR(_cur_tileloop_tile,      SLE_UINT32,                  6, SL_MAX_VERSION),
 
	    SLEG_VAR(_disaster_delay,         SLE_UINT16),
 
	    SLEG_VAR(_station_tick_ctr,       SLE_UINT16),
 
	    SLEG_VAR(_random_seeds[0][0],     SLE_UINT32),
 
	    SLEG_VAR(_random_seeds[0][1],     SLE_UINT32),
 
	SLEG_CONDVAR(_cur_town_ctr,           SLE_FILE_U8  | SLE_VAR_U32,  0, 9),
 
	SLEG_CONDVAR(_cur_town_ctr,           SLE_UINT32,                 10, SL_MAX_VERSION),
 
	    SLEG_VAR(_cur_player_tick_index,  SLE_FILE_U8  | SLE_VAR_U32),
 
	    SLEG_VAR(_next_competitor_start,  SLE_FILE_U16 | SLE_VAR_U32),
 
	    SLEG_VAR(_trees_tick_ctr,         SLE_UINT8),
 
	SLEG_CONDVAR(_pause,                  SLE_UINT8,                   4, SL_MAX_VERSION),
 
	SLEG_CONDVAR(_cur_town_iter,          SLE_UINT32,                 11, SL_MAX_VERSION),
 
	    SLEG_END()
 
};
 

	
 
// Save load date related variables as well as persistent tick counters
 
// XXX: currently some unrelated stuff is just put here
 
static void SaveLoad_DATE(void)
 
{
 
	SlGlobList(_date_desc);
 
}
 

	
 

	
 
static const SaveLoadGlobVarList _view_desc[] = {
 
	SLEG_CONDVAR(_saved_scrollpos_x,    SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
 
	SLEG_CONDVAR(_saved_scrollpos_x,    SLE_INT32,                  6, SL_MAX_VERSION),
 
	SLEG_CONDVAR(_saved_scrollpos_y,    SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
 
	SLEG_CONDVAR(_saved_scrollpos_y,    SLE_INT32,                  6, SL_MAX_VERSION),
 
	    SLEG_VAR(_saved_scrollpos_zoom, SLE_UINT8),
 
	    SLEG_END()
 
};
 

	
 
static void SaveLoad_VIEW(void)
 
{
 
	SlGlobList(_view_desc);
 
}
 

	
 
static uint32 _map_dim_x;
 
static uint32 _map_dim_y;
 

	
 
static const SaveLoadGlobVarList _map_dimensions[] = {
 
	SLEG_CONDVAR(_map_dim_x, SLE_UINT32, 6, SL_MAX_VERSION),
 
	SLEG_CONDVAR(_map_dim_y, SLE_UINT32, 6, SL_MAX_VERSION),
 
	    SLEG_END()
 
};
 

	
 
static void Save_MAPS(void)
 
{
 
	_map_dim_x = MapSizeX();
 
	_map_dim_y = MapSizeY();
 
	SlGlobList(_map_dimensions);
 
}
 

	
 
static void Load_MAPS(void)
 
{
 
	SlGlobList(_map_dimensions);
 
	AllocateMap(_map_dim_x, _map_dim_y);
 
}
 

	
 
static void Load_MAPT(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	for (i = 0; i != size;) {
 
		byte buf[4096];
 
		uint j;
 

	
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
		for (j = 0; j != lengthof(buf); j++) _m[i++].type_height = buf[j];
 
	}
 
}
 

	
 
static void Save_MAPT(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	SlSetLength(size);
 
	for (i = 0; i != size;) {
 
		byte buf[4096];
 
		uint j;
 

	
 
		for (j = 0; j != lengthof(buf); j++) buf[j] = _m[i++].type_height;
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
	}
 
}
 

	
 
static void Load_MAP1(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	for (i = 0; i != size;) {
 
		byte buf[4096];
 
		uint j;
 

	
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
		for (j = 0; j != lengthof(buf); j++) _m[i++].m1 = buf[j];
 
	}
 
}
 

	
 
static void Save_MAP1(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	SlSetLength(size);
 
	for (i = 0; i != size;) {
 
		byte buf[4096];
 
		uint j;
 

	
 
		for (j = 0; j != lengthof(buf); j++) buf[j] = _m[i++].m1;
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
	}
 
}
 

	
 
static void Load_MAP2(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	for (i = 0; i != size;) {
 
		uint16 buf[4096];
 
		uint j;
 

	
 
		SlArray(buf, lengthof(buf),
 
			/* In those versions the m2 was 8 bits */
 
			CheckSavegameVersion(5) ? SLE_FILE_U8 | SLE_VAR_U16 : SLE_UINT16
 
		);
 
		for (j = 0; j != lengthof(buf); j++) _m[i++].m2 = buf[j];
 
	}
 
}
 

	
 
static void Save_MAP2(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	SlSetLength(size * sizeof(_m[0].m2));
 
	for (i = 0; i != size;) {
 
		uint16 buf[4096];
 
		uint j;
 

	
 
		for (j = 0; j != lengthof(buf); j++) buf[j] = _m[i++].m2;
 
		SlArray(buf, lengthof(buf), SLE_UINT16);
 
	}
 
}
 

	
 
static void Load_MAP3(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	for (i = 0; i != size;) {
 
		byte buf[4096];
 
		uint j;
 

	
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
		for (j = 0; j != lengthof(buf); j++) _m[i++].m3 = buf[j];
 
	}
 
}
 

	
 
static void Save_MAP3(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	SlSetLength(size);
 
	for (i = 0; i != size;) {
 
		byte buf[4096];
 
		uint j;
 

	
 
		for (j = 0; j != lengthof(buf); j++) buf[j] = _m[i++].m3;
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
	}
 
}
 

	
 
static void Load_MAP4(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	for (i = 0; i != size;) {
 
		byte buf[4096];
 
		uint j;
 

	
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
		for (j = 0; j != lengthof(buf); j++) _m[i++].m4 = buf[j];
 
	}
 
}
 

	
 
static void Save_MAP4(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	SlSetLength(size);
 
	for (i = 0; i != size;) {
 
		byte buf[4096];
 
		uint j;
 

	
 
		for (j = 0; j != lengthof(buf); j++) buf[j] = _m[i++].m4;
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
	}
 
}
 

	
 
static void Load_MAP5(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	for (i = 0; i != size;) {
 
		byte buf[4096];
 
		uint j;
 

	
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
		for (j = 0; j != lengthof(buf); j++) _m[i++].m5 = buf[j];
 
	}
 
}
 

	
 
static void Save_MAP5(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	SlSetLength(size);
 
	for (i = 0; i != size;) {
 
		byte buf[4096];
 
		uint j;
 

	
 
		for (j = 0; j != lengthof(buf); j++) buf[j] = _m[i++].m5;
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
	}
 
}
 

	
 
static void Load_MAPE(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	if (CheckSavegameVersion(42)) {
 
		for (i = 0; i != size;) {
 
			uint8 buf[1024];
 
			uint j;
 

	
 
			SlArray(buf, lengthof(buf), SLE_UINT8);
 
			for (j = 0; j != lengthof(buf); j++) {
 
				_m[i++].extra = GB(buf[j], 0, 2);
 
				_m[i++].extra = GB(buf[j], 2, 2);
 
				_m[i++].extra = GB(buf[j], 4, 2);
 
				_m[i++].extra = GB(buf[j], 6, 2);
 
			}
 
		}
 
	} else {
 
		for (i = 0; i != size;) {
 
			byte buf[4096];
 
			uint j;
 

	
 
			SlArray(buf, lengthof(buf), SLE_UINT8);
 
			for (j = 0; j != lengthof(buf); j++) _m[i++].extra = buf[j];
 
		}
 
	}
 
}
 

	
 
static void Save_MAPE(void)
 
{
 
	uint size = MapSize();
 
	uint i;
 

	
 
	SlSetLength(size);
 
	for (i = 0; i != size;) {
 
		uint8 buf[4096];
 
		uint j;
 

	
 
		for (j = 0; j != lengthof(buf); j++) buf[j] = _m[i++].extra;
 
		SlArray(buf, lengthof(buf), SLE_UINT8);
 
	}
 
}
 

	
 

	
 
static void Save_CHTS(void)
 
{
 
	byte count = sizeof(_cheats)/sizeof(Cheat);
 
	Cheat* cht = (Cheat*) &_cheats;
 
	Cheat* cht_last = &cht[count];
 

	
 
	SlSetLength(count * 2);
 
	for (; cht != cht_last; cht++) {
 
		SlWriteByte(cht->been_used);
 
		SlWriteByte(cht->value);
 
	}
 
}
 

	
 
static void Load_CHTS(void)
 
{
 
	Cheat* cht = (Cheat*)&_cheats;
 
	uint count = SlGetFieldLength() / 2;
 
	uint i;
 

	
 
	for (i = 0; i < count; i++) {
 
		cht[i].been_used = SlReadByte();
 
		cht[i].value     = SlReadByte();
 
	}
 
}
 

	
 

	
 
const ChunkHandler _misc_chunk_handlers[] = {
 
	{ 'MAPS', Save_MAPS,     Load_MAPS,     CH_RIFF },
 
	{ 'MAPT', Save_MAPT,     Load_MAPT,     CH_RIFF },
 
	{ 'MAPO', Save_MAP1,     Load_MAP1,     CH_RIFF },
 
	{ 'MAP2', Save_MAP2,     Load_MAP2,     CH_RIFF },
 
	{ 'M3LO', Save_MAP3,     Load_MAP3,     CH_RIFF },
 
	{ 'M3HI', Save_MAP4,     Load_MAP4,     CH_RIFF },
 
	{ 'MAP5', Save_MAP5,     Load_MAP5,     CH_RIFF },
 
	{ 'MAPE', Save_MAPE,     Load_MAPE,     CH_RIFF },
 

	
 
	{ 'NAME', Save_NAME,     Load_NAME,     CH_ARRAY},
 
	{ 'DATE', SaveLoad_DATE, SaveLoad_DATE, CH_RIFF},
 
	{ 'VIEW', SaveLoad_VIEW, SaveLoad_VIEW, CH_RIFF},
 
	{ 'CHTS', Save_CHTS,     Load_CHTS,     CH_RIFF | CH_LAST}
 
};
src/misc_cmd.c
Show inline comments
 
deleted file
src/misc_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "string.h"
 
#include "table/strings.h"
 
#include "command.h"
 
#include "player.h"
 
#include "gfx.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "economy.h"
 
#include "network/network.h"
 
#include "variables.h"
 
#include "livery.h"
 

	
 
/** Change the player's face.
 
 * @param tile unused
 
 * @param p1 unused
 
 * @param p2 face bitmasked
 
 */
 
int32 CmdSetPlayerFace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (flags & DC_EXEC) {
 
		GetPlayer(_current_player)->face = p2;
 
		MarkWholeScreenDirty();
 
	}
 
	return 0;
 
}
 

	
 
/** Change the player's company-colour
 
 * @param tile unused
 
 * @param p1 bitstuffed:
 
 * p1 bits 0-7 scheme to set
 
 * p1 bits 8-9 set in use state or first/second colour
 
 * @param p2 new colour for vehicles, property, etc.
 
 */
 
int32 CmdSetPlayerColor(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Player *p, *pp;
 
	byte colour;
 
	LiveryScheme scheme = GB(p1, 0, 8);
 
	byte state = GB(p1, 8, 2);
 

	
 
	if (p2 >= 16) return CMD_ERROR; // max 16 colours
 
	colour = p2;
 

	
 
	if (scheme >= LS_END || state >= 3) return CMD_ERROR;
 

	
 
	p = GetPlayer(_current_player);
 

	
 
	/* Ensure no two companies have the same primary colour */
 
	if (scheme == LS_DEFAULT && state == 0) {
 
		FOR_ALL_PLAYERS(pp) {
 
			if (pp->is_active && pp != p && pp->player_color == colour) return CMD_ERROR;
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		switch (state) {
 
			case 0:
 
				p->livery[scheme].colour1 = colour;
 

	
 
				/* If setting the first colour of the default scheme, adjust the
 
				 * original and cached player colours too. */
 
				if (scheme == LS_DEFAULT) {
 
					_player_colors[_current_player] = colour;
 
					p->player_color = colour;
 
				}
 
				break;
 

	
 
			case 1:
 
				p->livery[scheme].colour2 = colour;
 
				break;
 

	
 
			case 2:
 
				p->livery[scheme].in_use = colour != 0;
 

	
 
				/* Now handle setting the default scheme's in_use flag.
 
				 * This is different to the other schemes, as it signifies if any
 
				 * scheme is active at all. If this flag is not set, then no
 
				 * processing of vehicle types occurs at all, and only the default
 
				 * colours will be used. */
 

	
 
				/* If enabling a scheme, set the default scheme to be in use too */
 
				if (colour != 0) {
 
					p->livery[LS_DEFAULT].in_use = true;
 
					break;
 
				}
 

	
 
				/* Else loop through all schemes to see if any are left enabled.
 
				 * If not, disable the default scheme too. */
 
				p->livery[LS_DEFAULT].in_use = false;
 
				for (scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
 
					if (p->livery[scheme].in_use) {
 
						p->livery[LS_DEFAULT].in_use = true;
 
						break;
 
					}
 
				}
 
				break;
 

	
 
			default:
 
				break;
 
		}
 
		MarkWholeScreenDirty();
 
	}
 
	return 0;
 
}
 

	
 
/** Increase the loan of your company.
 
 * @param tile unused
 
 * @param p1 unused
 
 * @param p2 when set, loans the maximum amount in one go (press CTRL)
 
 */
 
int32 CmdIncreaseLoan(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Player *p;
 

	
 
	p = GetPlayer(_current_player);
 

	
 
	if (p->current_loan >= _economy.max_loan) {
 
		SetDParam(0, _economy.max_loan);
 
		return_cmd_error(STR_702B_MAXIMUM_PERMITTED_LOAN);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		/* Loan the maximum amount or not? */
 
		int32 loan = (p2) ? _economy.max_loan - p->current_loan : (IsHumanPlayer(_current_player) || _patches.ainew_active) ? 10000 : 50000;
 

	
 
		p->money64 += loan;
 
		p->current_loan += loan;
 
		UpdatePlayerMoney32(p);
 
		InvalidatePlayerWindows(p);
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Decrease the loan of your company.
 
 * @param tile unused
 
 * @param p1 unused
 
 * @param p2 when set, pays back the maximum loan permitting money (press CTRL)
 
 */
 
int32 CmdDecreaseLoan(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Player *p;
 
	int32 loan;
 

	
 
	p = GetPlayer(_current_player);
 

	
 
	if (p->current_loan == 0) return_cmd_error(STR_702D_LOAN_ALREADY_REPAYED);
 

	
 
	loan = p->current_loan;
 

	
 
	/* p2 is true while CTRL is pressed (repay all possible loan, or max money you have)
 
	 * Repay any loan in chunks of 10.000 pounds */
 
	if (p2) {
 
		loan = min(loan, p->player_money);
 
		loan = max(loan, 10000);
 
		loan -= loan % 10000;
 
	} else {
 
		loan = min(loan, (IsHumanPlayer(_current_player) || _patches.ainew_active) ? 10000 : 50000);
 
	}
 

	
 
	if (p->player_money < loan) {
 
		SetDParam(0, loan);
 
		return_cmd_error(STR_702E_REQUIRED);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		p->money64 -= loan;
 
		p->current_loan -= loan;
 
		UpdatePlayerMoney32(p);
 
		InvalidatePlayerWindows(p);
 
	}
 
	return 0;
 
}
 

	
 
/** Change the name of the company.
 
 * @param tile unused
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdChangeCompanyName(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	StringID str;
 
	Player *p;
 

	
 
	if (_cmd_text[0] == '\0') return CMD_ERROR;
 

	
 
	str = AllocateNameUnique(_cmd_text, 4);
 
	if (str == 0) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		p = GetPlayer(_current_player);
 
		DeleteName(p->name_1);
 
		p->name_1 = str;
 
		MarkWholeScreenDirty();
 
	} else {
 
		DeleteName(str);
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Change the name of the president.
 
 * @param tile unused
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdChangePresidentName(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	StringID str;
 
	Player *p;
 

	
 
	if (_cmd_text[0] == '\0') return CMD_ERROR;
 

	
 
	str = AllocateNameUnique(_cmd_text, 4);
 
	if (str == 0) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		p = GetPlayer(_current_player);
 
		DeleteName(p->president_name_1);
 
		p->president_name_1 = str;
 

	
 
		if (p->name_1 == STR_SV_UNNAMED) {
 
			char buf[80];
 

	
 
			snprintf(buf, lengthof(buf), "%s Transport", _cmd_text);
 
			_cmd_text = buf;
 
			DoCommand(0, 0, 0, DC_EXEC, CMD_CHANGE_COMPANY_NAME);
 
		}
 
		MarkWholeScreenDirty();
 
	} else {
 
		DeleteName(str);
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Pause/Unpause the game (server-only).
 
 * Increase or decrease the pause counter. If the counter is zero,
 
 * the game is unpaused. A counter is used instead of a boolean value
 
 * to have more control over the game when saving/loading, etc.
 
 * @param tile unused
 
 * @param p1 0 = decrease pause counter; 1 = increase pause counter
 
 * @param p2 unused
 
 */
 
int32 CmdPause(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (flags & DC_EXEC) {
 
		_pause += (p1 == 1) ? 1 : -1;
 
		if (_pause == (byte)-1) _pause = 0;
 
		InvalidateWindow(WC_STATUS_BAR, 0);
 
		InvalidateWindow(WC_MAIN_TOOLBAR, 0);
 
	}
 
	return 0;
 
}
 

	
 
/** Change the financial flow of your company.
 
 * This is normally only enabled in offline mode, but if there is a debug
 
 * build, you can cheat (to test).
 
 * @param tile unused
 
 * @param p1 the amount of money to receive (if negative), or spend (if positive)
 
 * @param p2 unused
 
 */
 
int32 CmdMoneyCheat(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
#ifndef _DEBUG
 
	if (_networking) return CMD_ERROR;
 
#endif
 
	SET_EXPENSES_TYPE(EXPENSES_OTHER);
 
	return (int32)p1;
 
}
 

	
 
/** Transfer funds (money) from one player to another.
 
 * To prevent abuse in multiplayer games you can only send money to other
 
 * players if you have paid off your loan (either explicitely, or implicitely
 
 * given the fact that you have more money than loan).
 
 * @param tile unused
 
 * @param p1 the amount of money to transfer; max 20.000.000
 
 * @param p2 the player to transfer the money to
 
 */
 
int32 CmdGiveMoney(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	const Player *p = GetPlayer(_current_player);
 
	int32 amount = min((int32)p1, 20000000);
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_OTHER);
 

	
 
	/* You can only transfer funds that is in excess of your loan */
 
	if (p->money64 - p->current_loan < amount || amount <= 0) return CMD_ERROR;
 
	if (!_networking || !IsValidPlayer((PlayerID)p2)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		/* Add money to player */
 
		PlayerID old_cp = _current_player;
 
		_current_player = p2;
 
		SubtractMoneyFromPlayer(-amount);
 
		_current_player = old_cp;
 
	}
 

	
 
	/* Subtract money from local-player */
 
	return amount;
 
}
 

	
 
/** Change difficulty level/settings (server-only).
 
 * We cannot really check for valid values of p2 (too much work mostly); stored
 
 * in file 'settings_gui.c' _game_setting_info[]; we'll just trust the server it knows
 
 * what to do and does this correctly
 
 * @param tile unused
 
 * @param p1 the difficulty setting being changed. If it is -1, the difficulty level
 
 *           itself is changed. The new value is inside p2
 
 * @param p2 new value for a difficulty setting or difficulty level
 
 */
 
int32 CmdChangeDifficultyLevel(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (p1 != (uint32)-1L && ((int32)p1 >= GAME_DIFFICULTY_NUM || (int32)p1 < 0)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		if (p1 != (uint32)-1L) {
 
			((int*)&_opt_ptr->diff)[p1] = p2;
 
			_opt_ptr->diff_level = 3; // custom difficulty level
 
		} else {
 
			_opt_ptr->diff_level = p2;
 
		}
 

	
 
		/* If we are a network-client, update the difficult setting (if it is open).
 
		 * Use this instead of just dirtying the window because we need to load in
 
		 * the new difficulty settings */
 
		if (_networking && !_network_server && FindWindowById(WC_GAME_OPTIONS, 0) != NULL)
 
			ShowGameDifficulty();
 
	}
 
	return 0;
 
}
src/misc_gui.c
Show inline comments
 
deleted file
src/misc_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "hal.h"
 
#include "heightmap.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "newgrf.h"
 
#include "saveload.h"
 
#include "strings.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "table/tree_land.h"
 
#include "map.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "station.h"
 
#include "command.h"
 
#include "player.h"
 
#include "town.h"
 
#include "sound.h"
 
#include "network/network.h"
 
#include "string.h"
 
#include "variables.h"
 
#include "vehicle.h"
 
#include "train.h"
 
#include "tgp.h"
 
#include "settings.h"
 
#include "date.h"
 

	
 
#include "fios.h"
 
/* Variables to display file lists */
 
FiosItem *_fios_list;
 
int _saveload_mode;
 

	
 
extern void GenerateLandscape(byte mode);
 
extern void SwitchMode(int new_mode);
 

	
 
static bool _fios_path_changed;
 
static bool _savegame_sort_dirty;
 

	
 
enum {
 
	LAND_INFO_LINES          =   7,
 
	LAND_INFO_LINE_BUFF_SIZE = 512,
 
};
 

	
 
static char _landinfo_data[LAND_INFO_LINES][LAND_INFO_LINE_BUFF_SIZE];
 

	
 
static void LandInfoWndProc(Window *w, WindowEvent *e)
 
{
 
	if (e->event == WE_PAINT) {
 
		DrawWindowWidgets(w);
 

	
 
		DoDrawStringCentered(140, 16, _landinfo_data[0], 13);
 
		DoDrawStringCentered(140, 27, _landinfo_data[1], 0);
 
		DoDrawStringCentered(140, 38, _landinfo_data[2], 0);
 
		DoDrawStringCentered(140, 49, _landinfo_data[3], 0);
 
		DoDrawStringCentered(140, 60, _landinfo_data[4], 0);
 
		if (_landinfo_data[5][0] != '\0') DrawStringMultiCenter(140, 76, BindCString(_landinfo_data[5]), 276);
 
		if (_landinfo_data[6][0] != '\0') DoDrawStringCentered(140, 71, _landinfo_data[6], 0);
 
	}
 
}
 

	
 
static const Widget _land_info_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   279,     0,    13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   279,    14,    92, 0x0,                            STR_NULL},
 
{    WIDGETS_END},
 
};
 

	
 
static const WindowDesc _land_info_desc = {
 
	WDP_AUTO, WDP_AUTO, 280, 93,
 
	WC_LAND_INFO,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_land_info_widgets,
 
	LandInfoWndProc
 
};
 

	
 
static void Place_LandInfo(TileIndex tile)
 
{
 
	Player *p;
 
	Window *w;
 
	Town *t;
 
	int64 old_money;
 
	int64 costclear;
 
	AcceptedCargo ac;
 
	TileDesc td;
 
	StringID str;
 

	
 
	DeleteWindowById(WC_LAND_INFO, 0);
 

	
 
	w = AllocateWindowDesc(&_land_info_desc);
 
	WP(w, void_d).data = &_landinfo_data;
 

	
 
	p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : 0);
 
	t = ClosestTownFromTile(tile, _patches.dist_local_authority);
 

	
 
	old_money = p->money64;
 
	p->money64 = p->player_money = 0x7fffffff;
 
	costclear = DoCommand(tile, 0, 0, 0, CMD_LANDSCAPE_CLEAR);
 
	p->money64 = old_money;
 
	UpdatePlayerMoney32(p);
 

	
 
	/* Because build_date is not set yet in every TileDesc, we make sure it is empty */
 
	td.build_date = 0;
 
	GetAcceptedCargo(tile, ac);
 
	GetTileDesc(tile, &td);
 

	
 
	SetDParam(0, td.dparam[0]);
 
	GetString(_landinfo_data[0], td.str, lastof(_landinfo_data[0]));
 

	
 
	SetDParam(0, STR_01A6_N_A);
 
	if (td.owner != OWNER_NONE && td.owner != OWNER_WATER) GetNameOfOwner(td.owner, tile);
 
	GetString(_landinfo_data[1], STR_01A7_OWNER, lastof(_landinfo_data[1]));
 

	
 
	str = STR_01A4_COST_TO_CLEAR_N_A;
 
	if (!CmdFailed(costclear)) {
 
		SetDParam(0, costclear);
 
		str = STR_01A5_COST_TO_CLEAR;
 
	}
 
	GetString(_landinfo_data[2], str, lastof(_landinfo_data[2]));
 

	
 
	snprintf(_userstring, lengthof(_userstring), "0x%.4X", tile);
 
	SetDParam(0, TileX(tile));
 
	SetDParam(1, TileY(tile));
 
	SetDParam(2, STR_SPEC_USERSTRING);
 
	GetString(_landinfo_data[3], STR_LANDINFO_COORDS, lastof(_landinfo_data[3]));
 

	
 
	SetDParam(0, STR_01A9_NONE);
 
	if (t != NULL && IsValidTown(t)) {
 
		SetDParam(0, STR_TOWN);
 
		SetDParam(1, t->index);
 
	}
 
	GetString(_landinfo_data[4], STR_01A8_LOCAL_AUTHORITY, lastof(_landinfo_data[4]));
 

	
 
	{
 
		int i;
 
		char *p = GetString(_landinfo_data[5], STR_01CE_CARGO_ACCEPTED, lastof(_landinfo_data[5]));
 
		bool found = false;
 

	
 
		for (i = 0; i < NUM_CARGO; ++i) {
 
			if (ac[i] > 0) {
 
				/* Add a comma between each item. */
 
				if (found) {
 
					*p++ = ',';
 
					*p++ = ' ';
 
				}
 
				found = true;
 

	
 
				/* If the accepted value is less than 8, show it in 1/8:ths */
 
				if (ac[i] < 8) {
 
					SetDParam(0, ac[i]);
 
					SetDParam(1, _cargoc.names_s[i]);
 
					p = GetString(p, STR_01D1_8, lastof(_landinfo_data[5]));
 
				} else {
 
					p = GetString(p, _cargoc.names_s[i], lastof(_landinfo_data[5]));
 
				}
 
			}
 
		}
 

	
 
		if (!found) _landinfo_data[5][0] = '\0';
 
	}
 

	
 
	if (td.build_date != 0) {
 
		SetDParam(0, td.build_date);
 
		GetString(_landinfo_data[6], STR_BUILD_DATE, lastof(_landinfo_data[6]));
 
	} else {
 
		_landinfo_data[6][0] = '\0';
 
	}
 

	
 
#if defined(_DEBUG)
 
#	define LANDINFOD_LEVEL 0
 
#else
 
#	define LANDINFOD_LEVEL 1
 
#endif
 
	DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile));
 
	DEBUG(misc, LANDINFOD_LEVEL, "type_height  = %#x", _m[tile].type_height);
 
	DEBUG(misc, LANDINFOD_LEVEL, "m1           = %#x", _m[tile].m1);
 
	DEBUG(misc, LANDINFOD_LEVEL, "m2           = %#x", _m[tile].m2);
 
	DEBUG(misc, LANDINFOD_LEVEL, "m3           = %#x", _m[tile].m3);
 
	DEBUG(misc, LANDINFOD_LEVEL, "m4           = %#x", _m[tile].m4);
 
	DEBUG(misc, LANDINFOD_LEVEL, "m5           = %#x", _m[tile].m5);
 
	DEBUG(misc, LANDINFOD_LEVEL, "extra        = %#x", _m[tile].extra);
 
#undef LANDINFOD_LEVEL
 
}
 

	
 
void PlaceLandBlockInfo(void)
 
{
 
	if (_cursor.sprite == SPR_CURSOR_QUERY) {
 
		ResetObjectToPlace();
 
	} else {
 
		_place_proc = Place_LandInfo;
 
		SetObjectToPlace(SPR_CURSOR_QUERY, 1, 1, 0);
 
	}
 
}
 

	
 
static const char *credits[] = {
 
	/*************************************************************************
 
	 *                      maximum length of string which fits in window   -^*/
 
	"Original design by Chris Sawyer",
 
	"Original graphics by Simon Foster",
 
	"",
 
	"The OpenTTD team (in alphabetical order):",
 
	"  Jean-Francois Claeys (Belugas) - In training, not yet specialized",
 
	"  Bjarni Corfitzen (Bjarni) - MacOSX port, coder",
 
	"  Matthijs Kooijman (blathijs) - Pathfinder-guru",
 
	"  Victor Fischer (Celestar) - Programming everywhere you need him to",
 
	"  Tamás Faragó (Darkvater) - Lead coder",
 
	"  Loïc Guilloux (glx) - In training, not yet specialized",
 
	"  Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;)",
 
	"  Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host",
 
	"  Peter Nelson (peter1138) - Spiritual descendant from newgrf gods",
 
	"  Remko Bijker (Rubidium) - Belugas code scrutinizer",
 
	"  Christoph Mallon (Tron) - Programmer, code correctness police",
 
	"  Patric Stout (TrueLight) - Coder, network guru, SVN- and website host",
 
	"",
 
	"Retired Developers:",
 
	"  Ludvig Strigeus (ludde) - OpenTTD author, main coder (0.1 - 0.3.3)",
 
	"  Serge Paquet (vurlix) - Assistant project manager, coder (0.1 - 0.3.3)",
 
	"  Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3.0 - 0.3.6)",
 
	"  Owen Rudge (orudge) - Forum- and masterserver host, OS/2 port (0.1 - 0.4.8)",
 
	"",
 
	"Special thanks go out to:",
 
	"  Josef Drexler - For his great work on TTDPatch",
 
	"  Marcin Grzegorczyk - For his documentation of TTD internals",
 
	"  Petr Baudis (pasky) - Many patches, newgrf support",
 
	"  Stefan Meißner (sign_de) - For his work on the console",
 
	"  Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with (and PBS)",
 
	"  Cian Duffy (MYOB) - BeOS port / manual writing",
 
	"  Christian Rosentreter (tokai) - MorphOS / AmigaOS port",
 
	"  Richard Kempton (richK) - additional airports, initial TGP implementation",
 
	"",
 
	"  Michael Blunck - Pre-Signals and Semaphores © 2003",
 
	"  George - Canal/Lock graphics © 2003-2004",
 
	"  Marcin Grzegorczyk - Foundations for Tracks on Slopes",
 
	"  All Translators - Who made OpenTTD a truly international game",
 
	"  Bug Reporters - Without whom OpenTTD would still be full of bugs!",
 
	"",
 
	"",
 
	"And last but not least:",
 
	"  Chris Sawyer - For an amazing game!"
 
};
 

	
 
static void AboutWindowProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: /* Set up window counter and start position of scroller */
 
		WP(w, scroller_d).counter = 0;
 
		WP(w, scroller_d).height = w->height - 40;
 
		break;
 
	case WE_PAINT: {
 
		uint i;
 
		int y = WP(w, scroller_d).height;
 
		DrawWindowWidgets(w);
 

	
 
		// Show original copyright and revision version
 
		DrawStringCentered(210, 17, STR_00B6_ORIGINAL_COPYRIGHT, 0);
 
		DrawStringCentered(210, 17 + 10, STR_00B7_VERSION, 0);
 

	
 
		// Show all scrolling credits
 
		for (i = 0; i < lengthof(credits); i++) {
 
			if (y >= 50 && y < (w->height - 40)) {
 
				DoDrawString(credits[i], 10, y, 0x10);
 
			}
 
			y += 10;
 
		}
 

	
 
		// If the last text has scrolled start anew from the start
 
		if (y < 50) WP(w, scroller_d).height = w->height - 40;
 

	
 
		DoDrawStringCentered(210, w->height - 25, "Website: http://www.openttd.org", 16);
 
		DrawStringCentered(210, w->height - 15, STR_00BA_COPYRIGHT_OPENTTD, 0);
 
	}	break;
 
	case WE_MOUSELOOP: /* Timer to scroll the text and adjust the new top */
 
		if (WP(w, scroller_d).counter++ % 3 == 0) {
 
			WP(w, scroller_d).height--;
 
			SetWindowDirty(w);
 
		}
 
		break;
 
	}
 
}
 

	
 
static const Widget _about_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   419,     0,    13, STR_015B_OPENTTD, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   419,    14,   271, 0x0,              STR_NULL},
 
{      WWT_FRAME,   RESIZE_NONE,    14,     5,   414,    40,   245, STR_NULL,         STR_NULL},
 
{    WIDGETS_END},
 
};
 

	
 
static const WindowDesc _about_desc = {
 
	WDP_CENTER, WDP_CENTER, 420, 272,
 
	WC_GAME_OPTIONS,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_about_widgets,
 
	AboutWindowProc
 
};
 

	
 

	
 
void ShowAboutWindow(void)
 
{
 
	DeleteWindowById(WC_GAME_OPTIONS, 0);
 
	AllocateWindowDesc(&_about_desc);
 
}
 

	
 
static int _tree_to_plant;
 

	
 
static const uint32 _tree_sprites[] = {
 
	0x655, 0x663, 0x678, 0x62B, 0x647, 0x639, 0x64E, 0x632, 0x67F, 0x68D, 0x69B, 0x6A9,
 
	0x6AF, 0x6D2, 0x6D9, 0x6C4, 0x6CB, 0x6B6, 0x6BD, 0x6E0,
 
	0x72E, 0x734, 0x74A, 0x74F, 0x76B, 0x78F, 0x788, 0x77B, 0x75F, 0x774, 0x720, 0x797,
 
	0x79E, 0x7A5 | PALETTE_TO_GREEN, 0x7AC | PALETTE_TO_RED, 0x7B3, 0x7BA, 0x7C1 | PALETTE_TO_RED, 0x7C8 | PALETTE_TO_PALE_GREEN, 0x7CF | PALETTE_TO_YELLOW, 0x7D6 | PALETTE_TO_RED
 
};
 

	
 
static void BuildTreesWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		int x,y;
 
		int i, count;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		WP(w,tree_d).base = i = _tree_base_by_landscape[_opt.landscape];
 
		WP(w,tree_d).count = count = _tree_count_by_landscape[_opt.landscape];
 

	
 
		x = 18;
 
		y = 54;
 
		do {
 
			DrawSprite(_tree_sprites[i], x, y);
 
			x += 35;
 
			if (!(++i & 3)) {
 
				x -= 35 * 4;
 
				y += 47;
 
			}
 
		} while (--count);
 
	} break;
 

	
 
	case WE_CLICK: {
 
		int wid = e->we.click.widget;
 

	
 
		switch (wid) {
 
		case 0:
 
			ResetObjectToPlace();
 
			break;
 

	
 
		case 3: case 4: case 5: case 6:
 
		case 7: case 8: case 9: case 10:
 
		case 11:case 12: case 13: case 14:
 
			if (wid - 3 >= WP(w,tree_d).count) break;
 

	
 
			if (HandlePlacePushButton(w, wid, SPR_CURSOR_TREE, 1, NULL))
 
				_tree_to_plant = WP(w,tree_d).base + wid - 3;
 
			break;
 

	
 
		case 15: // tree of random type.
 
			if (HandlePlacePushButton(w, 15, SPR_CURSOR_TREE, 1, NULL))
 
				_tree_to_plant = -1;
 
			break;
 

	
 
		case 16: /* place trees randomly over the landscape*/
 
			LowerWindowWidget(w, 16);
 
			w->flags4 |= 5 << WF_TIMEOUT_SHL;
 
			SndPlayFx(SND_15_BEEP);
 
			PlaceTreesRandomly();
 
			MarkWholeScreenDirty();
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_PLACE_OBJ:
 
		VpStartPlaceSizing(e->we.place.tile, VPM_X_AND_Y_LIMITED);
 
		VpSetPlaceSizingLimit(20);
 
		break;
 

	
 
	case WE_PLACE_DRAG:
 
		VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata);
 
		return;
 

	
 
	case WE_PLACE_MOUSEUP:
 
		if (e->we.place.pt.x != -1) {
 
			DoCommandP(e->we.place.tile, _tree_to_plant, e->we.place.starttile, NULL,
 
				CMD_PLANT_TREE | CMD_AUTO | CMD_MSG(STR_2805_CAN_T_PLANT_TREE_HERE));
 
		}
 
		break;
 

	
 
	case WE_TIMEOUT:
 
		RaiseWindowWidget(w, 16);
 
		break;
 

	
 
	case WE_ABORT_PLACE_OBJ:
 
		RaiseWindowButtons(w);
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_trees_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   142,     0,    13, STR_2802_TREES,        STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   142,    14,   170, 0x0,                   STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   140,   157,   168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP},
 
{    WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_trees_desc = {
 
	497, 22, 143, 171,
 
	WC_BUILD_TREES, WC_SCEN_LAND_GEN,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_trees_widgets,
 
	BuildTreesWndProc
 
};
 

	
 
static const Widget _build_trees_scen_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   142,     0,    13, STR_2802_TREES,        STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   142,    14,   183, 0x0,                   STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   140,   157,   168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   140,   170,   181, STR_028A_RANDOM_TREES, STR_028B_PLANT_TREES_RANDOMLY_OVER},
 
{    WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_trees_scen_desc = {
 
	WDP_AUTO, WDP_AUTO, 143, 184,
 
	WC_BUILD_TREES,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_trees_scen_widgets,
 
	BuildTreesWndProc
 
};
 

	
 

	
 
void ShowBuildTreesToolbar(void)
 
{
 
	if (!IsValidPlayer(_current_player)) return;
 
	AllocateWindowDescFront(&_build_trees_desc, 0);
 
}
 

	
 
void ShowBuildTreesScenToolbar(void)
 
{
 
	AllocateWindowDescFront(&_build_trees_scen_desc, 0);
 
}
 

	
 
static uint32 _errmsg_decode_params[20];
 
static StringID _errmsg_message_1, _errmsg_message_2;
 
static uint _errmsg_duration;
 

	
 

	
 
static const Widget _errmsg_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     4,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     4,    11,   239,     0,    13, STR_00B2_MESSAGE, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     4,     0,   239,    14,    45, 0x0,              STR_NULL},
 
{    WIDGETS_END},
 
};
 

	
 
static const Widget _errmsg_face_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     4,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     4,    11,   333,     0,    13, STR_00B3_MESSAGE_FROM, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,     4,     0,   333,    14,   136, 0x0,                   STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static void ErrmsgWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		COPY_IN_DPARAM(0, _errmsg_decode_params, lengthof(_errmsg_decode_params));
 
		DrawWindowWidgets(w);
 
		COPY_IN_DPARAM(0, _errmsg_decode_params, lengthof(_errmsg_decode_params));
 
		if (!IsWindowOfPrototype(w, _errmsg_face_widgets)) {
 
			DrawStringMultiCenter(
 
				120,
 
				(_errmsg_message_1 == INVALID_STRING_ID ? 25 : 15),
 
				_errmsg_message_2,
 
				238);
 
			if (_errmsg_message_1 != INVALID_STRING_ID)
 
				DrawStringMultiCenter(
 
					120,
 
					30,
 
					_errmsg_message_1,
 
					238);
 
		} else {
 
			const Player *p = GetPlayer(GetDParamX(_errmsg_decode_params,2));
 
			DrawPlayerFace(p->face, p->player_color, 2, 16);
 

	
 
			DrawStringMultiCenter(
 
				214,
 
				(_errmsg_message_1 == INVALID_STRING_ID ? 65 : 45),
 
				_errmsg_message_2,
 
				238);
 
			if (_errmsg_message_1 != INVALID_STRING_ID)
 
				DrawStringMultiCenter(
 
					214,
 
					90,
 
					_errmsg_message_1,
 
					238);
 
		}
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		if (_right_button_down) DeleteWindow(w);
 
		break;
 

	
 
	case WE_4:
 
		if (--_errmsg_duration == 0) DeleteWindow(w);
 
		break;
 

	
 
	case WE_DESTROY:
 
		SetRedErrorSquare(0);
 
		_switch_mode_errorstr = INVALID_STRING_ID;
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		if (e->we.keypress.keycode == WKC_SPACE) {
 
			// Don't continue.
 
			e->we.keypress.cont = false;
 
			DeleteWindow(w);
 
		}
 
		break;
 
	}
 
}
 

	
 
void ShowErrorMessage(StringID msg_1, StringID msg_2, int x, int y)
 
{
 
	Window *w;
 
	const ViewPort *vp;
 
	Point pt;
 

	
 
	DeleteWindowById(WC_ERRMSG, 0);
 

	
 
	//assert(msg_2);
 
	if (msg_2 == 0) msg_2 = STR_EMPTY;
 

	
 
	_errmsg_message_1 = msg_1;
 
	_errmsg_message_2 = msg_2;
 
	COPY_OUT_DPARAM(_errmsg_decode_params, 0, lengthof(_errmsg_decode_params));
 
	_errmsg_duration = _patches.errmsg_duration;
 
	if (!_errmsg_duration) return;
 

	
 
	if (_errmsg_message_1 != STR_013B_OWNED_BY || GetDParamX(_errmsg_decode_params,2) >= 8) {
 

	
 
		if ( (x|y) != 0) {
 
			pt = RemapCoords2(x, y);
 
			vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
 

	
 
			// move x pos to opposite corner
 
			pt.x = ((pt.x - vp->virtual_left) >> vp->zoom) + vp->left;
 
			pt.x = (pt.x < (_screen.width >> 1)) ? _screen.width - 260 : 20;
 

	
 
			// move y pos to opposite corner
 
			pt.y = ((pt.y - vp->virtual_top) >> vp->zoom) + vp->top;
 
			pt.y = (pt.y < (_screen.height >> 1)) ? _screen.height - 80 : 100;
 

	
 
		} else {
 
			pt.x = (_screen.width - 240) >> 1;
 
			pt.y = (_screen.height - 46) >> 1;
 
		}
 
		w = AllocateWindow(pt.x, pt.y, 240, 46, ErrmsgWndProc, WC_ERRMSG, _errmsg_widgets);
 
	} else {
 
		if ( (x|y) != 0) {
 
			pt = RemapCoords2(x, y);
 
			vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
 
			pt.x = clamp(((pt.x - vp->virtual_left) >> vp->zoom) + vp->left - (334/2), 0, _screen.width - 334);
 
			pt.y = clamp(((pt.y - vp->virtual_top) >> vp->zoom) + vp->top - (137/2), 22, _screen.height - 137);
 
		} else {
 
			pt.x = (_screen.width - 334) >> 1;
 
			pt.y = (_screen.height - 137) >> 1;
 
		}
 
		w = AllocateWindow(pt.x, pt.y, 334, 137, ErrmsgWndProc, WC_ERRMSG, _errmsg_face_widgets);
 
	}
 

	
 
	w->desc_flags = WDF_STD_BTN | WDF_DEF_WIDGET;
 
}
 

	
 

	
 
void ShowEstimatedCostOrIncome(int32 cost, int x, int y)
 
{
 
	StringID msg = STR_0805_ESTIMATED_COST;
 

	
 
	if (cost < 0) {
 
		cost = -cost;
 
		msg = STR_0807_ESTIMATED_INCOME;
 
	}
 
	SetDParam(0, cost);
 
	ShowErrorMessage(INVALID_STRING_ID, msg, x, y);
 
}
 

	
 
void ShowCostOrIncomeAnimation(int x, int y, int z, int32 cost)
 
{
 
	StringID msg;
 
	Point pt = RemapCoords(x,y,z);
 

	
 
	msg = STR_0801_COST;
 
	if (cost < 0) {
 
		cost = -cost;
 
		msg = STR_0803_INCOME;
 
	}
 
	SetDParam(0, cost);
 
	AddTextEffect(msg, pt.x, pt.y, 0x250);
 
}
 

	
 
void ShowFeederIncomeAnimation(int x, int y, int z, int32 cost)
 
{
 
	Point pt = RemapCoords(x,y,z);
 

	
 
	SetDParam(0, cost);
 
	AddTextEffect(STR_FEEDER, pt.x, pt.y, 0x250);
 
}
 

	
 
static const Widget _tooltips_widgets[] = {
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   199,     0,    31, 0x0, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 

	
 
static void TooltipsWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			uint arg;
 
			GfxFillRect(0, 0, w->width - 1, w->height - 1, 0);
 
			GfxFillRect(1, 1, w->width - 2, w->height - 2, 0x44);
 

	
 
			for (arg = 0; arg < WP(w, tooltips_d).paramcount; arg++) {
 
				SetDParam(arg, WP(w, tooltips_d).params[arg]);
 
			}
 
			DrawStringMultiCenter((w->width >> 1), (w->height >> 1) - 5, WP(w, tooltips_d).string_id, 197);
 
			break;
 
		}
 

	
 
		case WE_MOUSELOOP:
 
			/* We can show tooltips while dragging tools. These are shown as long as
 
			 * we are dragging the tool. Normal tooltips work with rmb */
 
			if (WP(w, tooltips_d).paramcount == 0 ) {
 
				if (!_right_button_down) DeleteWindow(w);
 
			} else {
 
				if (!_left_button_down) DeleteWindow(w);
 
			}
 

	
 
			break;
 
	}
 
}
 

	
 
/** Shows a tooltip
 
* @param str String to be displayed
 
* @param params (optional) up to 5 pieces of additional information that may be
 
* added to a tooltip; currently only supports parameters of {NUM} (integer) */
 
void GuiShowTooltipsWithArgs(StringID str, uint paramcount, const uint32 params[])
 
{
 
	char buffer[512];
 
	BoundingRect br;
 
	Window *w;
 
	uint i;
 
	int x, y;
 

	
 
	DeleteWindowById(WC_TOOLTIPS, 0);
 

	
 
	/* We only show measurement tooltips with patch setting on */
 
	if (str == STR_NULL || (paramcount != 0 && !_patches.measure_tooltip)) return;
 

	
 
	for (i = 0; i != paramcount; i++) SetDParam(i, params[i]);
 
	GetString(buffer, str, lastof(buffer));
 

	
 
	br = GetStringBoundingBox(buffer);
 
	br.width += 6; br.height += 4; // increase slightly to have some space around the box
 

	
 
	/* Cut tooltip length to 200 pixels max, wrap to new line if longer */
 
	if (br.width > 200) {
 
		br.height += ((br.width - 4) / 176) * 10;
 
		br.width = 200;
 
	}
 

	
 
	/* Correctly position the tooltip position, watch out for window and cursor size
 
	 * Clamp value to below main toolbar and above statusbar. If tooltip would
 
	 * go below window, flip it so it is shown above the cursor */
 
	y = clamp(_cursor.pos.y + _cursor.size.y + _cursor.offs.y + 5, 22, _screen.height - 12);
 
	if (y + br.height > _screen.height - 12) y = _cursor.pos.y + _cursor.offs.y - br.height - 5;
 
	x = clamp(_cursor.pos.x - (br.width >> 1), 0, _screen.width - br.width);
 

	
 
	w = AllocateWindow(x, y, br.width, br.height, TooltipsWndProc, WC_TOOLTIPS, _tooltips_widgets);
 

	
 
	WP(w, tooltips_d).string_id = str;
 
	assert(sizeof(WP(w, tooltips_d).params[0]) == sizeof(params[0]));
 
	memcpy(WP(w, tooltips_d).params, params, sizeof(WP(w, tooltips_d).params[0]) * paramcount);
 
	WP(w, tooltips_d).paramcount = paramcount;
 

	
 
	w->flags4 &= ~WF_WHITE_BORDER_MASK; // remove white-border from tooltip
 
	w->widget[0].right = br.width;
 
	w->widget[0].bottom = br.height;
 
}
 

	
 

	
 
static void DrawStationCoverageText(const AcceptedCargo accepts,
 
	int str_x, int str_y, uint mask)
 
{
 
	char *b = _userstring;
 
	bool first = true;
 
	int i;
 

	
 
	b = InlineString(b, STR_000D_ACCEPTS);
 

	
 
	for (i = 0; i != NUM_CARGO; i++, mask >>= 1) {
 
		if (b >= lastof(_userstring) - 5) break;
 
		if (accepts[i] >= 8 && mask & 1) {
 
			if (first) {
 
				first = false;
 
			} else {
 
				/* Add a comma if this is not the first item */
 
				*b++ = ',';
 
				*b++ = ' ';
 
			}
 
			b = InlineString(b, _cargoc.names_s[i]);
 
		}
 
	}
 

	
 
	/* If first is still true then no cargo is accepted */
 
	if (first) b = InlineString(b, STR_00D0_NOTHING);
 

	
 
	*b = '\0';
 
	DrawStringMultiLine(str_x, str_y, STR_SPEC_USERSTRING, 144);
 
}
 

	
 
void DrawStationCoverageAreaText(int sx, int sy, uint mask, int rad) {
 
	TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
 
	AcceptedCargo accepts;
 
	if (tile < MapSize()) {
 
		GetAcceptanceAroundTiles(accepts, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad);
 
		DrawStationCoverageText(accepts, sx, sy, mask);
 
	}
 
}
 

	
 
void CheckRedrawStationCoverage(const Window *w)
 
{
 
	if (_thd.dirty & 1) {
 
		_thd.dirty &= ~1;
 
		SetWindowDirty(w);
 
	}
 
}
 

	
 
void SetVScrollCount(Window *w, int num)
 
{
 
	w->vscroll.count = num;
 
	num -= w->vscroll.cap;
 
	if (num < 0) num = 0;
 
	if (num < w->vscroll.pos) w->vscroll.pos = num;
 
}
 

	
 
void SetVScroll2Count(Window *w, int num)
 
{
 
	w->vscroll2.count = num;
 
	num -= w->vscroll2.cap;
 
	if (num < 0) num = 0;
 
	if (num < w->vscroll2.pos) w->vscroll2.pos = num;
 
}
 

	
 
void SetHScrollCount(Window *w, int num)
 
{
 
	w->hscroll.count = num;
 
	num -= w->hscroll.cap;
 
	if (num < 0) num = 0;
 
	if (num < w->hscroll.pos) w->hscroll.pos = num;
 
}
 

	
 
/* Delete a character at the caret position in a text buf.
 
 * If backspace is set, delete the character before the caret,
 
 * else delete the character after it. */
 
static void DelChar(Textbuf *tb, bool backspace)
 
{
 
	WChar c;
 
	uint width;
 
	size_t len;
 

	
 
	if (backspace) {
 
		do {
 
			tb->caretpos--;
 
		} while (IsUtf8Part(*(tb->buf + tb->caretpos)));
 
	}
 

	
 
	len = Utf8Decode(&c, tb->buf + tb->caretpos);
 
	width = GetCharacterWidth(FS_NORMAL, c);
 

	
 
	tb->width  -= width;
 
	if (backspace) tb->caretxoffs -= width;
 

	
 
	/* Move the remaining characters over the marker */
 
	memmove(tb->buf + tb->caretpos, tb->buf + tb->caretpos + len, tb->length - tb->caretpos - len + 1);
 
	tb->length -= len;
 
}
 

	
 
/**
 
 * Delete a character from a textbuffer, either with 'Delete' or 'Backspace'
 
 * The character is delete from the position the caret is at
 
 * @param tb @Textbuf type to be changed
 
 * @param delmode Type of deletion, either @WKC_BACKSPACE or @WKC_DELETE
 
 * @return Return true on successfull change of Textbuf, or false otherwise
 
 */
 
bool DeleteTextBufferChar(Textbuf *tb, int delmode)
 
{
 
	if (delmode == WKC_BACKSPACE && tb->caretpos != 0) {
 
		DelChar(tb, true);
 
		return true;
 
	} else if (delmode == WKC_DELETE && tb->caretpos < tb->length) {
 
		DelChar(tb, false);
 
		return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
/**
 
 * Delete every character in the textbuffer
 
 * @param tb @Textbuf buffer to be emptied
 
 */
 
void DeleteTextBufferAll(Textbuf *tb)
 
{
 
	memset(tb->buf, 0, tb->maxlength);
 
	tb->length = tb->width = 0;
 
	tb->caretpos = tb->caretxoffs = 0;
 
}
 

	
 
/**
 
 * Insert a character to a textbuffer. If maxwidth of the Textbuf is zero,
 
 * we don't care about the visual-length but only about the physical
 
 * length of the string
 
 * @param tb @Textbuf type to be changed
 
 * @param key Character to be inserted
 
 * @return Return true on successfull change of Textbuf, or false otherwise
 
 */
 
bool InsertTextBufferChar(Textbuf *tb, WChar key)
 
{
 
	const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
 
	size_t len = Utf8CharLen(key);
 
	if (tb->length < (tb->maxlength - len) && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) {
 
		memmove(tb->buf + tb->caretpos + len, tb->buf + tb->caretpos, tb->length - tb->caretpos + 1);
 
		Utf8Encode(tb->buf + tb->caretpos, key);
 
		tb->length += len;
 
		tb->width  += charwidth;
 

	
 
		tb->caretpos   += len;
 
		tb->caretxoffs += charwidth;
 
		return true;
 
	}
 
	return false;
 
}
 

	
 
/**
 
 * Handle text navigation with arrow keys left/right.
 
 * This defines where the caret will blink and the next characer interaction will occur
 
 * @param tb @Textbuf type where navigation occurs
 
 * @param navmode Direction in which navigation occurs @WKC_LEFT, @WKC_RIGHT, @WKC_END, @WKC_HOME
 
 * @return Return true on successfull change of Textbuf, or false otherwise
 
 */
 
bool MoveTextBufferPos(Textbuf *tb, int navmode)
 
{
 
	switch (navmode) {
 
	case WKC_LEFT:
 
		if (tb->caretpos != 0) {
 
			WChar c;
 

	
 
			do {
 
				tb->caretpos--;
 
			} while (IsUtf8Part(*(tb->buf + tb->caretpos)));
 

	
 
			Utf8Decode(&c, tb->buf + tb->caretpos);
 
			tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
 

	
 
			return true;
 
		}
 
		break;
 
	case WKC_RIGHT:
 
		if (tb->caretpos < tb->length) {
 
			WChar c;
 

	
 
			tb->caretpos   += Utf8Decode(&c, tb->buf + tb->caretpos);
 
			tb->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
 

	
 
			return true;
 
		}
 
		break;
 
	case WKC_HOME:
 
		tb->caretpos = 0;
 
		tb->caretxoffs = 0;
 
		return true;
 
	case WKC_END:
 
		tb->caretpos = tb->length;
 
		tb->caretxoffs = tb->width;
 
		return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
/**
 
 * Initialize the textbuffer by supplying it the buffer to write into
 
 * and the maximum length of this buffer
 
 * @param tb @Textbuf type which is getting initialized
 
 * @param buf the buffer that will be holding the data for input
 
 * @param maxlength maximum length in characters of this buffer
 
 * @param maxwidth maximum length in pixels of this buffer. If reached, buffer
 
 * cannot grow, even if maxlength would allow because there is space. A length
 
 * of zero '0' means the buffer is only restricted by maxlength */
 
void InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16 maxwidth)
 
{
 
	tb->buf = (char*)buf;
 
	tb->maxlength = maxlength;
 
	tb->maxwidth  = maxwidth;
 
	tb->caret = true;
 
	UpdateTextBufferSize(tb);
 
}
 

	
 
/**
 
 * Update @Textbuf type with its actual physical character and screenlength
 
 * Get the count of characters in the string as well as the width in pixels.
 
 * Useful when copying in a larger amount of text at once
 
 * @param tb @Textbuf type which length is calculated
 
 */
 
void UpdateTextBufferSize(Textbuf *tb)
 
{
 
	const char *buf = tb->buf;
 
	WChar c = Utf8Consume(&buf);
 

	
 
	tb->width = 0;
 
	tb->length = 0;
 

	
 
	for (; c != '\0' && tb->length < (tb->maxlength - 1); c = Utf8Consume(&buf)) {
 
		tb->width += GetCharacterWidth(FS_NORMAL, c);
 
		tb->length += Utf8CharLen(c);
 
	}
 

	
 
	tb->caretpos = tb->length;
 
	tb->caretxoffs = tb->width;
 
}
 

	
 
int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *e)
 
{
 
	e->we.keypress.cont = false;
 

	
 
	switch (e->we.keypress.keycode) {
 
	case WKC_ESC: return 2;
 
	case WKC_RETURN: case WKC_NUM_ENTER: return 1;
 
	case (WKC_CTRL | 'V'):
 
		if (InsertTextBufferClipboard(&string->text))
 
			InvalidateWidget(w, wid);
 
		break;
 
	case (WKC_CTRL | 'U'):
 
		DeleteTextBufferAll(&string->text);
 
		InvalidateWidget(w, wid);
 
		break;
 
	case WKC_BACKSPACE: case WKC_DELETE:
 
		if (DeleteTextBufferChar(&string->text, e->we.keypress.keycode))
 
			InvalidateWidget(w, wid);
 
		break;
 
	case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
 
		if (MoveTextBufferPos(&string->text, e->we.keypress.keycode))
 
			InvalidateWidget(w, wid);
 
		break;
 
	default:
 
		if (IsValidChar(e->we.keypress.key, string->afilter)) {
 
			if (InsertTextBufferChar(&string->text, e->we.keypress.key)) {
 
				InvalidateWidget(w, wid);
 
			}
 
		} else { // key wasn't caught. Continue only if standard entry specified
 
			e->we.keypress.cont = (string->afilter == CS_ALPHANUMERAL);
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
bool HandleCaret(Textbuf *tb)
 
{
 
	/* caret changed? */
 
	bool b = !!(_caret_timer & 0x20);
 

	
 
	if (b != tb->caret) {
 
		tb->caret = b;
 
		return true;
 
	}
 
	return false;
 
}
 

	
 
void HandleEditBox(Window *w, querystr_d *string, int wid)
 
{
 
	if (HandleCaret(&string->text)) InvalidateWidget(w, wid);
 
}
 

	
 
void DrawEditBox(Window *w, querystr_d *string, int wid)
 
{
 
	DrawPixelInfo dpi, *old_dpi;
 
	int delta;
 
	const Widget *wi = &w->widget[wid];
 
	const Textbuf *tb = &string->text;
 

	
 
	/* Limit the drawing of the string inside the widget boundaries */
 
	if (!FillDrawPixelInfo(&dpi,
 
	      wi->left + 4,
 
	      wi->top + 1,
 
	      wi->right - wi->left - 4,
 
	      wi->bottom - wi->top - 1)
 
	) return;
 

	
 
	GfxFillRect(wi->left + 1, wi->top + 1, wi->right - 1, wi->bottom - 1, 215);
 

	
 
	old_dpi = _cur_dpi;
 
	_cur_dpi = &dpi;
 

	
 
	/* We will take the current widget length as maximum width, with a small
 
	 * space reserved at the end for the caret to show */
 
	delta = (wi->right - wi->left) - tb->width - 10;
 
	if (delta > 0) delta = 0;
 

	
 
	if (tb->caretxoffs + delta < 0) delta = -tb->caretxoffs;
 

	
 
	DoDrawString(tb->buf, delta, 0, 8);
 
	if (tb->caret) DoDrawString("_", tb->caretxoffs + delta, 0, 12);
 

	
 
	_cur_dpi = old_dpi;
 
}
 

	
 
enum QueryStringWidgets {
 
	QUERY_STR_WIDGET_TEXT = 3,
 
	QUERY_STR_WIDGET_CANCEL,
 
	QUERY_STR_WIDGET_OK
 
};
 

	
 

	
 
static void QueryStringWndProc(Window *w, WindowEvent *e)
 
{
 
	querystr_d *qs = &WP(w, querystr_d);
 

	
 
	switch (e->event) {
 
		case WE_CREATE:
 
			SETBIT(_no_scroll, SCROLL_EDIT);
 
			break;
 

	
 
		case WE_PAINT:
 
			SetDParam(0, qs->caption);
 
			DrawWindowWidgets(w);
 

	
 
			DrawEditBox(w, qs, QUERY_STR_WIDGET_TEXT);
 
			break;
 

	
 
		case WE_CLICK:
 
			switch (e->we.click.widget) {
 
				case QUERY_STR_WIDGET_OK:
 
		press_ok:;
 
					if (qs->orig == NULL || strcmp(qs->text.buf, qs->orig) != 0) {
 
						Window *parent = w->parent;
 
						qs->handled = true;
 

	
 
						/* If the parent is NULL, the editbox is handled by general function
 
						 * HandleOnEditText */
 
						if (parent != NULL) {
 
							WindowEvent e;
 
							e.event = WE_ON_EDIT_TEXT;
 
							e.we.edittext.str = qs->text.buf;
 
							parent->wndproc(parent, &e);
 
						} else {
 
							HandleOnEditText(qs->text.buf);
 
						}
 
					}
 
					/* Fallthrough */
 
				case QUERY_STR_WIDGET_CANCEL:
 
					DeleteWindow(w);
 
					break;
 
			}
 
			break;
 

	
 
		case WE_MOUSELOOP:
 
			HandleEditBox(w, qs, QUERY_STR_WIDGET_TEXT);
 
			break;
 

	
 
		case WE_KEYPRESS:
 
			switch (HandleEditBoxKey(w, qs, QUERY_STR_WIDGET_TEXT, e)) {
 
				case 1: goto press_ok; /* Enter pressed, confirms change */
 
				case 2: DeleteWindow(w); break; /* ESC pressed, closes window, abandons changes */
 
			}
 
			break;
 

	
 
		case WE_DESTROY: /* Call cancellation of query, if we have not handled it before */
 
			if (!qs->handled && w->parent != NULL) {
 
				WindowEvent e;
 
				Window *parent = w->parent;
 

	
 
				qs->handled = true;
 
				e.event = WE_ON_EDIT_TEXT_CANCEL;
 
				parent->wndproc(parent, &e);
 
			}
 
			CLRBIT(_no_scroll, SCROLL_EDIT);
 
			break;
 
		}
 
}
 

	
 
static const Widget _query_string_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,        STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   259,     0,    13, STR_012D,        STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   259,    14,    29, 0x0,             STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,   257,    16,    27, 0x0,             STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     0,   129,    30,    41, STR_012E_CANCEL, STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   130,   259,    30,    41, STR_012F_OK,     STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _query_string_desc = {
 
	190, 219, 260, 42,
 
	WC_QUERY_STRING,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_query_string_widgets,
 
	QueryStringWndProc
 
};
 

	
 
static char _edit_str_buf[64];
 

	
 
/** Show a query popup window with a textbox in it.
 
 * @param str StringID for the text shown in the textbox
 
 * @param caption StringID of text shown in caption of querywindow
 
 * @param maxlen maximum length in characters allowed. If bit 12 is set we
 
 * will not check the resulting string against to original string to return success
 
 * @param maxwidth maximum width in pixels allowed
 
 * @param parent pointer to a Window that will handle the events (ok/cancel) of this
 
 * window. If NULL, results are handled by global function HandleOnEditText
 
 * @param afilter filters out unwanted character input */
 
void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, Window *parent, CharSetFilter afilter)
 
{
 
	static char orig_str_buf[lengthof(_edit_str_buf)];
 
	Window *w;
 
	uint realmaxlen = maxlen & ~0x1000;
 

	
 
	assert(realmaxlen < lengthof(_edit_str_buf));
 

	
 
	DeleteWindowById(WC_QUERY_STRING, 0);
 
	DeleteWindowById(WC_SAVELOAD, 0);
 

	
 
	w = AllocateWindowDesc(&_query_string_desc);
 
	w->parent = parent;
 

	
 
	GetString(_edit_str_buf, str, lastof(_edit_str_buf));
 
	_edit_str_buf[realmaxlen - 1] = '\0';
 

	
 
	if (maxlen & 0x1000) {
 
		WP(w, querystr_d).orig = NULL;
 
	} else {
 
		strecpy(orig_str_buf, _edit_str_buf, lastof(orig_str_buf));
 
		WP(w, querystr_d).orig = orig_str_buf;
 
	}
 

	
 
	LowerWindowWidget(w, QUERY_STR_WIDGET_TEXT);
 
	WP(w, querystr_d).caption = caption;
 
	WP(w, querystr_d).afilter = afilter;
 
	InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, realmaxlen, maxwidth);
 
}
 

	
 

	
 
enum QueryWidgets {
 
	QUERY_WIDGET_CAPTION = 1,
 
	QUERY_WIDGET_NO = 3,
 
	QUERY_WIDGET_YES
 
};
 

	
 

	
 
typedef struct query_d {
 
	void (*proc)(Window*, bool); ///< callback function executed on closing of popup. Window* points to parent, bool is true if 'yes' clicked, false otherwise
 
	StringID message;            ///< message shown for query window
 
	uint32 params[20];           ///< local copy of _decode_parameters
 
	bool calledback;             ///< has callback been executed already (internal usage for WE_DESTROY event)
 
} query_d;
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(query_d));
 

	
 

	
 
static void QueryWndProc(Window *w, WindowEvent *e)
 
{
 
	query_d *q = &WP(w, query_d);
 

	
 
	switch (e->event) {
 
		case WE_PAINT:
 
			COPY_IN_DPARAM(0, q->params, lengthof(q->params));
 
			DrawWindowWidgets(w);
 
			COPY_IN_DPARAM(0, q->params, lengthof(q->params));
 

	
 
			DrawStringMultiCenter(w->width / 2, (w->height / 2) - 10, q->message, w->width);
 
			break;
 

	
 
		case WE_CLICK:
 
			switch (e->we.click.widget) {
 
				case QUERY_WIDGET_YES:
 
					q->calledback = true;
 
					if (q->proc != NULL) q->proc(w->parent, true);
 
					/* Fallthrough */
 
				case QUERY_WIDGET_NO:
 
					DeleteWindow(w);
 
					break;
 
				}
 
			break;
 

	
 
		case WE_KEYPRESS: /* ESC closes the window, Enter confirms the action */
 
			switch (e->we.keypress.keycode) {
 
				case WKC_RETURN:
 
				case WKC_NUM_ENTER:
 
					q->calledback = true;
 
					if (q->proc != NULL) q->proc(w->parent, true);
 
					/* Fallthrough */
 
				case WKC_ESC:
 
					e->we.keypress.cont = false;
 
					DeleteWindow(w);
 
					break;
 
			}
 
			break;
 

	
 
		case WE_DESTROY: /* Call callback function (if any) on window close if not yet called */
 
			if (!q->calledback && q->proc != NULL) {
 
				q->calledback = true;
 
				q->proc(w->parent, false);
 
			}
 
			break;
 
	}
 
}
 

	
 

	
 
static const Widget _query_widgets[] = {
 
{  WWT_CLOSEBOX, RESIZE_NONE,  4,   0,  10,   0,  13, STR_00C5,        STR_018B_CLOSE_WINDOW},
 
{   WWT_CAPTION, RESIZE_NONE,  4,  11, 209,   0,  13, STR_NULL,        STR_NULL},
 
{     WWT_PANEL, RESIZE_NONE,  4,   0, 209,  14,  81, 0x0, /*OVERRIDE*/STR_NULL},
 
{WWT_PUSHTXTBTN, RESIZE_NONE,  3,  20,  90,  62,  73, STR_00C9_NO,     STR_NULL},
 
{WWT_PUSHTXTBTN, RESIZE_NONE,  3, 120, 190,  62,  73, STR_00C8_YES,    STR_NULL},
 
{   WIDGETS_END },
 
};
 

	
 
static const WindowDesc _query_desc = {
 
	WDP_CENTER, WDP_CENTER, 210, 82,
 
	WC_CONFIRM_POPUP_QUERY, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_DEF_WIDGET | WDF_MODAL,
 
	_query_widgets,
 
	QueryWndProc
 
};
 

	
 
/** Show a modal confirmation window with standard 'yes' and 'no' buttons
 
 * The window is aligned to the centre of its parent.
 
 * NOTE: You cannot use BindCString as parameter for this window!
 
 * @param caption string shown as window caption
 
 * @param message string that will be shown for the window
 
 * @param parent pointer to parent window, if this pointer is NULL the parent becomes
 
 * the main window WC_MAIN_WINDOW
 
 * @param x,y coordinates to show the window at
 
 * @param yes_no_callback callback function called when window is closed through any button */
 
void ShowQuery(StringID caption, StringID message, Window *parent, void (*callback)(Window*, bool))
 
{
 
	Window *w = AllocateWindowDesc(&_query_desc);
 
	if (w == NULL) return;
 

	
 
	if (parent == NULL) parent = FindWindowById(WC_MAIN_WINDOW, 0);
 
	w->parent = parent;
 
	w->left = parent->left + (parent->width / 2) - (w->width / 2);
 
	w->top = parent->top + (parent->height / 2) - (w->height / 2);
 

	
 
	/* Create a backup of the variadic arguments to strings because it will be
 
	 * overridden pretty often. We will copy these back for drawing */
 
	COPY_OUT_DPARAM(WP(w, query_d).params, 0, lengthof(WP(w, query_d).params));
 
	w->widget[QUERY_WIDGET_CAPTION].data = caption;
 
	WP(w, query_d).message    = message;
 
	WP(w, query_d).proc       = callback;
 
	WP(w, query_d).calledback = false;
 
}
 

	
 

	
 
static const Widget _load_dialog_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   256,     0,    13, STR_NULL,         STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,   127,    14,    25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   128,   256,    14,    25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP},
 
{      WWT_PANEL,  RESIZE_RIGHT,    14,     0,   256,    26,    47, 0x0,              STR_NULL},
 
{      WWT_PANEL,     RESIZE_RB,    14,     0,   256,    48,   293, 0x0,              STR_NULL},
 
{ WWT_PUSHIMGBTN,     RESIZE_LR,    14,   245,   256,    48,    59, SPR_HOUSE_ICON,   STR_SAVELOAD_HOME_BUTTON},
 
{      WWT_INSET,     RESIZE_RB,    14,     2,   243,    50,   291, 0x0,              STR_400A_LIST_OF_DRIVES_DIRECTORIES},
 
{  WWT_SCROLLBAR,    RESIZE_LRB,    14,   245,   256,    60,   281, 0x0,              STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   245,   256,   282,   293, 0x0,              STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _save_dialog_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   256,     0,    13, STR_NULL,         STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,   127,    14,    25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   128,   256,    14,    25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP},
 
{      WWT_PANEL,  RESIZE_RIGHT,    14,     0,   256,    26,    47, 0x0,              STR_NULL},
 
{      WWT_PANEL,     RESIZE_RB,    14,     0,   256,    48,   291, 0x0,              STR_NULL},
 
{ WWT_PUSHIMGBTN,     RESIZE_LR,    14,   245,   256,    48,    59, SPR_HOUSE_ICON,   STR_SAVELOAD_HOME_BUTTON},
 
{      WWT_INSET,     RESIZE_RB,    14,     2,   243,    50,   290, 0x0,              STR_400A_LIST_OF_DRIVES_DIRECTORIES},
 
{  WWT_SCROLLBAR,    RESIZE_LRB,    14,   245,   256,    60,   291, 0x0,              STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,    RESIZE_RTB,    14,     0,   256,   292,   307, 0x0,              STR_NULL},
 
{      WWT_PANEL,    RESIZE_RTB,    14,     2,   254,   294,   305, 0x0,              STR_400B_CURRENTLY_SELECTED_NAME},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   127,   308,   319, STR_4003_DELETE,  STR_400C_DELETE_THE_CURRENTLY_SELECTED},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   128,   244,   308,   319, STR_4002_SAVE,    STR_400D_SAVE_THE_CURRENT_GAME_USING},
 
{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   245,   256,   308,   319, 0x0,              STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
// Colors for fios types
 
const byte _fios_colors[] = {13, 9, 9, 6, 5, 6, 5, 6, 6, 8};
 

	
 
void BuildFileList(void)
 
{
 
	_fios_path_changed = true;
 
	FiosFreeSavegameList();
 

	
 
	switch (_saveload_mode) {
 
		case SLD_NEW_GAME:
 
		case SLD_LOAD_SCENARIO:
 
		case SLD_SAVE_SCENARIO:
 
			_fios_list = FiosGetScenarioList(_saveload_mode); break;
 
		case SLD_LOAD_HEIGHTMAP:
 
			_fios_list = FiosGetHeightmapList(_saveload_mode); break;
 

	
 
		default: _fios_list = FiosGetSavegameList(_saveload_mode); break;
 
	}
 
}
 

	
 
static void DrawFiosTexts(uint maxw)
 
{
 
	static const char *path = NULL;
 
	static StringID str = STR_4006_UNABLE_TO_READ_DRIVE;
 
	static uint32 tot = 0;
 

	
 
	if (_fios_path_changed) {
 
		str = FiosGetDescText(&path, &tot);
 
		_fios_path_changed = false;
 
	}
 

	
 
	if (str != STR_4006_UNABLE_TO_READ_DRIVE) SetDParam(0, tot);
 
	DrawString(2, 37, str, 0);
 
	DoDrawStringTruncated(path, 2, 27, 16, maxw);
 
}
 

	
 
static void MakeSortedSaveGameList(void)
 
{
 
	uint sort_start = 0;
 
	uint sort_end = 0;
 
	uint s_amount;
 
	int i;
 

	
 
	/* Directories are always above the files (FIOS_TYPE_DIR)
 
	 * Drives (A:\ (windows only) are always under the files (FIOS_TYPE_DRIVE)
 
	 * Only sort savegames/scenarios, not directories
 
	 */
 
	for (i = 0; i < _fios_num; i++) {
 
		switch (_fios_list[i].type) {
 
			case FIOS_TYPE_DIR:    sort_start++; break;
 
			case FIOS_TYPE_PARENT: sort_start++; break;
 
			case FIOS_TYPE_DRIVE:  sort_end++;   break;
 
		}
 
	}
 

	
 
	s_amount = _fios_num - sort_start - sort_end;
 
	if (s_amount > 0)
 
		qsort(_fios_list + sort_start, s_amount, sizeof(FiosItem), compare_FiosItems);
 
}
 

	
 
static void GenerateFileName(void)
 
{
 
	/* Check if we are not a specatator who wants to generate a name..
 
	    Let's use the name of player #0 for now. */
 
	const Player *p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : 0);
 

	
 
	SetDParam(0, p->name_1);
 
	SetDParam(1, p->name_2);
 
	SetDParam(2, _date);
 
	GetString(_edit_str_buf, STR_4004, lastof(_edit_str_buf));
 
}
 

	
 
extern void StartupEngines(void);
 

	
 
static void SaveLoadDlgWndProc(Window *w, WindowEvent *e)
 
{
 
	static FiosItem o_dir;
 

	
 
	switch (e->event) {
 
	case WE_CREATE: { /* Set up OPENTTD button */
 
		o_dir.type = FIOS_TYPE_DIRECT;
 
		switch (_saveload_mode) {
 
			case SLD_SAVE_GAME:
 
			case SLD_LOAD_GAME:
 
				ttd_strlcpy(&o_dir.name[0], _paths.save_dir, sizeof(o_dir.name));
 
				break;
 

	
 
			case SLD_SAVE_SCENARIO:
 
			case SLD_LOAD_SCENARIO:
 
				ttd_strlcpy(&o_dir.name[0], _paths.scenario_dir, sizeof(o_dir.name));
 
				break;
 

	
 
			case SLD_LOAD_HEIGHTMAP:
 
				ttd_strlcpy(&o_dir.name[0], _paths.heightmap_dir, sizeof(o_dir.name));
 
				break;
 

	
 
			default:
 
				ttd_strlcpy(&o_dir.name[0], _paths.personal_dir, sizeof(o_dir.name));
 
		}
 
		break;
 
		}
 

	
 
	case WE_PAINT: {
 
		int pos;
 
		int y;
 

	
 
		SetVScrollCount(w, _fios_num);
 
		DrawWindowWidgets(w);
 
		DrawFiosTexts(w->width);
 

	
 
		if (_savegame_sort_dirty) {
 
			_savegame_sort_dirty = false;
 
			MakeSortedSaveGameList();
 
		}
 

	
 
		GfxFillRect(w->widget[7].left + 1, w->widget[7].top + 1, w->widget[7].right, w->widget[7].bottom, 0xD7);
 
		DoDrawString(
 
			_savegame_sort_order & SORT_DESCENDING ? DOWNARROW : UPARROW,
 
			_savegame_sort_order & SORT_BY_NAME ? w->widget[2].right - 9 : w->widget[3].right - 9,
 
			15, 16
 
		);
 

	
 
		y = w->widget[7].top + 1;
 
		for (pos = w->vscroll.pos; pos < _fios_num; pos++) {
 
			const FiosItem *item = _fios_list + pos;
 

	
 
			DoDrawStringTruncated(item->title, 4, y, _fios_colors[item->type], w->width - 18);
 
			y += 10;
 
			if (y >= w->vscroll.cap * 10 + w->widget[7].top + 1) break;
 
		}
 

	
 
		if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
 
			DrawEditBox(w, &WP(w,querystr_d), 10);
 
		}
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 2: /* Sort save names by name */
 
			_savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ?
 
				SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME;
 
			_savegame_sort_dirty = true;
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 3: /* Sort save names by date */
 
			_savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ?
 
				SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE;
 
			_savegame_sort_dirty = true;
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 6: /* OpenTTD 'button', jumps to OpenTTD directory */
 
			FiosBrowseTo(&o_dir);
 
			SetWindowDirty(w);
 
			BuildFileList();
 
			break;
 

	
 
		case 7: { /* Click the listbox */
 
			int y = (e->we.click.pt.y - w->widget[e->we.click.widget].top - 1) / 10;
 
			char *name;
 
			const FiosItem *file;
 

	
 
			if (y < 0 || (y += w->vscroll.pos) >= w->vscroll.count) return;
 

	
 
			file = _fios_list + y;
 

	
 
			name = FiosBrowseTo(file);
 
			if (name != NULL) {
 
				if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) {
 
					_switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD;
 

	
 
					SetFiosType(file->type);
 
					ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
 
					ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
 

	
 
					DeleteWindow(w);
 
				} else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) {
 
					SetFiosType(file->type);
 
					ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
 
					ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
 

	
 
					DeleteWindow(w);
 
					ShowHeightmapLoad();
 
				} else {
 
					// SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox
 
					ttd_strlcpy(WP(w, querystr_d).text.buf, file->title, WP(w, querystr_d).text.maxlength);
 
					UpdateTextBufferSize(&WP(w, querystr_d).text);
 
					InvalidateWidget(w, 10);
 
				}
 
			} else {
 
				// Changed directory, need repaint.
 
				SetWindowDirty(w);
 
				BuildFileList();
 
			}
 
			break;
 
		}
 

	
 
		case 11: case 12: /* Delete, Save game */
 
			break;
 
		}
 
		break;
 
	case WE_MOUSELOOP:
 
		if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
 
			HandleEditBox(w, &WP(w, querystr_d), 10);
 
		}
 
		break;
 
	case WE_KEYPRESS:
 
		if (e->we.keypress.keycode == WKC_ESC) {
 
			DeleteWindow(w);
 
			return;
 
		}
 

	
 
		if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
 
			if (HandleEditBoxKey(w, &WP(w, querystr_d), 10, e) == 1) /* Press Enter */
 
					HandleButtonClick(w, 12);
 
		}
 
		break;
 
	case WE_TIMEOUT:
 
		/* This test protects against using widgets 11 and 12 which are only available
 
		 * in those two saveload mode  */
 
		if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) break;
 

	
 
		if (IsWindowWidgetLowered(w, 11)) { /* Delete button clicked */
 
			if (!FiosDelete(WP(w,querystr_d).text.buf)) {
 
				ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0);
 
			} else {
 
				BuildFileList();
 
				/* Reset file name to current date on successfull delete */
 
				if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName();
 
			}
 

	
 
			UpdateTextBufferSize(&WP(w, querystr_d).text);
 
			SetWindowDirty(w);
 
		} else if (IsWindowWidgetLowered(w, 12)) { /* Save button clicked */
 
			_switch_mode = SM_SAVE;
 
			FiosMakeSavegameName(_file_to_saveload.name, WP(w,querystr_d).text.buf, sizeof(_file_to_saveload.name));
 

	
 
			/* In the editor set up the vehicle engines correctly (date might have changed) */
 
			if (_game_mode == GM_EDITOR) StartupEngines();
 
		}
 
		break;
 
	case WE_DESTROY:
 
		// pause is only used in single-player, non-editor mode, non menu mode
 
		if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) {
 
			DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
 
		}
 
		FiosFreeSavegameList();
 
		CLRBIT(_no_scroll, SCROLL_SAVE);
 
		break;
 
	case WE_RESIZE: {
 
		/* Widget 2 and 3 have to go with halve speed, make it so obiwan */
 
		uint diff = e->we.sizing.diff.x / 2;
 
		w->widget[2].right += diff;
 
		w->widget[3].left  += diff;
 
		w->widget[3].right += e->we.sizing.diff.x;
 

	
 
		/* Same for widget 11 and 12 in save-dialog */
 
		if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
 
			w->widget[11].right += diff;
 
			w->widget[12].left  += diff;
 
			w->widget[12].right += e->we.sizing.diff.x;
 
		}
 

	
 
		w->vscroll.cap += e->we.sizing.diff.y / 10;
 
		} break;
 
	}
 
}
 

	
 
static const WindowDesc _load_dialog_desc = {
 
	WDP_CENTER, WDP_CENTER, 257, 294,
 
	WC_SAVELOAD,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_load_dialog_widgets,
 
	SaveLoadDlgWndProc,
 
};
 

	
 
static const WindowDesc _save_dialog_desc = {
 
	WDP_CENTER, WDP_CENTER, 257, 320,
 
	WC_SAVELOAD,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_save_dialog_widgets,
 
	SaveLoadDlgWndProc,
 
};
 

	
 
void ShowSaveLoadDialog(int mode)
 
{
 
	static const StringID saveload_captions[] = {
 
		STR_4001_LOAD_GAME,
 
		STR_0298_LOAD_SCENARIO,
 
		STR_4000_SAVE_GAME,
 
		STR_0299_SAVE_SCENARIO,
 
		STR_4011_LOAD_HEIGHTMAP,
 
	};
 

	
 
	Window *w;
 
	const WindowDesc *sld = &_save_dialog_desc;
 

	
 

	
 
	SetObjectToPlace(SPR_CURSOR_ZZZ, 0, 0, 0);
 
	DeleteWindowById(WC_QUERY_STRING, 0);
 
	DeleteWindowById(WC_SAVELOAD, 0);
 

	
 
	_saveload_mode = mode;
 
	SETBIT(_no_scroll, SCROLL_SAVE);
 

	
 
	switch (mode) {
 
		case SLD_SAVE_GAME:     GenerateFileName(); break;
 
		case SLD_SAVE_SCENARIO: strcpy(_edit_str_buf, "UNNAMED"); break;
 
		default:                sld = &_load_dialog_desc; break;
 
	}
 

	
 
	assert((uint)mode < lengthof(saveload_captions));
 
	w = AllocateWindowDesc(sld);
 
	w->widget[1].data = saveload_captions[mode];
 
	w->vscroll.cap = 24;
 
	w->resize.step_width = 2;
 
	w->resize.step_height = 10;
 
	w->resize.height = w->height - 14 * 10; // Minimum of 10 items
 
	LowerWindowWidget(w, 7);
 

	
 
	WP(w, querystr_d).afilter = CS_ALPHANUMERAL;
 
	InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, lengthof(_edit_str_buf), 240);
 

	
 
	// pause is only used in single-player, non-editor mode, non-menu mode. It
 
	// will be unpaused in the WE_DESTROY event handler.
 
	if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) {
 
		DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
 
	}
 

	
 
	BuildFileList();
 

	
 
	ResetObjectToPlace();
 
}
 

	
 
void RedrawAutosave(void)
 
{
 
	SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0));
 
}
 

	
 
void SetFiosType(const byte fiostype)
 
{
 
	switch (fiostype) {
 
		case FIOS_TYPE_FILE:
 
		case FIOS_TYPE_SCENARIO:
 
			_file_to_saveload.mode = SL_LOAD;
 
			break;
 

	
 
		case FIOS_TYPE_OLDFILE:
 
		case FIOS_TYPE_OLD_SCENARIO:
 
			_file_to_saveload.mode = SL_OLD_LOAD;
 
			break;
 

	
 
#ifdef WITH_PNG
 
		case FIOS_TYPE_PNG:
 
			_file_to_saveload.mode = SL_PNG;
 
			break;
 
#endif /* WITH_PNG */
 

	
 
		case FIOS_TYPE_BMP:
 
			_file_to_saveload.mode = SL_BMP;
 
			break;
 

	
 
		default:
 
			_file_to_saveload.mode = SL_INVALID;
 
			break;
 
	}
 
}
 

	
 
static int32 ClickMoneyCheat(int32 p1, int32 p2)
 
{
 
		DoCommandP(0, -10000000, 0, NULL, CMD_MONEY_CHEAT);
 
		return true;
 
}
 

	
 
// p1 player to set to, p2 is -1 or +1 (down/up)
 
static int32 ClickChangePlayerCheat(int32 p1, int32 p2)
 
{
 
	while (IsValidPlayer((PlayerID)p1)) {
 
		if (_players[p1].is_active) {
 
			SetLocalPlayer((PlayerID)p1);
 

	
 
			MarkWholeScreenDirty();
 
			return _local_player;
 
		}
 
		p1 += p2;
 
	}
 

	
 
	return _local_player;
 
}
 

	
 
// p1 -1 or +1 (down/up)
 
static int32 ClickChangeClimateCheat(int32 p1, int32 p2)
 
{
 
	if (p1 == -1) p1 = 3;
 
	if (p1 ==  4) p1 = 0;
 
	_opt.landscape = p1;
 
	ReloadNewGRFData();
 
	return _opt.landscape;
 
}
 

	
 
extern void EnginesMonthlyLoop(void);
 

	
 
// p2 1 (increase) or -1 (decrease)
 
static int32 ClickChangeDateCheat(int32 p1, int32 p2)
 
{
 
	YearMonthDay ymd;
 
	ConvertDateToYMD(_date, &ymd);
 

	
 
	if ((ymd.year == MIN_YEAR && p2 == -1) || (ymd.year == MAX_YEAR && p2 == 1)) return _cur_year;
 

	
 
	SetDate(ConvertYMDToDate(_cur_year + p2, ymd.month, ymd.day));
 
	EnginesMonthlyLoop();
 
	SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0));
 
	return _cur_year;
 
}
 

	
 
typedef int32 CheckButtonClick(int32, int32);
 

	
 
enum ce_flags {CE_CLICK = 1 << 0};
 

	
 
typedef byte ce_flags;
 

	
 
typedef struct CheatEntry {
 
	VarType type;          // type of selector
 
	ce_flags flags;        // selector flags
 
	StringID str;          // string with descriptive text
 
	void *variable;        // pointer to the variable
 
	bool *been_used;       // has this cheat been used before?
 
	CheckButtonClick *proc;// procedure
 
	int16 min, max;        // range for spinbox setting
 
} CheatEntry;
 

	
 
static const CheatEntry _cheats_ui[] = {
 
	{SLE_BOOL,CE_CLICK, STR_CHEAT_MONEY,          &_cheats.money.value,           &_cheats.money.been_used,           &ClickMoneyCheat,         0,  0},
 
	{SLE_UINT8,      0, STR_CHEAT_CHANGE_PLAYER,  &_local_player,                 &_cheats.switch_player.been_used,   &ClickChangePlayerCheat,  0, 11},
 
	{SLE_BOOL,       0, STR_CHEAT_EXTRA_DYNAMITE, &_cheats.magic_bulldozer.value, &_cheats.magic_bulldozer.been_used, NULL,                     0,  0},
 
	{SLE_BOOL,       0, STR_CHEAT_CROSSINGTUNNELS,&_cheats.crossing_tunnels.value,&_cheats.crossing_tunnels.been_used,NULL,                     0,  0},
 
	{SLE_BOOL,       0, STR_CHEAT_BUILD_IN_PAUSE, &_cheats.build_in_pause.value,  &_cheats.build_in_pause.been_used,  NULL,                     0,  0},
 
	{SLE_BOOL,       0, STR_CHEAT_NO_JETCRASH,    &_cheats.no_jetcrash.value,     &_cheats.no_jetcrash.been_used,     NULL,                     0,  0},
 
	{SLE_BOOL,       0, STR_CHEAT_SETUP_PROD,     &_cheats.setup_prod.value,      &_cheats.setup_prod.been_used,      NULL,                     0,  0},
 
	{SLE_UINT8,      0, STR_CHEAT_SWITCH_CLIMATE, &_opt.landscape,                &_cheats.switch_climate.been_used,  &ClickChangeClimateCheat,-1,  4},
 
	{SLE_INT32,      0, STR_CHEAT_CHANGE_DATE,    &_cur_year,                     &_cheats.change_date.been_used,     &ClickChangeDateCheat,   -1,  1},
 
};
 

	
 

	
 
static const Widget _cheat_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,   STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   399,     0,    13, STR_CHEATS, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   399,    14,   169, 0x0,        STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   399,    14,   169, 0x0,        STR_CHEATS_TIP},
 
{   WIDGETS_END},
 
};
 

	
 
extern void DrawPlayerIcon(PlayerID pid, int x, int y);
 

	
 
static void CheatsWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		int clk = WP(w,def_d).data_1;
 
		int x, y;
 
		int i;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		DrawStringMultiCenter(200, 25, STR_CHEATS_WARNING, 350);
 

	
 
		x = 0;
 
		y = 45;
 

	
 
		for (i = 0; i != lengthof(_cheats_ui); i++) {
 
			const CheatEntry *ce = &_cheats_ui[i];
 

	
 
			DrawSprite((*ce->been_used) ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, x + 5, y + 2);
 

	
 
			switch (ce->type) {
 
			case SLE_BOOL: {
 
				bool on = (*(bool*)ce->variable);
 

	
 
				if (ce->flags & CE_CLICK) {
 
					DrawFrameRect(x + 20, y + 1, x + 30 + 9, y + 9, 0, (clk - (i * 2) == 1) ? FR_LOWERED : 0);
 
					if (i == 0) { // XXX - hack/hack for first element which is increase money. Told ya it's a mess
 
						SetDParam64(0, 10000000);
 
					} else {
 
						SetDParam(0, false);
 
					}
 
				} else {
 
					DrawFrameRect(x + 20, y + 1, x + 30 + 9, y + 9, on ? 6 : 4, on ? FR_LOWERED : 0);
 
					SetDParam(0, on ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF);
 
				}
 
			} break;
 
			default: {
 
				int32 val = (int32)ReadValue(ce->variable, ce->type);
 
				char buf[512];
 

	
 
				/* Draw [<][>] boxes for settings of an integer-type */
 
				DrawArrowButtons(x + 20, y, 3, clk - (i * 2), true, true);
 

	
 
				switch (ce->str) {
 
				/* Display date for change date cheat */
 
				case STR_CHEAT_CHANGE_DATE: SetDParam(0, _date); break;
 
				/* Draw colored flag for change player cheat */
 
				case STR_CHEAT_CHANGE_PLAYER:
 
					SetDParam(0, val);
 
					GetString(buf, STR_CHEAT_CHANGE_PLAYER, lastof(buf));
 
					DrawPlayerIcon(_current_player, 60 + GetStringBoundingBox(buf).width, y + 2);
 
					break;
 
				/* Set correct string for switch climate cheat */
 
				case STR_CHEAT_SWITCH_CLIMATE: val += STR_TEMPERATE_LANDSCAPE;
 
				/* Fallthrough */
 
				default: SetDParam(0, val);
 
				}
 
			} break;
 
			}
 

	
 
			DrawString(50, y + 1, ce->str, 0);
 

	
 
			y += 12;
 
		}
 
		break;
 
	}
 

	
 
	case WE_CLICK: {
 
			const CheatEntry *ce;
 
			uint btn = (e->we.click.pt.y - 46) / 12;
 
			int32 value, oldvalue;
 
			uint x = e->we.click.pt.x;
 

	
 
			// not clicking a button?
 
			if (!IS_INT_INSIDE(x, 20, 40) || btn >= lengthof(_cheats_ui)) break;
 

	
 
			ce = &_cheats_ui[btn];
 
			oldvalue = value = (int32)ReadValue(ce->variable, ce->type);
 

	
 
			*ce->been_used = true;
 

	
 
			switch (ce->type) {
 
			case SLE_BOOL:
 
				if (ce->flags & CE_CLICK) WP(w,def_d).data_1 = btn * 2 + 1;
 
				value ^= 1;
 
				if (ce->proc != NULL) ce->proc(value, 0);
 
				break;
 
			default: {
 
				/* Add a dynamic step-size to the scroller. In a maximum of
 
				 * 50-steps you should be able to get from min to max */
 
				uint16 step = ((ce->max - ce->min) / 20);
 
				if (step == 0) step = 1;
 

	
 
				/* Increase or decrease the value and clamp it to extremes */
 
				value += (x >= 30) ? step : -step;
 
				clamp(value, ce->min, ce->max);
 

	
 
				// take whatever the function returns
 
				value = ce->proc(value, (x >= 30) ? 1 : -1);
 

	
 
				if (value != oldvalue) {
 
					WP(w,def_d).data_1 = btn * 2 + 1 + ((x >= 30) ? 1 : 0);
 
				}
 
			} break;
 
			}
 

	
 
			if (value != oldvalue) {
 
				WriteValue(ce->variable, ce->type, (int64)value);
 
				SetWindowDirty(w);
 
			}
 

	
 
			w->flags4 |= 5 << WF_TIMEOUT_SHL;
 

	
 
			SetWindowDirty(w);
 
		}
 
		break;
 
	case WE_TIMEOUT:
 
		WP(w,def_d).data_1 = 0;
 
		SetWindowDirty(w);
 
		break;
 
	}
 
}
 

	
 
static const WindowDesc _cheats_desc = {
 
	240, 22, 400, 170,
 
	WC_CHEATS,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_cheat_widgets,
 
	CheatsWndProc
 
};
 

	
 

	
 
void ShowCheatWindow(void)
 
{
 
	DeleteWindowById(WC_CHEATS, 0);
 
	AllocateWindowDesc(&_cheats_desc);
 
}
src/mixer.c
Show inline comments
 
deleted file
src/mixer.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "mixer.h"
 

	
 
struct MixerChannel {
 
	bool active;
 

	
 
	// pointer to allocated buffer memory
 
	int8 *memory;
 

	
 
	// current position in memory
 
	uint32 pos;
 
	uint32 frac_pos;
 
	uint32 frac_speed;
 
	uint32 samples_left;
 

	
 
	// Mixing volume
 
	uint volume_left;
 
	uint volume_right;
 

	
 
	uint flags;
 
};
 

	
 
static MixerChannel _channels[8];
 
static uint32 _play_rate;
 

	
 

	
 
static void mix_int8_to_int16(MixerChannel *sc, int16 *buffer, uint samples)
 
{
 
	int8 *b;
 
	uint32 frac_pos;
 
	uint32 frac_speed;
 
	uint volume_left;
 
	uint volume_right;
 

	
 
	if (samples > sc->samples_left) samples = sc->samples_left;
 
	sc->samples_left -= samples;
 
	assert(samples > 0);
 

	
 
	b = sc->memory + sc->pos;
 
	frac_pos = sc->frac_pos;
 
	frac_speed = sc->frac_speed;
 
	volume_left = sc->volume_left;
 
	volume_right = sc->volume_right;
 

	
 
	if (frac_speed == 0x10000) {
 
		// Special case when frac_speed is 0x10000
 
		do {
 
			buffer[0] += *b * volume_left >> 8;
 
			buffer[1] += *b * volume_right >> 8;
 
			b++;
 
			buffer += 2;
 
		} while (--samples > 0);
 
	} else {
 
		do {
 
			buffer[0] += *b * volume_left >> 8;
 
			buffer[1] += *b * volume_right >> 8;
 
			buffer += 2;
 
			frac_pos += frac_speed;
 
			b += frac_pos >> 16;
 
			frac_pos &= 0xffff;
 
		} while (--samples > 0);
 
	}
 

	
 
	sc->frac_pos = frac_pos;
 
	sc->pos = b - sc->memory;
 
}
 

	
 
static void MxCloseChannel(MixerChannel *mc)
 
{
 
	if (mc->flags & MX_AUTOFREE) free(mc->memory);
 
	mc->active = false;
 
	mc->memory = NULL;
 
}
 

	
 
void MxMixSamples(void *buffer, uint samples)
 
{
 
	MixerChannel *mc;
 

	
 
	// Clear the buffer
 
	memset(buffer, 0, sizeof(int16) * 2 * samples);
 

	
 
	// Mix each channel
 
	for (mc = _channels; mc != endof(_channels); mc++) {
 
		if (mc->active) {
 
			mix_int8_to_int16(mc, buffer, samples);
 
			if (mc->samples_left == 0) MxCloseChannel(mc);
 
		}
 
	}
 
}
 

	
 
MixerChannel *MxAllocateChannel(void)
 
{
 
	MixerChannel *mc;
 
	for (mc = _channels; mc != endof(_channels); mc++)
 
		if (mc->memory == NULL) {
 
			mc->active = false;
 
			return mc;
 
		}
 
	return NULL;
 
}
 

	
 
void MxSetChannelRawSrc(MixerChannel *mc, int8 *mem, uint size, uint rate, uint flags)
 
{
 
	mc->memory = mem;
 
	mc->flags = flags;
 
	mc->frac_pos = 0;
 
	mc->pos = 0;
 

	
 
	mc->frac_speed = (rate << 16) / _play_rate;
 

	
 
	// adjust the magnitude to prevent overflow
 
	while (size & 0xFFFF0000) {
 
		size >>= 1;
 
		rate = (rate >> 1) + 1;
 
	}
 

	
 
	mc->samples_left = size * _play_rate / rate;
 
}
 

	
 
void MxSetChannelVolume(MixerChannel *mc, uint left, uint right)
 
{
 
	mc->volume_left = left;
 
	mc->volume_right = right;
 
}
 

	
 

	
 
void MxActivateChannel(MixerChannel* mc)
 
{
 
	mc->active = true;
 
}
 

	
 

	
 
bool MxInitialize(uint rate)
 
{
 
	_play_rate = rate;
 
	return true;
 
}
src/music.c
Show inline comments
 
deleted file
src/music.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "music.h"
 

	
 
const SongSpecs origin_songs_specs[NUM_SONGS_AVAILABLE] = {
 
	{"gm_tt00.gm", "Tycoon DELUXE Theme"},
 
	{"gm_tt02.gm", "Easy Driver"},
 
	{"gm_tt03.gm", "Little Red Diesel"},
 
	{"gm_tt17.gm", "Cruise Control"},
 
	{"gm_tt07.gm", "Don't Walk!"},
 
	{"gm_tt09.gm", "Fell Apart On Me"},
 
	{"gm_tt04.gm", "City Groove"},
 
	{"gm_tt19.gm", "Funk Central"},
 
	{"gm_tt06.gm", "Stoke It"},
 
	{"gm_tt12.gm", "Road Hog"},
 
	{"gm_tt05.gm", "Aliens Ate My Railway"},
 
	{"gm_tt01.gm", "Snarl Up"},
 
	{"gm_tt18.gm", "Stroll On"},
 
	{"gm_tt10.gm", "Can't Get There From Here"},
 
	{"gm_tt08.gm", "Sawyer's Tune"},
 
	{"gm_tt13.gm", "Hold That Train!"},
 
	{"gm_tt21.gm", "Movin' On"},
 
	{"gm_tt15.gm", "Goss Groove"},
 
	{"gm_tt16.gm", "Small Town"},
 
	{"gm_tt14.gm", "Broomer's Oil Rag"},
 
	{"gm_tt20.gm", "Jammit"},
 
	{"gm_tt11.gm", "Hard Drivin'"},
 
};
src/music/extmidi.c
Show inline comments
 
deleted file
src/music/extmidi.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef __MORPHOS__
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "../sound.h"
 
#include "../string.h"
 
#include "../variables.h"
 
#include "../debug.h"
 
#include "extmidi.h"
 
#include <fcntl.h>
 
#include <sys/types.h>
 
#include <sys/wait.h>
 
#include <unistd.h>
 
#include <signal.h>
 
#include <sys/stat.h>
 
#include <errno.h>
 

	
 
static struct {
 
	char song[MAX_PATH];
 
	pid_t pid;
 
} _midi;
 

	
 
static void DoPlay(void);
 
static void DoStop(void);
 

	
 
static const char* ExtMidiStart(const char* const * parm)
 
{
 
	_midi.song[0] = '\0';
 
	_midi.pid = -1;
 
	return NULL;
 
}
 

	
 
static void ExtMidiStop(void)
 
{
 
	_midi.song[0] = '\0';
 
	DoStop();
 
}
 

	
 
static void ExtMidiPlaySong(const char* filename)
 
{
 
	ttd_strlcpy(_midi.song, filename, lengthof(_midi.song));
 
	DoStop();
 
}
 

	
 
static void ExtMidiStopSong(void)
 
{
 
	_midi.song[0] = '\0';
 
	DoStop();
 
}
 

	
 
static bool ExtMidiIsPlaying(void)
 
{
 
	if (_midi.pid != -1 && waitpid(_midi.pid, NULL, WNOHANG) == _midi.pid)
 
		_midi.pid = -1;
 
	if (_midi.pid == -1 && _midi.song[0] != '\0') DoPlay();
 
	return _midi.pid != -1;
 
}
 

	
 
static void ExtMidiSetVolume(byte vol)
 
{
 
	DEBUG(driver, 1, "extmidi: set volume not implemented");
 
}
 

	
 
static void DoPlay(void)
 
{
 
	_midi.pid = fork();
 
	switch (_midi.pid) {
 
		case 0: {
 
			int d;
 

	
 
			close(0);
 
			d = open("/dev/null", O_RDONLY);
 
			if (d != -1 && dup2(d, 1) != -1 && dup2(d, 2) != -1) {
 
				#if defined(MIDI_ARG)
 
					execlp(msf.extmidi, "extmidi", MIDI_ARG, _midi.song, (char*)0);
 
				#else
 
					execlp(msf.extmidi, "extmidi", _midi.song, (char*)0);
 
				#endif
 
			}
 
			_exit(1);
 
		}
 

	
 
		case -1:
 
			DEBUG(driver, 0, "extmidi: couldn't fork: %s", strerror(errno));
 
			/* FALLTHROUGH */
 

	
 
		default:
 
			_midi.song[0] = '\0';
 
			break;
 
	}
 
}
 

	
 
static void DoStop(void)
 
{
 
	if (_midi.pid != -1) kill(_midi.pid, SIGTERM);
 
}
 

	
 
const HalMusicDriver _extmidi_music_driver = {
 
	ExtMidiStart,
 
	ExtMidiStop,
 
	ExtMidiPlaySong,
 
	ExtMidiStopSong,
 
	ExtMidiIsPlaying,
 
	ExtMidiSetVolume,
 
};
 

	
 
#endif /* __MORPHOS__ */
src/music/null_m.c
Show inline comments
 
deleted file
src/music/null_m.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "null_m.h"
 

	
 
static const char* NullMidiStart(const char* const* parm) { return NULL; }
 
static void NullMidiStop(void) {}
 
static void NullMidiPlaySong(const char *filename) {}
 
static void NullMidiStopSong(void) {}
 
static bool NullMidiIsSongPlaying(void) { return true; }
 
static void NullMidiSetVolume(byte vol) {}
 

	
 
const HalMusicDriver _null_music_driver = {
 
	NullMidiStart,
 
	NullMidiStop,
 
	NullMidiPlaySong,
 
	NullMidiStopSong,
 
	NullMidiIsSongPlaying,
 
	NullMidiSetVolume,
 
};
src/music/os2_m.c
Show inline comments
 
deleted file
src/music/os2_m.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "os2_m.h"
 

	
 
#define INCL_DOS
 
#define INCL_OS2MM
 
#define INCL_WIN
 

	
 
#include <stdarg.h>
 
#include <os2.h>
 
#include <os2me.h>
 

	
 
/**********************
 
 * OS/2 MIDI PLAYER
 
 **********************/
 

	
 
/* Interesting how similar the MCI API in OS/2 is to the Win32 MCI API,
 
 * eh? Anyone would think they both came from the same place originally! ;)
 
 */
 

	
 
static long CDECL MidiSendCommand(const char *cmd, ...)
 
{
 
	va_list va;
 
	char buf[512];
 
	va_start(va, cmd);
 
	vsprintf(buf, cmd, va);
 
	va_end(va);
 
	return mciSendString(buf, NULL, 0, NULL, 0);
 
}
 

	
 
static void OS2MidiPlaySong(const char *filename)
 
{
 
	MidiSendCommand("close all");
 

	
 
	if (MidiSendCommand("open %s type sequencer alias song", filename) != 0)
 
		return;
 

	
 
	MidiSendCommand("play song from 0");
 
}
 

	
 
static void OS2MidiStopSong(void)
 
{
 
	MidiSendCommand("close all");
 
}
 

	
 
static void OS2MidiSetVolume(byte vol)
 
{
 
	MidiSendCommand("set song audio volume %d", ((vol/127)*100));
 
}
 

	
 
static bool OS2MidiIsSongPlaying(void)
 
{
 
	char buf[16];
 
	mciSendString("status song mode", buf, sizeof(buf), NULL, 0);
 
	return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0;
 
}
 

	
 
static const char *OS2MidiStart(const char * const *parm)
 
{
 
	return 0;
 
}
 

	
 
static void OS2MidiStop(void)
 
{
 
	MidiSendCommand("close all");
 
}
 

	
 
const HalMusicDriver _os2_music_driver = {
 
	OS2MidiStart,
 
	OS2MidiStop,
 
	OS2MidiPlaySong,
 
	OS2MidiStopSong,
 
	OS2MidiIsSongPlaying,
 
	OS2MidiSetVolume,
 
};
src/music/qtmidi.c
Show inline comments
 
deleted file
src/music/qtmidi.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/**
 
 * @file qtmidi.c
 
 * @brief MIDI music player for MacOS X using QuickTime.
 
 *
 
 * This music player should work in all MacOS X releases starting from 10.0,
 
 * as QuickTime is an integral part of the system since the old days of the
 
 * Motorola 68k-based Macintoshes. The only extra dependency apart from
 
 * QuickTime itself is Carbon, which is included since 10.0 as well.
 
 *
 
 * QuickTime gets fooled with the MIDI files from Transport Tycoon Deluxe
 
 * because of the @c .gm suffix. To force QuickTime to load the MIDI files
 
 * without the need of dealing with the individual QuickTime components
 
 * needed to play music (data source, MIDI parser, note allocators,
 
 * synthesizers and the like) some Carbon functions are used to set the file
 
 * type as seen by QuickTime, using @c FSpSetFInfo() (which modifies the
 
 * file's resource fork).
 
 */
 

	
 

	
 
/*
 
 * OpenTTD includes.
 
 */
 
#define  WindowClass OSX_WindowClass
 
#include <QuickTime/QuickTime.h>
 
#undef   WindowClass
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "qtmidi.h"
 

	
 
/*
 
 * System includes. We need to workaround with some defines because there's
 
 * stuff already defined in QuickTime headers.
 
 */
 
#define  OTTD_Random OSX_OTTD_Random
 
#undef   OTTD_Random
 
#undef   WindowClass
 
#undef   SL_ERROR
 
#undef   bool
 

	
 
#include <assert.h>
 
#include <unistd.h>
 
#include <fcntl.h>
 

	
 
// we need to include debug.h after CoreServices because defining DEBUG will break CoreServices in OSX 10.2
 
#include "../debug.h"
 

	
 

	
 
enum {
 
	midiType = 'Midi' /**< OSType code for MIDI songs. */
 
};
 

	
 

	
 
/**
 
 * Converts a Unix-like pathname to a @c FSSpec structure which may be
 
 * used with functions from several MacOS X frameworks (Carbon, QuickTime,
 
 * etc). The pointed file or directory must exist.
 
 *
 
 * @param *path A string containing a Unix-like path.
 
 * @param *spec Pointer to a @c FSSpec structure where the result will be
 
 *              stored.
 
 * @return Wether the conversion was successful.
 
 */
 
static bool PathToFSSpec(const char *path, FSSpec *spec)
 
{
 
	FSRef ref;
 
	assert(spec != NULL);
 
	assert(path != NULL);
 

	
 
	return
 
		FSPathMakeRef((UInt8*)path, &ref, NULL) == noErr &&
 
		FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL) == noErr;
 
}
 

	
 

	
 
/**
 
 * Sets the @c OSType of a given file to @c 'Midi', but only if it's not
 
 * already set.
 
 *
 
 * @param *spec A @c FSSpec structure referencing a file.
 
 */
 
static void SetMIDITypeIfNeeded(const FSSpec *spec)
 
{
 
	FSRef ref;
 
	FSCatalogInfo catalogInfo;
 

	
 
	assert(spec);
 

	
 
	if (noErr != FSpMakeFSRef(spec, &ref)) return;
 
	if (noErr != FSGetCatalogInfo(&ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL)) return;
 
	if (!(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) {
 
		FileInfo * const info = (FileInfo *) catalogInfo.finderInfo;
 
		if (info->fileType != midiType && !(info->finderFlags & kIsAlias)) {
 
			OSErr e;
 
			info->fileType = midiType;
 
			e = FSSetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catalogInfo);
 
			if (e == noErr) {
 
				DEBUG(driver, 3, "qtmidi: changed filetype to 'Midi'");
 
			} else {
 
				DEBUG(driver, 0, "qtmidi: changing filetype to 'Midi' failed - error %d", e);
 
			}
 
		}
 
	}
 
}
 

	
 

	
 
/**
 
 * Loads a MIDI file and returns it as a QuickTime Movie structure.
 
 *
 
 * @param *path String with the path of an existing MIDI file.
 
 * @param *moov Pointer to a @c Movie where the result will be stored.
 
 * @return Wether the file was loaded and the @c Movie successfully created.
 
 */
 
static bool LoadMovieForMIDIFile(const char *path, Movie *moov)
 
{
 
	int fd;
 
	int ret;
 
	char magic[4];
 
	FSSpec fsspec;
 
	short refnum = 0;
 
	short resid  = 0;
 

	
 
	assert(path != NULL);
 
	assert(moov != NULL);
 

	
 
	DEBUG(driver, 2, "qtmidi: start loading '%s'...", path);
 

	
 
	/*
 
	 * XXX Manual check for MIDI header ('MThd'), as I don't know how to make
 
	 * QuickTime load MIDI files without a .mid suffix without knowing it's
 
	 * a MIDI file and setting the OSType of the file to the 'Midi' value.
 
	 * Perhahaps ugly, but it seems that it does the Right Thing(tm).
 
	 */
 
	fd = open(path, O_RDONLY, 0);
 
	if (fd == -1) return false;
 
	ret = read(fd, magic, 4);
 
	close(fd);
 
	if (ret < 4) return false;
 

	
 
	DEBUG(driver, 3, "qtmidi: header is '%.4s'", magic);
 
	if (magic[0] != 'M' || magic[1] != 'T' || magic[2] != 'h' || magic[3] != 'd')
 
		return false;
 

	
 
	if (!PathToFSSpec(path, &fsspec)) return false;
 
	SetMIDITypeIfNeeded(&fsspec);
 

	
 
	if (OpenMovieFile(&fsspec, &refnum, fsRdPerm) != noErr) return false;
 
	DEBUG(driver, 3, "qtmidi: '%s' successfully opened", path);
 

	
 
	if (noErr != NewMovieFromFile(moov, refnum, &resid, NULL,
 
				newMovieActive | newMovieDontAskUnresolvedDataRefs, NULL)) {
 
		CloseMovieFile(refnum);
 
		return false;
 
	}
 
	DEBUG(driver, 3, "qtmidi: movie container created");
 

	
 
	CloseMovieFile(refnum);
 
	return true;
 
}
 

	
 

	
 
/**
 
 * Flag which has the @c true value when QuickTime is available and
 
 * initialized.
 
 */
 
static bool _quicktime_started = false;
 

	
 

	
 
/**
 
 * Initialize QuickTime if needed. This function sets the
 
 * #_quicktime_started flag to @c true if QuickTime is present in the system
 
 * and it was initialized properly.
 
 */
 
static void InitQuickTimeIfNeeded(void)
 
{
 
	OSStatus dummy;
 

	
 
	if (_quicktime_started) return;
 

	
 
	DEBUG(driver, 2, "qtmidi: initializing Quicktime");
 
	/* Be polite: check wether QuickTime is available and initialize it. */
 
	_quicktime_started =
 
		(noErr == Gestalt(gestaltQuickTime, &dummy)) &&
 
		(noErr == EnterMovies());
 
	if (!_quicktime_started) DEBUG(driver, 0, "qtmidi: Quicktime initialization failed!");
 
}
 

	
 

	
 
/** Possible states of the QuickTime music driver. */
 
enum {
 
	QT_STATE_IDLE, /**< No file loaded. */
 
	QT_STATE_PLAY, /**< File loaded, playing. */
 
	QT_STATE_STOP, /**< File loaded, stopped. */
 
};
 

	
 

	
 
static Movie _quicktime_movie;                  /**< Current QuickTime @c Movie. */
 
static byte  _quicktime_volume = 127;           /**< Current volume. */
 
static int   _quicktime_state  = QT_STATE_IDLE; /**< Current player state. */
 

	
 

	
 
/**
 
 * Maps OpenTTD volume to QuickTime notion of volume.
 
 */
 
#define VOLUME  ((short)((0x00FF & _quicktime_volume) << 1))
 

	
 

	
 
static void StopSong(void);
 

	
 

	
 
/**
 
 * Initialized the MIDI player, including QuickTime initialization.
 
 *
 
 * @todo Give better error messages by inspecting error codes returned by
 
 * @c Gestalt() and @c EnterMovies(). Needs changes in
 
 * #InitQuickTimeIfNeeded.
 
 */
 
static const char* StartDriver(const char * const *parm)
 
{
 
	InitQuickTimeIfNeeded();
 
	return (_quicktime_started) ? NULL : "can't initialize QuickTime";
 
}
 

	
 

	
 
/**
 
 * Checks wether the player is active.
 
 *
 
 * This function is called at regular intervals from OpenTTD's main loop, so
 
 * we call @c MoviesTask() from here to let QuickTime do its work.
 
 */
 
static bool SongIsPlaying(void)
 
{
 
	if (!_quicktime_started) return true;
 

	
 
	switch (_quicktime_state) {
 
		case QT_STATE_IDLE:
 
		case QT_STATE_STOP:
 
			/* Do nothing. */
 
			break;
 
		case QT_STATE_PLAY:
 
			MoviesTask(_quicktime_movie, 0);
 
			/* Check wether movie ended. */
 
			if (IsMovieDone(_quicktime_movie) ||
 
					(GetMovieTime(_quicktime_movie, NULL) >=
 
					 GetMovieDuration(_quicktime_movie)))
 
				_quicktime_state = QT_STATE_STOP;
 
	}
 

	
 
	return _quicktime_state == QT_STATE_PLAY;
 
}
 

	
 

	
 
/**
 
 * Stops the MIDI player.
 
 *
 
 * Stops playing and frees any used resources before returning. As it
 
 * deinitilizes QuickTime, the #_quicktime_started flag is set to @c false.
 
 */
 
static void StopDriver(void)
 
{
 
	if (!_quicktime_started) return;
 

	
 
	DEBUG(driver, 2, "qtmidi: stopping driver...");
 
	switch (_quicktime_state) {
 
		case QT_STATE_IDLE:
 
			DEBUG(driver, 3, "qtmidi: stopping not needed, already idle");
 
			/* Do nothing. */
 
			break;
 
		case QT_STATE_PLAY:
 
			StopSong();
 
		case QT_STATE_STOP:
 
			DisposeMovie(_quicktime_movie);
 
	}
 

	
 
	ExitMovies();
 
	_quicktime_started = false;
 
}
 

	
 

	
 
/**
 
 * Starts playing a new song.
 
 *
 
 * @param filename Path to a MIDI file.
 
 */
 
static void PlaySong(const char *filename)
 
{
 
	if (!_quicktime_started) return;
 

	
 
	DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename);
 
	switch (_quicktime_state) {
 
		case QT_STATE_PLAY:
 
			StopSong();
 
			DEBUG(driver, 3, "qtmidi: previous tune stopped");
 
			/* XXX Fall-through -- no break needed. */
 
		case QT_STATE_STOP:
 
			DisposeMovie(_quicktime_movie);
 
			DEBUG(driver, 3, "qtmidi: previous tune disposed");
 
			_quicktime_state = QT_STATE_IDLE;
 
			/* XXX Fall-through -- no break needed. */
 
		case QT_STATE_IDLE:
 
			LoadMovieForMIDIFile(filename, &_quicktime_movie);
 
			SetMovieVolume(_quicktime_movie, VOLUME);
 
			StartMovie(_quicktime_movie);
 
			_quicktime_state = QT_STATE_PLAY;
 
	}
 
	DEBUG(driver, 3, "qtmidi: playing '%s'", filename);
 
}
 

	
 

	
 
/**
 
 * Stops playing the current song, if the player is active.
 
 */
 
static void StopSong(void)
 
{
 
	if (!_quicktime_started) return;
 

	
 
	switch (_quicktime_state) {
 
		case QT_STATE_IDLE:
 
			/* XXX Fall-through -- no break needed. */
 
		case QT_STATE_STOP:
 
			DEBUG(driver, 3, "qtmidi: stop requested, but already idle");
 
			/* Do nothing. */
 
			break;
 
		case QT_STATE_PLAY:
 
			StopMovie(_quicktime_movie);
 
			_quicktime_state = QT_STATE_STOP;
 
			DEBUG(driver, 3, "qtmidi: player stopped");
 
	}
 
}
 

	
 

	
 
/**
 
 * Changes the playing volume of the MIDI player.
 
 *
 
 * As QuickTime controls volume in a per-movie basis, the desired volume is
 
 * stored in #_quicktime_volume, and the volume is set here using the
 
 * #VOLUME macro, @b and when loading new song in #PlaySong.
 
 *
 
 * @param vol The desired volume, range of the value is @c 0-127
 
 */
 
static void SetVolume(byte vol)
 
{
 
	if (!_quicktime_started) return;
 

	
 
	_quicktime_volume = vol;
 

	
 
	DEBUG(driver, 2, "qtmidi: set volume to %u (%hi)", vol, VOLUME);
 
	switch (_quicktime_state) {
 
		case QT_STATE_IDLE:
 
			/* Do nothing. */
 
			break;
 
		case QT_STATE_PLAY:
 
		case QT_STATE_STOP:
 
			SetMovieVolume(_quicktime_movie, VOLUME);
 
	}
 
}
 

	
 

	
 
/**
 
 * Table of callbacks that implement the QuickTime MIDI player.
 
 */
 
const HalMusicDriver _qtime_music_driver = {
 
	StartDriver,
 
	StopDriver,
 
	PlaySong,
 
	StopSong,
 
	SongIsPlaying,
 
	SetVolume,
 
};
src/music/win32_m.c
Show inline comments
 
deleted file
src/music/win32_m.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "win32_m.h"
 
#include <windows.h>
 
#include <mmsystem.h>
 

	
 
static struct {
 
	bool stop_song;
 
	bool terminate;
 
	bool playing;
 
	int new_vol;
 
	HANDLE wait_obj;
 
	UINT_PTR devid;
 
	char start_song[260];
 
} _midi;
 

	
 
static void Win32MidiPlaySong(const char *filename)
 
{
 
	strcpy(_midi.start_song, filename);
 
	_midi.playing = true;
 
	_midi.stop_song = false;
 
	SetEvent(_midi.wait_obj);
 
}
 

	
 
static void Win32MidiStopSong(void)
 
{
 
	if (_midi.playing) {
 
		_midi.stop_song = true;
 
		_midi.start_song[0] = '\0';
 
		SetEvent(_midi.wait_obj);
 
	}
 
}
 

	
 
static bool Win32MidiIsSongPlaying(void)
 
{
 
	return _midi.playing;
 
}
 

	
 
static void Win32MidiSetVolume(byte vol)
 
{
 
	_midi.new_vol = vol;
 
	SetEvent(_midi.wait_obj);
 
}
 

	
 
static MCIERROR CDECL MidiSendCommand(const char* cmd, ...)
 
{
 
	va_list va;
 
	char buf[512];
 

	
 
	va_start(va, cmd);
 
	vsprintf(buf, cmd, va);
 
	va_end(va);
 
	return mciSendStringA(buf, NULL, 0, 0);
 
}
 

	
 
static bool MidiIntPlaySong(const char *filename)
 
{
 
	MidiSendCommand("close all");
 
	if (MidiSendCommand("open \"%s\" type sequencer alias song", filename) != 0)
 
		return false;
 

	
 
	if (MidiSendCommand("play song from 0") != 0)
 
		return false;
 
	return true;
 
}
 

	
 
static void MidiIntStopSong(void)
 
{
 
	MidiSendCommand("close all");
 
}
 

	
 
static void MidiIntSetVolume(int vol)
 
{
 
	DWORD v = (vol * 65535 / 127);
 
	midiOutSetVolume((HMIDIOUT)_midi.devid, v + (v << 16));
 
}
 

	
 
static bool MidiIntIsSongPlaying(void)
 
{
 
	char buf[16];
 
	mciSendStringA("status song mode", buf, sizeof(buf), 0);
 
	return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0;
 
}
 

	
 
static DWORD WINAPI MidiThread(LPVOID arg)
 
{
 
	_midi.wait_obj = CreateEvent(NULL, FALSE, FALSE, NULL);
 

	
 
	do {
 
		char *s;
 
		int vol;
 

	
 
		vol = _midi.new_vol;
 
		if (vol != -1) {
 
			_midi.new_vol = -1;
 
			MidiIntSetVolume(vol);
 
		}
 

	
 
		s = _midi.start_song;
 
		if (s[0] != '\0') {
 
			_midi.playing = MidiIntPlaySong(s);
 
			s[0] = '\0';
 

	
 
			// Delay somewhat in case we don't manage to play.
 
			if (!_midi.playing) {
 
				Sleep(5000);
 
			}
 
		}
 

	
 
		if (_midi.stop_song && _midi.playing) {
 
			_midi.stop_song = false;
 
			_midi.playing = false;
 
			MidiIntStopSong();
 
		}
 

	
 
		if (_midi.playing && !MidiIntIsSongPlaying())
 
			_midi.playing = false;
 

	
 
		WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 1000);
 
	} while (!_midi.terminate);
 

	
 
	DeleteObject(_midi.wait_obj);
 
	return 0;
 
}
 

	
 
static const char *Win32MidiStart(const char * const *parm)
 
{
 
	MIDIOUTCAPS midicaps;
 
	DWORD threadId;
 
	UINT nbdev;
 
	UINT_PTR dev;
 
	char buf[16];
 

	
 
	mciSendStringA("capability sequencer has audio", buf, lengthof(buf), 0);
 
	if (strcmp(buf, "true") != 0) return "MCI sequencer can't play audio";
 

	
 
	memset(&_midi, 0, sizeof(_midi));
 
	_midi.new_vol = -1;
 

	
 
	/* Get midi device */
 
	_midi.devid = MIDI_MAPPER;
 
	for (dev = 0, nbdev = midiOutGetNumDevs(); dev < nbdev; dev++) {
 
		if (midiOutGetDevCaps(dev, &midicaps, sizeof(midicaps)) == 0 && (midicaps.dwSupport & MIDICAPS_VOLUME)) {
 
			_midi.devid = dev;
 
			break;
 
		}
 
	}
 

	
 
	if (CreateThread(NULL, 8192, MidiThread, 0, 0, &threadId) == NULL)
 
		return "Failed to create thread";
 

	
 
	return NULL;
 
}
 

	
 
static void Win32MidiStop(void)
 
{
 
	_midi.terminate = true;
 
	SetEvent(_midi.wait_obj);
 
}
 

	
 
const HalMusicDriver _win32_music_driver = {
 
	Win32MidiStart,
 
	Win32MidiStop,
 
	Win32MidiPlaySong,
 
	Win32MidiStopSong,
 
	Win32MidiIsSongPlaying,
 
	Win32MidiSetVolume,
 
};
src/music_gui.c
Show inline comments
 
deleted file
src/music_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "functions.h"
 
#include "window.h"
 
#include "gfx.h"
 
#include "sound.h"
 
#include "hal.h"
 
#include "macros.h"
 
#include "variables.h"
 
#include "music.h"
 

	
 
static byte _music_wnd_cursong;
 
static bool _song_is_active;
 
static byte _cur_playlist[NUM_SONGS_PLAYLIST];
 

	
 

	
 

	
 
static byte _playlist_all[] = {
 
	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0
 
};
 

	
 
static byte _playlist_old_style[] = {
 
	1, 8, 2, 9, 14, 15, 19, 13, 0
 
};
 

	
 
static byte _playlist_new_style[] = {
 
	6, 11, 10, 17, 21, 18, 5, 0
 
};
 

	
 
static byte _playlist_ezy_street[] = {
 
	12, 7, 16, 3, 20, 4, 0
 
};
 

	
 
static byte * const _playlists[] = {
 
	_playlist_all,
 
	_playlist_old_style,
 
	_playlist_new_style,
 
	_playlist_ezy_street,
 
	msf.custom_1,
 
	msf.custom_2,
 
};
 

	
 
static void SkipToPrevSong(void)
 
{
 
	byte *b = _cur_playlist;
 
	byte *p = b;
 
	byte t;
 

	
 
	if (b[0] == 0) return; // empty playlist
 

	
 
	do p++; while (p[0] != 0); // find the end
 

	
 
	t = *--p; // and copy the bytes
 
	while (p != b) {
 
		p--;
 
		p[1] = p[0];
 
	}
 
	*b = t;
 

	
 
	_song_is_active = false;
 
}
 

	
 
static void SkipToNextSong(void)
 
{
 
	byte* b = _cur_playlist;
 
	byte t;
 

	
 
	t = b[0];
 
	if (t != 0) {
 
		while (b[1] != 0) {
 
			b[0] = b[1];
 
			b++;
 
		}
 
		b[0] = t;
 
	}
 

	
 
	_song_is_active = false;
 
}
 

	
 
static void MusicVolumeChanged(byte new_vol)
 
{
 
	_music_driver->set_volume(new_vol);
 
}
 

	
 
static void DoPlaySong(void)
 
{
 
	char filename[256];
 
	snprintf(filename, sizeof(filename), "%s%s",
 
		_paths.gm_dir, origin_songs_specs[_music_wnd_cursong - 1].filename);
 
	_music_driver->play_song(filename);
 
}
 

	
 
static void DoStopMusic(void)
 
{
 
	_music_driver->stop_song();
 
}
 

	
 
static void SelectSongToPlay(void)
 
{
 
	uint i = 0;
 
	uint j = 0;
 
	char filename[256];
 

	
 
	memset(_cur_playlist, 0, sizeof(_cur_playlist));
 
	do {
 
		if (_playlists[msf.playlist][i] != 0) {  // Don't evaluate playlist terminator
 
			snprintf(filename, sizeof(filename),  "%s%s",
 
				_paths.gm_dir, origin_songs_specs[(_playlists[msf.playlist][i]) - 1].filename);
 

	
 
			/* we are now checking for the existence of that file prior
 
			 * to add it to the list of available songs */
 
			if (FileExists(filename)) {
 
				_cur_playlist[j] = _playlists[msf.playlist][i];
 
				j++;
 
			}
 
		}
 
	} while (_playlists[msf.playlist][i++] != 0 && i < lengthof(_cur_playlist) - 1);
 

	
 
	if (msf.shuffle) {
 
		i = 500;
 
		do {
 
			uint32 r = InteractiveRandom();
 
			byte *a = &_cur_playlist[GB(r, 0, 5)];
 
			byte *b = &_cur_playlist[GB(r, 8, 5)];
 

	
 
			if (*a != 0 && *b != 0) {
 
				byte t = *a;
 
				*a = *b;
 
				*b = t;
 
			}
 
		} while (--i);
 
	}
 
}
 

	
 
static void StopMusic(void)
 
{
 
	_music_wnd_cursong = 0;
 
	DoStopMusic();
 
	_song_is_active = false;
 
	InvalidateWindowWidget(WC_MUSIC_WINDOW, 0, 9);
 
}
 

	
 
static void PlayPlaylistSong(void)
 
{
 
	if (_cur_playlist[0] == 0) {
 
		SelectSongToPlay();
 
		/* if there is not songs in the playlist, it may indicate
 
		 * no file on the gm folder, or even no gm folder.
 
		 * Stop the playback, then */
 
		if (_cur_playlist[0] == 0) {
 
			_song_is_active = false;
 
			_music_wnd_cursong = 0;
 
			msf.playing = false;
 
			return;
 
		}
 
	}
 
	_music_wnd_cursong = _cur_playlist[0];
 
	DoPlaySong();
 
	_song_is_active = true;
 

	
 
	InvalidateWindowWidget(WC_MUSIC_WINDOW, 0, 9);
 
}
 

	
 
void ResetMusic(void)
 
{
 
	_music_wnd_cursong = 1;
 
	DoPlaySong();
 
}
 

	
 
void MusicLoop(void)
 
{
 
	if (!msf.playing && _song_is_active) {
 
		StopMusic();
 
	} else if (msf.playing && !_song_is_active) {
 
		PlayPlaylistSong();
 
	}
 

	
 
	if (!_song_is_active) return;
 

	
 
	if (!_music_driver->is_song_playing()) {
 
		if (_game_mode != GM_MENU) {
 
			StopMusic();
 
			SkipToNextSong();
 
			PlayPlaylistSong();
 
		} else {
 
			ResetMusic();
 
		}
 
	}
 
}
 

	
 
static void MusicTrackSelectionWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const byte* p;
 
		uint i;
 
		int y;
 

	
 
		SetWindowWidgetDisabledState(w, 11, msf.playlist <= 3);
 
		LowerWindowWidget(w, 3);
 
		LowerWindowWidget(w, 4);
 
		DrawWindowWidgets(w);
 

	
 
		GfxFillRect(3, 23, 3+177,23+191,0);
 
		GfxFillRect(251, 23, 251+177,23+191,0);
 

	
 
		DrawStringCentered(92, 15, STR_01EE_TRACK_INDEX, 0);
 

	
 
		SetDParam(0, STR_01D5_ALL + msf.playlist);
 
		DrawStringCentered(340, 15, STR_01EF_PROGRAM, 0);
 

	
 
		for (i = 1; i <= NUM_SONGS_AVAILABLE; i++) {
 
			SetDParam(0, i);
 
			SetDParam(2, i);
 
			SetDParam(1, SPECSTR_SONGNAME);
 
			DrawString(4, 23+(i-1)*6, (i < 10) ? STR_01EC_0 : STR_01ED, 0);
 
		}
 

	
 
		for (i = 0; i != 6; i++) {
 
			DrawStringCentered(216, 45 + i * 8, STR_01D5_ALL + i, (i == msf.playlist) ? 0xC : 0x10);
 
		}
 

	
 
		DrawStringCentered(216, 45+8*6+16, STR_01F0_CLEAR, 0);
 
#if 0
 
		DrawStringCentered(216, 45 + 8 * 6 + 16 * 2, STR_01F1_SAVE, 0);
 
#endif
 

	
 
		y = 23;
 
		for (p = _playlists[msf.playlist], i = 0; (i = *p) != 0; p++) {
 
			SetDParam(0, i);
 
			SetDParam(1, SPECSTR_SONGNAME);
 
			SetDParam(2, i);
 
			DrawString(252, y, (i < 10) ? STR_01EC_0 : STR_01ED, 0);
 
			y += 6;
 
		}
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 3: { // add to playlist
 
			int y = (e->we.click.pt.y - 23) / 6;
 
			uint i;
 
			byte *p;
 

	
 
			if (msf.playlist < 4) return;
 
			if (!IS_INT_INSIDE(y, 0, NUM_SONGS_AVAILABLE)) return;
 

	
 
			p = _playlists[msf.playlist];
 
			for (i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) {
 
				if (p[i] == 0) {
 
					p[i] = y + 1;
 
					p[i + 1] = 0;
 
					SetWindowDirty(w);
 
					SelectSongToPlay();
 
					break;
 
				}
 
			}
 
		} break;
 

	
 
		case 4: { // remove from playlist
 
			int y = (e->we.click.pt.y - 23) / 6;
 
			uint i;
 
			byte *p;
 

	
 
			if (msf.playlist < 4) return;
 
			if (!IS_INT_INSIDE(y, 0, NUM_SONGS_AVAILABLE)) return;
 

	
 
			p = _playlists[msf.playlist];
 
			for (i = y; i != NUM_SONGS_PLAYLIST - 1; i++) {
 
				p[i] = p[i + 1];
 
				}
 

	
 
			SetWindowDirty(w);
 
			SelectSongToPlay();
 
		} break;
 

	
 
		case 11: // clear
 
			_playlists[msf.playlist][0] = 0;
 
			SetWindowDirty(w);
 
			StopMusic();
 
			SelectSongToPlay();
 
			break;
 

	
 
#if 0
 
		case 12: // save
 
			ShowInfo("MusicTrackSelectionWndProc:save not implemented");
 
			break;
 
#endif
 

	
 
		case 5: case 6: case 7: case 8: case 9: case 10: /* set playlist */
 
			msf.playlist = e->we.click.widget - 5;
 
			SetWindowDirty(w);
 
			InvalidateWindow(WC_MUSIC_WINDOW, 0);
 
			StopMusic();
 
			SelectSongToPlay();
 
			break;
 
		}
 
		break;
 
	}
 
}
 

	
 
static const Widget _music_track_selection_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   431,     0,    13, STR_01EB_MUSIC_PROGRAM_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   431,    14,   217, 0x0,                              STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     2,   181,    22,   215, 0x0,                              STR_01FA_CLICK_ON_MUSIC_TRACK_TO},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   250,   429,    22,   215, 0x0,                              STR_CLICK_ON_TRACK_TO_REMOVE},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   186,   245,    44,    51, 0x0,                              STR_01F3_SELECT_ALL_TRACKS_PROGRAM},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   186,   245,    52,    59, 0x0,                              STR_01F4_SELECT_OLD_STYLE_MUSIC},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   186,   245,    60,    67, 0x0,                              STR_01F5_SELECT_NEW_STYLE_MUSIC},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   186,   245,    68,    75, 0x0,                              STR_0330_SELECT_EZY_STREET_STYLE},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   186,   245,    76,    83, 0x0,                              STR_01F6_SELECT_CUSTOM_1_USER_DEFINED},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   186,   245,    84,    91, 0x0,                              STR_01F7_SELECT_CUSTOM_2_USER_DEFINED},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   186,   245,   108,   115, 0x0,                              STR_01F8_CLEAR_CURRENT_PROGRAM_CUSTOM1},
 
#if 0
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   186,   245,   124,   131, 0x0,                              STR_01F9_SAVE_MUSIC_SETTINGS},
 
#endif
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _music_track_selection_desc = {
 
	104, 131, 432, 218,
 
	WC_MUSIC_TRACK_SELECTION,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_music_track_selection_widgets,
 
	MusicTrackSelectionWndProc
 
};
 

	
 
static void ShowMusicTrackSelection(void)
 
{
 
	AllocateWindowDescFront(&_music_track_selection_desc, 0);
 
}
 

	
 
static void MusicWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		uint i;
 
		StringID str;
 

	
 
		RaiseWindowWidget(w, 7);
 
		RaiseWindowWidget(w, 9);
 
		DrawWindowWidgets(w);
 

	
 
		GfxFillRect(187, 16, 200, 33, 0);
 

	
 
		for (i = 0; i != 8; i++) {
 
			int color = 0xD0;
 
			if (i > 4) {
 
				color = 0xBF;
 
				if (i > 6) {
 
					color = 0xB8;
 
				}
 
			}
 
			GfxFillRect(187, NUM_SONGS_PLAYLIST - i * 2, 200, NUM_SONGS_PLAYLIST - i * 2, color);
 
		}
 

	
 
		GfxFillRect(60, 46, 239, 52, 0);
 

	
 
		if (_song_is_active == 0 || _music_wnd_cursong == 0) {
 
			str = STR_01E3;
 
		} else {
 
			SetDParam(0, _music_wnd_cursong);
 
			str = (_music_wnd_cursong < 10) ? STR_01E4_0 : STR_01E5;
 
		}
 
		DrawString(62, 46, str, 0);
 

	
 
		str = STR_01E6;
 
		if (_song_is_active != 0 && _music_wnd_cursong != 0) {
 
			str = STR_01E7;
 
			SetDParam(0, SPECSTR_SONGNAME);
 
			SetDParam(1, _music_wnd_cursong);
 
		}
 
		DrawStringCentered(155, 46, str, 0);
 

	
 

	
 
		DrawString(60, 38, STR_01E8_TRACK_XTITLE, 0);
 

	
 
		for (i = 0; i != 6; i++) {
 
			DrawStringCentered(25 + i * 50, 59, STR_01D5_ALL + i, msf.playlist == i ? 0xC : 0x10);
 
		}
 

	
 
		DrawStringCentered(31, 43, STR_01E9_SHUFFLE, (msf.shuffle ? 0xC : 0x10));
 
		DrawStringCentered(269, 43, STR_01EA_PROGRAM, 0);
 
		DrawStringCentered(141, 15, STR_01DB_MUSIC_VOLUME, 0);
 
		DrawStringCentered(141, 29, STR_01DD_MIN_MAX, 0);
 
		DrawStringCentered(247, 15, STR_01DC_EFFECTS_VOLUME, 0);
 
		DrawStringCentered(247, 29, STR_01DD_MIN_MAX, 0);
 

	
 
		DrawFrameRect(108, 23, 174, 26, 14, FR_LOWERED);
 
		DrawFrameRect(214, 23, 280, 26, 14, FR_LOWERED);
 

	
 
		DrawFrameRect(
 
			108 + msf.music_vol / 2, 22, 111 + msf.music_vol / 2, 28, 14, 0
 
		);
 

	
 
		DrawFrameRect(
 
			214 + msf.effect_vol / 2, 22, 217 + msf.effect_vol / 2, 28, 14, 0
 
		);
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 2: // skip to prev
 
			if (!_song_is_active)
 
				return;
 
			SkipToPrevSong();
 
			break;
 
		case 3: // skip to next
 
			if (!_song_is_active)
 
				return;
 
			SkipToNextSong();
 
			break;
 
		case 4: // stop playing
 
			msf.playing = false;
 
			break;
 
		case 5: // start playing
 
			msf.playing = true;
 
			break;
 
		case 6:{ // volume sliders
 
			byte *vol,new_vol;
 
			int x = e->we.click.pt.x - 88;
 

	
 
			if (x < 0)
 
				return;
 

	
 
			vol = &msf.music_vol;
 
			if (x >= 106) {
 
				vol = &msf.effect_vol;
 
				x -= 106;
 
			}
 

	
 
			new_vol = min(max(x-21,0)*2,127);
 
			if (new_vol != *vol) {
 
				*vol = new_vol;
 
				if (vol == &msf.music_vol)
 
					MusicVolumeChanged(new_vol);
 
				SetWindowDirty(w);
 
			}
 

	
 
			_left_button_clicked = false;
 
		} break;
 
		case 10: //toggle shuffle
 
			msf.shuffle ^= 1;
 
			StopMusic();
 
			SelectSongToPlay();
 
			break;
 
		case 11: //show track selection
 
			ShowMusicTrackSelection();
 
			break;
 
		case 12: case 13: case 14: case 15: case 16: case 17: // playlist
 
			msf.playlist = e->we.click.widget - 12;
 
			SetWindowDirty(w);
 
			InvalidateWindow(WC_MUSIC_TRACK_SELECTION, 0);
 
			StopMusic();
 
			SelectSongToPlay();
 
			break;
 
		}
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		InvalidateWindowWidget(WC_MUSIC_WINDOW, 0, 7);
 
		break;
 
	}
 

	
 
}
 

	
 
static const Widget _music_window_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   299,     0,    13, STR_01D2_JAZZ_JUKEBOX, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,    14,     0,    21,    14,    35, SPR_IMG_SKIP_TO_PREV,  STR_01DE_SKIP_TO_PREVIOUS_TRACK},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,    14,    22,    43,    14,    35, SPR_IMG_SKIP_TO_NEXT,  STR_01DF_SKIP_TO_NEXT_TRACK_IN_SELECTION},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,    14,    44,    65,    14,    35, SPR_IMG_STOP_MUSIC,    STR_01E0_STOP_PLAYING_MUSIC},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,    14,    66,    87,    14,    35, SPR_IMG_PLAY_MUSIC,    STR_01E1_START_PLAYING_MUSIC},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    88,   299,    14,    35, 0x0,                   STR_01E2_DRAG_SLIDERS_TO_SET_MUSIC},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   186,   201,    15,    34, 0x0,                   STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   299,    36,    57, 0x0,                   STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    59,   240,    45,    53, 0x0,                   STR_NULL},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,     6,    55,    42,    49, 0x0,                   STR_01FB_TOGGLE_PROGRAM_SHUFFLE},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   244,   293,    42,    49, 0x0,                   STR_01FC_SHOW_MUSIC_TRACK_SELECTION},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,     0,    49,    58,    65, 0x0,                   STR_01F3_SELECT_ALL_TRACKS_PROGRAM},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,    50,    99,    58,    65, 0x0,                   STR_01F4_SELECT_OLD_STYLE_MUSIC},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   100,   149,    58,    65, 0x0,                   STR_01F5_SELECT_NEW_STYLE_MUSIC},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   150,   199,    58,    65, 0x0,                   STR_0330_SELECT_EZY_STREET_STYLE},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   200,   249,    58,    65, 0x0,                   STR_01F6_SELECT_CUSTOM_1_USER_DEFINED},
 
{    WWT_PUSHBTN,   RESIZE_NONE,    14,   250,   299,    58,    65, 0x0,                   STR_01F7_SELECT_CUSTOM_2_USER_DEFINED},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _music_window_desc = {
 
	0, 22, 300, 66,
 
	WC_MUSIC_WINDOW,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_music_window_widgets,
 
	MusicWindowWndProc
 
};
 

	
 
void ShowMusicWindow(void)
 
{
 
	AllocateWindowDescFront(&_music_window_desc, 0);
 
}
src/namegen.c
Show inline comments
 
deleted file
src/namegen.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "macros.h"
 
#include "namegen.h"
 
#include "table/namegen.h"
 
#include "string.h"
 

	
 
static inline uint32 SeedChance(int shift_by, int max, uint32 seed)
 
{
 
	return (GB(seed, shift_by, 16) * max) >> 16;
 
}
 

	
 
static inline uint32 SeedModChance(int shift_by, int max, uint32 seed)
 
{
 
	/* This actually gives *MUCH* more even distribution of the values
 
	 * than SeedChance(), which is absolutely horrible in that. If
 
	 * you do not believe me, try with i.e. the Czech town names,
 
	 * compare the words (nicely visible on prefixes) generated by
 
	 * SeedChance() and SeedModChance(). Do not get dicouraged by the
 
	 * never-use-modulo myths, which hold true only for the linear
 
	 * congruential generators (and Random() isn't such a generator).
 
	 * --pasky */
 
	// TODO: Perhaps we should use it for all the name generators? --pasky
 
	return (seed >> shift_by) % max;
 
}
 

	
 
static inline int32 SeedChanceBias(int shift_by, int max, uint32 seed, int bias)
 
{
 
	return SeedChance(shift_by, max + bias, seed) - bias;
 
}
 

	
 
static void ReplaceWords(const char *org, const char *rep, char *buf)
 
{
 
	if (strncmp(buf, org, 4) == 0) strncpy(buf, rep, 4);
 
}
 

	
 
static byte MakeEnglishOriginalTownName(char *buf, uint32 seed, const char *last)
 
{
 
	int i;
 

	
 
	//null terminates the string for strcat
 
	strecpy(buf, "", last);
 

	
 
	// optional first segment
 
	i = SeedChanceBias(0, lengthof(name_original_english_1), seed, 50);
 
	if (i >= 0)
 
		strecat(buf, name_original_english_1[i], last);
 

	
 
	//mandatory middle segments
 
	strecat(buf, name_original_english_2[SeedChance(4,  lengthof(name_original_english_2), seed)], last);
 
	strecat(buf, name_original_english_3[SeedChance(7,  lengthof(name_original_english_3), seed)], last);
 
	strecat(buf, name_original_english_4[SeedChance(10, lengthof(name_original_english_4), seed)], last);
 
	strecat(buf, name_original_english_5[SeedChance(13, lengthof(name_original_english_5), seed)], last);
 

	
 
	//optional last segment
 
	i = SeedChanceBias(15, lengthof(name_original_english_6), seed, 60);
 
	if (i >= 0)
 
		strecat(buf, name_original_english_6[i], last);
 

	
 
	if (buf[0] == 'C' && (buf[1] == 'e' || buf[1] == 'i'))
 
		buf[0] = 'K';
 

	
 
	ReplaceWords("Cunt", "East", buf);
 
	ReplaceWords("Slag", "Pits", buf);
 
	ReplaceWords("Slut", "Edin", buf);
 
	//ReplaceWords("Fart", "Boot", buf);
 
	ReplaceWords("Drar", "Quar", buf);
 
	ReplaceWords("Dreh", "Bash", buf);
 
	ReplaceWords("Frar", "Shor", buf);
 
	ReplaceWords("Grar", "Aber", buf);
 
	ReplaceWords("Brar", "Over", buf);
 
	ReplaceWords("Wrar", "Inve", buf);
 

	
 
	return 0;
 
}
 

	
 

	
 
static byte MakeEnglishAdditionalTownName(char *buf, uint32 seed, const char *last)
 
{
 
	int i;
 

	
 
	//null terminates the string for strcat
 
	strecpy(buf, "", last);
 

	
 
	// optional first segment
 
	i = SeedChanceBias(0, lengthof(name_additional_english_prefix), seed, 50);
 
	if (i >= 0)
 
		strecat(buf,name_additional_english_prefix[i], last);
 

	
 
	if (SeedChance(3, 20, seed) >= 14) {
 
		strecat(buf, name_additional_english_1a[SeedChance(6, lengthof(name_additional_english_1a), seed)], last);
 
	} else {
 
		strecat(buf, name_additional_english_1b1[SeedChance(6, lengthof(name_additional_english_1b1), seed)], last);
 
		strecat(buf, name_additional_english_1b2[SeedChance(9, lengthof(name_additional_english_1b2), seed)], last);
 
		if (SeedChance(11, 20, seed) >= 4) {
 
			strecat(buf, name_additional_english_1b3a[SeedChance(12, lengthof(name_additional_english_1b3a), seed)], last);
 
		} else {
 
			strecat(buf, name_additional_english_1b3b[SeedChance(12, lengthof(name_additional_english_1b3b), seed)], last);
 
		}
 
	}
 

	
 
	strecat(buf, name_additional_english_2[SeedChance(14, lengthof(name_additional_english_2), seed)], last);
 

	
 
	//optional last segment
 
	i = SeedChanceBias(15, lengthof(name_additional_english_3), seed, 60);
 
	if (i >= 0)
 
		strecat(buf, name_additional_english_3[i], last);
 

	
 
	ReplaceWords("Cunt", "East", buf);
 
	ReplaceWords("Slag", "Pits", buf);
 
	ReplaceWords("Slut", "Edin", buf);
 
	ReplaceWords("Fart", "Boot", buf);
 
	ReplaceWords("Drar", "Quar", buf);
 
	ReplaceWords("Dreh", "Bash", buf);
 
	ReplaceWords("Frar", "Shor", buf);
 
	ReplaceWords("Grar", "Aber", buf);
 
	ReplaceWords("Brar", "Over", buf);
 
	ReplaceWords("Wrar", "Stan", buf);
 

	
 
	return 0;
 
}
 

	
 
static byte MakeAustrianTownName(char *buf, uint32 seed, const char *last)
 
{
 
	int i, j = 0;
 
	strecpy(buf, "", last);
 

	
 
	// Bad, Maria, Gross, ...
 
	i = SeedChanceBias(0, lengthof(name_austrian_a1), seed, 15);
 
	if (i >= 0) strecat(buf, name_austrian_a1[i], last);
 

	
 
	i = SeedChance(4, 6, seed);
 
	if (i >= 4) {
 
		// Kaisers-kirchen
 
		strecat(buf, name_austrian_a2[SeedChance( 7, lengthof(name_austrian_a2), seed)], last);
 
		strecat(buf, name_austrian_a3[SeedChance(13, lengthof(name_austrian_a3), seed)], last);
 
	} else if (i >= 2) {
 
		// St. Johann
 
		strecat(buf, name_austrian_a5[SeedChance( 7, lengthof(name_austrian_a5), seed)], last);
 
		strecat(buf, name_austrian_a6[SeedChance( 9, lengthof(name_austrian_a6), seed)], last);
 
		j = 1; // More likely to have a " an der " or " am "
 
	} else {
 
		// Zell
 
		strecat(buf, name_austrian_a4[SeedChance( 7, lengthof(name_austrian_a4), seed)], last);
 
	}
 

	
 
	i = SeedChance(1, 6, seed);
 
	if (i >= 4 - j) {
 
		// an der Donau (rivers)
 
		strecat(buf, name_austrian_f1[SeedChance(4, lengthof(name_austrian_f1), seed)], last);
 
		strecat(buf, name_austrian_f2[SeedChance(5, lengthof(name_austrian_f2), seed)], last);
 
	} else if (i >= 2 - j) {
 
		// am Dachstein (mountains)
 
		strecat(buf, name_austrian_b1[SeedChance(4, lengthof(name_austrian_b1), seed)], last);
 
		strecat(buf, name_austrian_b2[SeedChance(5, lengthof(name_austrian_b2), seed)], last);
 
	}
 

	
 
	return 0;
 
}
 

	
 
static byte MakeGermanTownName(char *buf, uint32 seed, const char *last)
 
{
 
	uint i;
 
	uint seed_derivative;
 

	
 
	//null terminates the string for strcat
 
	strecpy(buf, "", last);
 

	
 
	seed_derivative = SeedChance(7, 28, seed);
 

	
 
	//optional prefix
 
	if (seed_derivative == 12 || seed_derivative == 19) {
 
		i = SeedChance(2, lengthof(name_german_pre), seed);
 
		strecat(buf,name_german_pre[i], last);
 
	}
 

	
 
	// mandatory middle segments including option of hardcoded name
 
	i = SeedChance(3, lengthof(name_german_real) + lengthof(name_german_1), seed);
 
	if (i < lengthof(name_german_real)) {
 
		strecat(buf,name_german_real[i], last);
 
	} else {
 
		strecat(buf, name_german_1[i - lengthof(name_german_real)], last);
 

	
 
		i = SeedChance(5, lengthof(name_german_2), seed);
 
		strecat(buf, name_german_2[i], last);
 
	}
 

	
 
	// optional suffix
 
	if (seed_derivative == 24) {
 
		i = SeedChance(9,
 
			lengthof(name_german_4_an_der) + lengthof(name_german_4_am), seed);
 
		if (i < lengthof(name_german_4_an_der)) {
 
			strecat(buf, name_german_3_an_der[0], last);
 
			strecat(buf, name_german_4_an_der[i], last);
 
		} else {
 
			strecat(buf, name_german_3_am[0], last);
 
			strecat(buf, name_german_4_am[i - lengthof(name_german_4_an_der)], last);
 
		}
 
	}
 
	return 0;
 
}
 

	
 
static byte MakeSpanishTownName(char *buf, uint32 seed, const char *last)
 
{
 
	strecpy(buf, name_spanish_real[SeedChance(0, lengthof(name_spanish_real), seed)], last);
 
	return 0;
 
}
 

	
 
static byte MakeFrenchTownName(char *buf, uint32 seed, const char *last)
 
{
 
	strecpy(buf, name_french_real[SeedChance(0, lengthof(name_french_real), seed)], last);
 
	return 0;
 
}
 

	
 
static byte MakeSillyTownName(char *buf, uint32 seed, const char *last)
 
{
 
	strecpy(buf, name_silly_1[SeedChance( 0, lengthof(name_silly_1), seed)], last);
 
	strecat(buf, name_silly_2[SeedChance(16, lengthof(name_silly_2), seed)], last);
 
	return 0;
 
}
 

	
 
static byte MakeSwedishTownName(char *buf, uint32 seed, const char *last)
 
{
 
	int i;
 

	
 
	//null terminates the string for strcat
 
	strecpy(buf, "", last);
 

	
 
	// optional first segment
 
	i = SeedChanceBias(0, lengthof(name_swedish_1), seed, 50);
 
	if (i >= 0)
 
		strecat(buf, name_swedish_1[i], last);
 

	
 
	// mandatory middle segments including option of hardcoded name
 
	if (SeedChance(4, 5, seed) >= 3) {
 
		strecat(buf, name_swedish_2[SeedChance( 7, lengthof(name_swedish_2), seed)], last);
 
	} else {
 
		strecat(buf, name_swedish_2a[SeedChance( 7, lengthof(name_swedish_2a), seed)], last);
 
		strecat(buf, name_swedish_2b[SeedChance(10, lengthof(name_swedish_2b), seed)], last);
 
		strecat(buf, name_swedish_2c[SeedChance(13, lengthof(name_swedish_2c), seed)], last);
 
	}
 

	
 
	strecat(buf, name_swedish_3[SeedChance(16, lengthof(name_swedish_3), seed)], last);
 

	
 
	return 0;
 
}
 

	
 
static byte MakeDutchTownName(char *buf, uint32 seed, const char *last)
 
{
 
	int i;
 

	
 
	//null terminates the string for strcat
 
	strecpy(buf, "", last);
 

	
 
	// optional first segment
 
	i = SeedChanceBias(0, lengthof(name_dutch_1), seed, 50);
 
	if (i >= 0)
 
		strecat(buf, name_dutch_1[i], last);
 

	
 
	// mandatory middle segments including option of hardcoded name
 
	if (SeedChance(6, 9, seed) > 4) {
 
		strecat(buf, name_dutch_2[SeedChance( 9, lengthof(name_dutch_2), seed)], last);
 
	} else {
 
		strecat(buf, name_dutch_3[SeedChance( 9, lengthof(name_dutch_3), seed)], last);
 
		strecat(buf, name_dutch_4[SeedChance(12, lengthof(name_dutch_4), seed)], last);
 
	}
 
	strecat(buf, name_dutch_5[SeedChance(15, lengthof(name_dutch_5), seed)], last);
 

	
 
	return 0;
 
}
 

	
 
static byte MakeFinnishTownName(char *buf, uint32 seed, const char *last)
 
{
 
	//null terminates the string for strcat
 
	strecpy(buf, "", last);
 

	
 
	// Select randomly if town name should consists of one or two parts.
 
	if (SeedChance(0, 15, seed) >= 10) {
 
		strecat(buf, name_finnish_real[SeedChance(2, lengthof(name_finnish_real), seed)], last);
 
	} else if (SeedChance(0, 15, seed) >= 5) {
 
		// A two-part name by combining one of name_finnish_1 + "la"/"lä"
 
		// The reason for not having the contents of name_finnish_{1,2} in the same table is
 
		// that the ones in name_finnish_2 are not good for this purpose.
 
		uint sel = SeedChance( 0, lengthof(name_finnish_1), seed);
 
		char *end;
 
		strecat(buf, name_finnish_1[sel], last);
 
		end = &buf[strlen(buf)-1];
 
		if (*end == 'i')
 
			*end = 'e';
 
		if (strstr(buf, "a") || strstr(buf, "o") || strstr(buf, "u") ||
 
			strstr(buf, "A") || strstr(buf, "O") || strstr(buf, "U"))
 
		{
 
			strecat(buf, "la", last);
 
		} else {
 
			strecat(buf, "lä", last);
 
		}
 
	} else {
 
		// A two-part name by combining one of name_finnish_{1,2} + name_finnish_3.
 
		// Why aren't name_finnish_{1,2} just one table? See above.
 
		uint sel = SeedChance(2,
 
			lengthof(name_finnish_1) + lengthof(name_finnish_2), seed);
 
		if (sel >= lengthof(name_finnish_1)) {
 
			strecat(buf, name_finnish_2[sel-lengthof(name_finnish_1)], last);
 
		} else {
 
			strecat(buf, name_finnish_1[sel], last);
 
		}
 
		strecat(buf, name_finnish_3[SeedChance(10, lengthof(name_finnish_3), seed)], last);
 
	}
 

	
 
	return 0;
 
}
 

	
 
static byte MakePolishTownName(char *buf, uint32 seed, const char *last)
 
{
 
	uint i;
 
	uint j;
 

	
 
	//null terminates the string for strcat
 
	strecpy(buf, "", last);
 

	
 
	// optional first segment
 
	i = SeedChance(0,
 
		lengthof(name_polish_2_o) + lengthof(name_polish_2_m) +
 
		lengthof(name_polish_2_f) + lengthof(name_polish_2_n),
 
		seed);
 
	j = SeedChance(2, 20, seed);
 

	
 

	
 
	if (i < lengthof(name_polish_2_o)) {
 
		strecat(buf, name_polish_2_o[SeedChance(3, lengthof(name_polish_2_o), seed)], last);
 
	} else if (i < lengthof(name_polish_2_m) + lengthof(name_polish_2_o)) {
 
		if (j < 4)
 
			strecat(buf, name_polish_1_m[SeedChance(5, lengthof(name_polish_1_m), seed)], last);
 

	
 
		strecat(buf, name_polish_2_m[SeedChance(7, lengthof(name_polish_2_m), seed)], last);
 

	
 
		if (j >= 4 && j < 16)
 
			strecat(buf, name_polish_3_m[SeedChance(10, lengthof(name_polish_3_m), seed)], last);
 
	} else if (i < lengthof(name_polish_2_f) + lengthof(name_polish_2_m) + lengthof(name_polish_2_o)) {
 
		if (j < 4)
 
			strecat(buf, name_polish_1_f[SeedChance(5, lengthof(name_polish_1_f), seed)], last);
 

	
 
		strecat(buf, name_polish_2_f[SeedChance(7, lengthof(name_polish_2_f), seed)], last);
 

	
 
		if (j >= 4 && j < 16)
 
			strecat(buf, name_polish_3_f[SeedChance(10, lengthof(name_polish_3_f), seed)], last);
 
	} else {
 
		if (j < 4)
 
			strecat(buf, name_polish_1_n[SeedChance(5, lengthof(name_polish_1_n), seed)], last);
 

	
 
		strecat(buf, name_polish_2_n[SeedChance(7, lengthof(name_polish_2_n), seed)], last);
 

	
 
		if (j >= 4 && j < 16)
 
			strecat(buf, name_polish_3_n[SeedChance(10, lengthof(name_polish_3_n), seed)], last);
 
	}
 
	return 0;
 
}
 

	
 
static byte MakeCzechTownName(char *buf, uint32 seed, const char *last)
 
{
 
	/* Probability of prefixes/suffixes */
 
	/* 0..11 prefix, 12..13 prefix+suffix, 14..17 suffix, 18..31 nothing */
 
	int prob_tails;
 
	bool do_prefix, do_suffix, dynamic_subst;
 
	/* IDs of the respective parts */
 
	int prefix = 0, ending = 0, suffix = 0;
 
	uint postfix = 0;
 
	uint stem;
 
	/* The select criteria. */
 
	CzechGender gender;
 
	CzechChoose choose;
 
	CzechAllow allow;
 

	
 
	// 1:3 chance to use a real name.
 
	if (SeedModChance(0, 4, seed) == 0) {
 
		strecpy(buf, name_czech_real[SeedModChance(4, lengthof(name_czech_real), seed)], last);
 
		return 0;
 
	}
 

	
 
	// NUL terminates the string for strcat()
 
	strecpy(buf, "", last);
 

	
 
	prob_tails = SeedModChance(2, 32, seed);
 
	do_prefix = prob_tails < 12;
 
	do_suffix = prob_tails > 11 && prob_tails < 17;
 

	
 
	if (do_prefix) prefix = SeedModChance(5, lengthof(name_czech_adj) * 12, seed) / 12;
 
	if (do_suffix) suffix = SeedModChance(7, lengthof(name_czech_suffix), seed);
 
	// 3:1 chance 3:1 to use dynamic substantive
 
	stem = SeedModChance(9,
 
		lengthof(name_czech_subst_full) + 3 * lengthof(name_czech_subst_stem),
 
		seed);
 
	if (stem < lengthof(name_czech_subst_full)) {
 
		// That was easy!
 
		dynamic_subst = false;
 
		gender = name_czech_subst_full[stem].gender;
 
		choose = name_czech_subst_full[stem].choose;
 
		allow = name_czech_subst_full[stem].allow;
 
	} else {
 
		unsigned int map[lengthof(name_czech_subst_ending)];
 
		int ending_start = -1, ending_stop = -1;
 
		int i;
 

	
 
		// Load the substantive
 
		dynamic_subst = true;
 
		stem -= lengthof(name_czech_subst_full);
 
		stem %= lengthof(name_czech_subst_stem);
 
		gender = name_czech_subst_stem[stem].gender;
 
		choose = name_czech_subst_stem[stem].choose;
 
		allow = name_czech_subst_stem[stem].allow;
 

	
 
		// Load the postfix (1:1 chance that a postfix will be inserted)
 
		postfix = SeedModChance(14, lengthof(name_czech_subst_postfix) * 2, seed);
 

	
 
		if (choose & CZC_POSTFIX) {
 
			// Always get a real postfix.
 
			postfix %= lengthof(name_czech_subst_postfix);
 
		}
 
		if (choose & CZC_NOPOSTFIX) {
 
			// Always drop a postfix.
 
			postfix += lengthof(name_czech_subst_postfix);
 
		}
 
		if (postfix < lengthof(name_czech_subst_postfix)) {
 
			choose |= CZC_POSTFIX;
 
		} else {
 
			choose |= CZC_NOPOSTFIX;
 
		}
 

	
 
		// Localize the array segment containing a good gender
 
		for (ending = 0; ending < (int) lengthof(name_czech_subst_ending); ending++) {
 
			const CzechNameSubst *e = &name_czech_subst_ending[ending];
 

	
 
			if (gender == CZG_FREE ||
 
					(gender == CZG_NFREE && e->gender != CZG_SNEUT && e->gender != CZG_PNEUT) ||
 
					gender == e->gender) {
 
				if (ending_start < 0)
 
					ending_start = ending;
 

	
 
			} else if (ending_start >= 0) {
 
				ending_stop = ending - 1;
 
				break;
 
			}
 
		}
 
		if (ending_stop < 0) {
 
			// Whoa. All the endings matched.
 
			ending_stop = ending - 1;
 
		}
 

	
 
		// Make a sequential map of the items with good mask
 
		i = 0;
 
		for (ending = ending_start; ending <= ending_stop; ending++) {
 
			const CzechNameSubst *e = &name_czech_subst_ending[ending];
 

	
 
			if ((e->choose & choose) == choose && (e->allow & allow) != 0)
 
				map[i++] = ending;
 
		}
 
		assert(i > 0);
 

	
 
		// Load the ending
 
		ending = map[SeedModChance(16, i, seed)];
 
		// Override possible CZG_*FREE; this must be a real gender,
 
		// otherwise we get overflow when modifying the adjectivum.
 
		gender = name_czech_subst_ending[ending].gender;
 
		assert(gender != CZG_FREE && gender != CZG_NFREE);
 
	}
 

	
 
	if (do_prefix && (name_czech_adj[prefix].choose & choose) != choose) {
 
		// Throw away non-matching prefix.
 
		do_prefix = false;
 
	}
 

	
 
	// Now finally construct the name
 

	
 
	if (do_prefix) {
 
		CzechPattern pattern = name_czech_adj[prefix].pattern;
 
		size_t endpos;
 

	
 
		strecat(buf, name_czech_adj[prefix].name, last);
 
		endpos = strlen(buf) - 1;
 
		/* Find the first character in a UTF-8 sequence */
 
		while (GB(buf[endpos], 6, 2) == 2) endpos--;
 
		if (gender == CZG_SMASC && pattern == CZP_PRIVL) {
 
			/* -ovX -> -uv */
 
			buf[endpos - 2] = 'u';
 
			assert(buf[endpos - 1] == 'v');
 
			buf[endpos] = '\0';
 
		} else {
 
			strecpy(buf + endpos, name_czech_patmod[gender][pattern], last);
 
		}
 

	
 
		strecat(buf, " ", last);
 
	}
 

	
 
	if (dynamic_subst) {
 
		strecat(buf, name_czech_subst_stem[stem].name, last);
 
		if (postfix < lengthof(name_czech_subst_postfix)) {
 
			const char *poststr = name_czech_subst_postfix[postfix];
 
			const char *endstr = name_czech_subst_ending[ending].name;
 
			size_t postlen, endlen;
 

	
 
			postlen = strlen(poststr);
 
			endlen = strlen(endstr);
 
			assert(postlen > 0 && endlen > 0);
 

	
 
			// Kill the "avava" and "Jananna"-like cases
 
			if (postlen < 2 || postlen > endlen || (
 
						(poststr[1] != 'v' || poststr[1] != endstr[1]) &&
 
						poststr[2] != endstr[1])
 
					) {
 
				size_t buflen;
 
				strecat(buf, poststr, last);
 
				buflen = strlen(buf);
 

	
 
				// k-i -> c-i, h-i -> z-i
 
				if (endstr[0] == 'i') {
 
					switch (buf[buflen - 1]) {
 
						case 'k': buf[buflen - 1] = 'c'; break;
 
						case 'h': buf[buflen - 1] = 'z'; break;
 
						default: break;
 
					}
 
				}
 
			}
 
		}
 
		strecat(buf, name_czech_subst_ending[ending].name, last);
 
	} else {
 
		strecat(buf, name_czech_subst_full[stem].name, last);
 
	}
 

	
 
	if (do_suffix) {
 
		strecat(buf, " ", last);
 
		strecat(buf, name_czech_suffix[suffix], last);
 
	}
 

	
 
	return 0;
 
}
 

	
 
static byte MakeRomanianTownName(char *buf, uint32 seed, const char *last)
 
{
 
	strecpy(buf, name_romanian_real[SeedChance(0, lengthof(name_romanian_real), seed)], last);
 
	return 0;
 
}
 

	
 
static byte MakeSlovakTownName(char *buf, uint32 seed, const char *last)
 
{
 
	strecpy(buf, name_slovak_real[SeedChance(0, lengthof(name_slovak_real), seed)], last);
 
	return 0;
 
}
 

	
 
static byte MakeNorwegianTownName(char *buf, uint32 seed, const char *last)
 
{
 
	strecpy(buf, "", last);
 

	
 
	// Use first 4 bit from seed to decide whether or not this town should
 
	// have a real name 3/16 chance.  Bit 0-3
 
	if (SeedChance(0, 15, seed) < 3) {
 
		// Use 7bit for the realname table index.  Bit 4-10
 
		strecat(buf, name_norwegian_real[SeedChance(4, lengthof(name_norwegian_real), seed)], last);
 
	} else {
 
		// Use 7bit for the first fake part.  Bit 4-10
 
		strecat(buf, name_norwegian_1[SeedChance(4, lengthof(name_norwegian_1), seed)], last);
 
		// Use 7bit for the last fake part.  Bit 11-17
 
		strecat(buf, name_norwegian_2[SeedChance(11, lengthof(name_norwegian_2), seed)], last);
 
	}
 

	
 
	return 0;
 
}
 

	
 
static byte MakeHungarianTownName(char *buf, uint32 seed, const char *last)
 
{
 
	uint i;
 

	
 
	//null terminates the string for strcat
 
	strecpy(buf, "", last);
 

	
 
	if (SeedChance(12, 15, seed) < 3) {
 
		strecat(buf, name_hungarian_real[SeedChance(0, lengthof(name_hungarian_real), seed)], last);
 
	} else {
 
		// optional first segment
 
		i = SeedChance(3, lengthof(name_hungarian_1) * 3, seed);
 
		if (i < lengthof(name_hungarian_1))
 
			strecat(buf, name_hungarian_1[i], last);
 

	
 
		// mandatory middle segments
 
		strecat(buf, name_hungarian_2[SeedChance(3, lengthof(name_hungarian_2), seed)], last);
 
		strecat(buf, name_hungarian_3[SeedChance(6, lengthof(name_hungarian_3), seed)], last);
 

	
 
		// optional last segment
 
		i = SeedChance(10, lengthof(name_hungarian_4) * 3, seed);
 
		if (i < lengthof(name_hungarian_4)) {
 
			strecat(buf, name_hungarian_4[i], last);
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
static byte MakeSwissTownName(char *buf, uint32 seed, const char *last)
 
{
 
	strecpy(buf, name_swiss_real[SeedChance(0, lengthof(name_swiss_real), seed)], last);
 
	return 0;
 
}
 

	
 
static byte MakeDanishTownName(char *buf, uint32 seed, const char *last)
 
{
 
	int i;
 

	
 
	// null terminates the string for strcat
 
	strecpy(buf, "", last);
 

	
 
	// optional first segment
 
	i = SeedChanceBias(0, lengthof(name_danish_1), seed, 50);
 
	if (i >= 0)
 
		strecat(buf, name_danish_1[i], last);
 

	
 
	// middle segments removed as this algorithm seems to create much more realistic names
 
	strecat(buf, name_danish_2[SeedChance( 7, lengthof(name_danish_2), seed)], last);
 
	strecat(buf, name_danish_3[SeedChance(16, lengthof(name_danish_3), seed)], last);
 

	
 
	return 0;
 
}
 

	
 
static byte MakeTurkishTownName(char *buf, uint32 seed, const char *last)
 
{
 
	uint i;
 

	
 
	// null terminates the string for strcat
 
	strecpy(buf, "", last);
 

	
 
	if ((i = SeedModChance(0, 5, seed)) == 0) {
 
		strecat(buf, name_turkish_prefix[SeedModChance( 2, lengthof(name_turkish_prefix), seed)], last);
 

	
 
		// middle segment
 
		strecat(buf, name_turkish_middle[SeedModChance( 4, lengthof(name_turkish_middle), seed)], last);
 

	
 
		// optional suffix
 
		if (SeedModChance(0, 7, seed) == 0) {
 
			strecat(buf, name_turkish_suffix[SeedModChance( 10, lengthof(name_turkish_suffix), seed)], last);
 
		}
 
	} else {
 
		if (i == 1 || i == 2) {
 
			strecat(buf, name_turkish_prefix[SeedModChance( 2, lengthof(name_turkish_prefix), seed)], last);
 
			strecat(buf, name_turkish_suffix[SeedModChance( 4, lengthof(name_turkish_suffix), seed)], last);
 
		} else {
 
			strecat(buf, name_turkish_real[SeedModChance( 4, lengthof(name_turkish_real), seed)], last);
 
		}
 
	}
 
	return 0;
 
}
 

	
 
static const char *mascul_femin_italian[] = {
 
	"o",
 
	"a",
 
};
 

	
 
static byte MakeItalianTownName(char *buf, uint32 seed, const char *last)
 
{
 
	strecpy(buf, "", last);
 

	
 
	if (SeedModChance(0, 6, seed) == 0) { // real city names
 
		strecat(buf, name_italian_real[SeedModChance(4, lengthof(name_italian_real), seed)], last);
 
	} else {
 
		uint i;
 

	
 
		if (SeedModChance(0, 8, seed) == 0) { // prefix
 
			strecat(buf, name_italian_pref[SeedModChance(11, lengthof(name_italian_pref), seed)], last);
 
		}
 

	
 
		i = SeedChance(0, 2, seed);
 
		if (i == 0) { // masculine form
 
			strecat(buf, name_italian_1m[SeedModChance(4, lengthof(name_italian_1m), seed)], last);
 
		} else { // feminine form
 
			strecat(buf, name_italian_1f[SeedModChance(4, lengthof(name_italian_1f), seed)], last);
 
		}
 

	
 
		if (SeedModChance(3, 3, seed) == 0) {
 
			strecat(buf, name_italian_2[SeedModChance(11, lengthof(name_italian_2), seed)], last);
 
			strecat(buf,mascul_femin_italian[i], last);
 
		} else {
 
			strecat(buf, name_italian_2i[SeedModChance(16, lengthof(name_italian_2i), seed)], last);
 
		}
 

	
 
		if (SeedModChance(15, 4, seed) == 0) {
 
			if (SeedModChance(5, 2, seed) == 0) { // generic suffix
 
				strecat(buf, name_italian_3[SeedModChance(4, lengthof(name_italian_3), seed)], last);
 
			} else { // river name suffix
 
				strecat(buf, name_italian_river1[SeedModChance(4, lengthof(name_italian_river1), seed)], last);
 
				strecat(buf, name_italian_river2[SeedModChance(16, lengthof(name_italian_river2), seed)], last);
 
			}
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
static byte MakeCatalanTownName(char *buf, uint32 seed, const char *last)
 
{
 
	strecpy(buf, "", last);
 

	
 
	if (SeedModChance(0, 3, seed) == 0) { // real city names
 
		strecat(buf, name_catalan_real[SeedModChance(4, lengthof(name_catalan_real), seed)], last);
 
	} else {
 
		uint i;
 

	
 
		if (SeedModChance(0, 2, seed) == 0) { // prefix
 
			strecat(buf, name_catalan_pref[SeedModChance(11, lengthof(name_catalan_pref), seed)], last);
 
		}
 

	
 
		i = SeedChance(0, 2, seed);
 
		if (i == 0) { // masculine form
 
			strecat(buf, name_catalan_1m[SeedModChance(4, lengthof(name_catalan_1m), seed)], last);
 
			strecat(buf, name_catalan_2m[SeedModChance(11, lengthof(name_catalan_2m), seed)], last);
 
		} else { // feminine form
 
			strecat(buf, name_catalan_1f[SeedModChance(4, lengthof(name_catalan_1f), seed)], last);
 
			strecat(buf, name_catalan_2f[SeedModChance(11, lengthof(name_catalan_2f), seed)], last);
 
		}
 

	
 

	
 
		if (SeedModChance(15, 5, seed) == 0) {
 
			if (SeedModChance(5, 2, seed) == 0) { // generic suffix
 
				strecat(buf, name_catalan_3[SeedModChance(4, lengthof(name_catalan_3), seed)], last);
 
			} else { // river name suffix
 
				strecat(buf, name_catalan_river1[SeedModChance(4, lengthof(name_catalan_river1), seed)], last);
 
			}
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 

	
 
TownNameGenerator * const _town_name_generators[] =
 
{
 
	MakeEnglishOriginalTownName,
 
	MakeFrenchTownName,
 
	MakeGermanTownName,
 
	MakeEnglishAdditionalTownName,
 
	MakeSpanishTownName,
 
	MakeSillyTownName,
 
	MakeSwedishTownName,
 
	MakeDutchTownName,
 
	MakeFinnishTownName,
 
	MakePolishTownName,
 
	MakeSlovakTownName,
 
	MakeNorwegianTownName,
 
	MakeHungarianTownName,
 
	MakeAustrianTownName,
 
	MakeRomanianTownName,
 
	MakeCzechTownName,
 
	MakeSwissTownName,
 
	MakeDanishTownName,
 
	MakeTurkishTownName,
 
	MakeItalianTownName,
 
	MakeCatalanTownName,
 
};
 

	
 
// DO WE NEED THIS ANY MORE?
 
#define FIXNUM(x, y, z) (((((x) << 16) / (y)) + 1) << z)
 

	
 
uint32 GetOldTownName(uint32 townnameparts, byte old_town_name_type)
 
{
 
	switch (old_town_name_type) {
 
		case 0: case 3: /* English, American */
 
			/* Already OK */
 
			return townnameparts;
 

	
 
		case 1: /* French */
 
			/* For some reason 86 needs to be subtracted from townnameparts
 
			 * 0000 0000 0000 0000 0000 0000 1111 1111 */
 
			return FIXNUM(townnameparts - 86, lengthof(name_french_real), 0);
 

	
 
		case 2: /* German */
 
			DEBUG(misc, 0, "German Townnames are buggy (%d)", townnameparts);
 
			return townnameparts;
 

	
 
		case 4: /* Latin-American */
 
			/* 0000 0000 0000 0000 0000 0000 1111 1111 */
 
			return FIXNUM(townnameparts, lengthof(name_spanish_real), 0);
 

	
 
		case 5: /* Silly */
 
			/* NUM_SILLY_1 - lower 16 bits
 
			 * NUM_SILLY_2 - upper 16 bits without leading 1 (first 8 bytes)
 
			 * 1000 0000 2222 2222 0000 0000 1111 1111 */
 
			return FIXNUM(townnameparts, lengthof(name_silly_1), 0) | FIXNUM(GB(townnameparts, 16, 8), lengthof(name_silly_2), 16);
 
	}
 
	return 0;
 
}
src/network/core/core.c
Show inline comments
 
deleted file
src/network/core/core.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../../stdafx.h"
 
#include "../../debug.h"
 
#include "os_abstraction.h"
 

	
 
#ifdef __MORPHOS__
 
/* the library base is required here */
 
struct Library *SocketBase = NULL;
 
#endif
 

	
 
/**
 
 * Initializes the network core (as that is needed for some platforms
 
 */
 
bool NetworkCoreInitialize(void)
 
{
 
#if defined(__MORPHOS__) || defined(__AMIGA__)
 
	/*
 
	 *  IMPORTANT NOTE: SocketBase needs to be initialized before we use _any_
 
	 *  network related function, else: crash.
 
	 */
 
	DEBUG(net, 3, "[core] loading bsd socket library");
 
	SocketBase = OpenLibrary("bsdsocket.library", 4);
 
	if (SocketBase == NULL) {
 
		DEBUG(net, 0, "[core] can't open bsdsocket.library version 4, network unavailable");
 
		return false;
 
	}
 

	
 
#if defined(__AMIGA__)
 
	/* for usleep() implementation (only required for legacy AmigaOS builds) */
 
	TimerPort = CreateMsgPort();
 
	if (TimerPort != NULL) {
 
		TimerRequest = (struct timerequest*)CreateIORequest(TimerPort, sizeof(struct timerequest);
 
		if (TimerRequest != NULL) {
 
			if (OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest*)TimerRequest, 0) == 0) {
 
				TimerBase = TimerRequest->tr_node.io_Device;
 
				if (TimerBase == NULL) {
 
					/* free ressources... */
 
					DEBUG(net, 0, "[core] can't initialize timer, network unavailable");
 
					return false;
 
				}
 
			}
 
		}
 
	}
 
#endif // __AMIGA__
 
#endif // __MORPHOS__ / __AMIGA__
 

	
 
/* Let's load the network in windows */
 
#ifdef WIN32
 
	{
 
		WSADATA wsa;
 
		DEBUG(net, 3, "[core] loading windows socket library");
 
		if (WSAStartup(MAKEWORD(2, 0), &wsa) != 0) {
 
			DEBUG(net, 0, "[core] WSAStartup failed, network unavailable");
 
			return false;
 
		}
 
	}
 
#endif /* WIN32 */
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Shuts down the network core (as that is needed for some platforms
 
 */
 
void NetworkCoreShutdown(void)
 
{
 
#if defined(__MORPHOS__) || defined(__AMIGA__)
 
	/* free allocated resources */
 
#if defined(__AMIGA__)
 
	if (TimerBase    != NULL) CloseDevice((struct IORequest*)TimerRequest); // XXX This smells wrong
 
	if (TimerRequest != NULL) DeleteIORequest(TimerRequest);
 
	if (TimerPort    != NULL) DeleteMsgPort(TimerPort);
 
#endif
 

	
 
	if (SocketBase != NULL) CloseLibrary(SocketBase);
 
#endif
 

	
 
#if defined(WIN32)
 
	WSACleanup();
 
#endif
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/core/packet.c
Show inline comments
 
deleted file
src/network/core/packet.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../../stdafx.h"
 
#include "../../macros.h"
 
#include "../../string.h"
 

	
 
#include "packet.h"
 

	
 
/**
 
 * @file packet.h Basic functions to create, fill and read packets.
 
 */
 

	
 

	
 
/* Do not want to include functions.h and all required headers */
 
extern void NORETURN CDECL error(const char *str, ...);
 

	
 

	
 
/**
 
 * Create a packet for sending
 
 * @param type the of packet
 
 * @return the newly created packet
 
 */
 
Packet *NetworkSend_Init(const PacketType type)
 
{
 
	Packet *packet = malloc(sizeof(Packet));
 
	/* An error is inplace here, because it simply means we ran out of memory. */
 
	if (packet == NULL) error("Failed to allocate Packet");
 

	
 
	/* Skip the size so we can write that in before sending the packet */
 
	packet->size = sizeof(packet->size);
 
	packet->buffer[packet->size++] = type;
 
	packet->pos = 0;
 

	
 
	return packet;
 
}
 

	
 
/**
 
 * Writes the packet size from the raw packet from packet->size
 
 * @param packet the packet to write the size of
 
 */
 
void NetworkSend_FillPacketSize(Packet *packet)
 
{
 
	packet->buffer[0] = GB(packet->size, 0, 8);
 
	packet->buffer[1] = GB(packet->size, 8, 8);
 
}
 

	
 
/**
 
 * The next couple of functions make sure we can send
 
 *  uint8, uint16, uint32 and uint64 endian-safe
 
 *  over the network. The least significant bytes are
 
 *  sent first.
 
 *
 
 *  So 0x01234567 would be sent as 67 45 23 01.
 
 */
 

	
 
void NetworkSend_uint8(Packet *packet, uint8 data)
 
{
 
	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
 
	packet->buffer[packet->size++] = data;
 
}
 

	
 
void NetworkSend_uint16(Packet *packet, uint16 data)
 
{
 
	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
 
	packet->buffer[packet->size++] = GB(data, 0, 8);
 
	packet->buffer[packet->size++] = GB(data, 8, 8);
 
}
 

	
 
void NetworkSend_uint32(Packet *packet, uint32 data)
 
{
 
	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
 
	packet->buffer[packet->size++] = GB(data,  0, 8);
 
	packet->buffer[packet->size++] = GB(data,  8, 8);
 
	packet->buffer[packet->size++] = GB(data, 16, 8);
 
	packet->buffer[packet->size++] = GB(data, 24, 8);
 
}
 

	
 
void NetworkSend_uint64(Packet *packet, uint64 data)
 
{
 
	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
 
	packet->buffer[packet->size++] = GB(data,  0, 8);
 
	packet->buffer[packet->size++] = GB(data,  8, 8);
 
	packet->buffer[packet->size++] = GB(data, 16, 8);
 
	packet->buffer[packet->size++] = GB(data, 24, 8);
 
	packet->buffer[packet->size++] = GB(data, 32, 8);
 
	packet->buffer[packet->size++] = GB(data, 40, 8);
 
	packet->buffer[packet->size++] = GB(data, 48, 8);
 
	packet->buffer[packet->size++] = GB(data, 56, 8);
 
}
 

	
 
/**
 
 *  Sends a string over the network. It sends out
 
 *  the string + '\0'. No size-byte or something.
 
 */
 
void NetworkSend_string(Packet *packet, const char* data)
 
{
 
	assert(data != NULL);
 
	assert(packet->size < sizeof(packet->buffer) - strlen(data) - 1);
 
	while ((packet->buffer[packet->size++] = *data++) != '\0') {}
 
}
 

	
 

	
 
/**
 
 * Receiving commands
 
 * Again, the next couple of functions are endian-safe
 
 *  see the comment before NetworkSend_uint8 for more info.
 
 */
 

	
 

	
 
extern uint CloseConnection(NetworkClientState *cs);
 

	
 
/** Is it safe to read from the packet, i.e. didn't we run over the buffer ? */
 
static inline bool CanReadFromPacket(NetworkClientState *cs, const Packet *packet, const uint bytes_to_read)
 
{
 
	/* Don't allow reading from a closed socket */
 
	if (HasClientQuit(cs)) return false;
 

	
 
	/* Check if variable is within packet-size */
 
	if (packet->pos + bytes_to_read > packet->size) {
 
		CloseConnection(cs);
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Reads the packet size from the raw packet and stores it in the packet->size
 
 * @param packet the packet to read the size of
 
 */
 
void NetworkRecv_ReadPacketSize(Packet *packet)
 
{
 
	packet->size  = (uint16)packet->buffer[0];
 
	packet->size += (uint16)packet->buffer[1] << 8;
 
}
 

	
 
uint8 NetworkRecv_uint8(NetworkClientState *cs, Packet *packet)
 
{
 
	uint8 n;
 

	
 
	if (!CanReadFromPacket(cs, packet, sizeof(n))) return 0;
 

	
 
	n = packet->buffer[packet->pos++];
 
	return n;
 
}
 

	
 
uint16 NetworkRecv_uint16(NetworkClientState *cs, Packet *packet)
 
{
 
	uint16 n;
 

	
 
	if (!CanReadFromPacket(cs, packet, sizeof(n))) return 0;
 

	
 
	n  = (uint16)packet->buffer[packet->pos++];
 
	n += (uint16)packet->buffer[packet->pos++] << 8;
 
	return n;
 
}
 

	
 
uint32 NetworkRecv_uint32(NetworkClientState *cs, Packet *packet)
 
{
 
	uint32 n;
 

	
 
	if (!CanReadFromPacket(cs, packet, sizeof(n))) return 0;
 

	
 
	n  = (uint32)packet->buffer[packet->pos++];
 
	n += (uint32)packet->buffer[packet->pos++] << 8;
 
	n += (uint32)packet->buffer[packet->pos++] << 16;
 
	n += (uint32)packet->buffer[packet->pos++] << 24;
 
	return n;
 
}
 

	
 
uint64 NetworkRecv_uint64(NetworkClientState *cs, Packet *packet)
 
{
 
	uint64 n;
 

	
 
	if (!CanReadFromPacket(cs, packet, sizeof(n))) return 0;
 

	
 
	n  = (uint64)packet->buffer[packet->pos++];
 
	n += (uint64)packet->buffer[packet->pos++] << 8;
 
	n += (uint64)packet->buffer[packet->pos++] << 16;
 
	n += (uint64)packet->buffer[packet->pos++] << 24;
 
	n += (uint64)packet->buffer[packet->pos++] << 32;
 
	n += (uint64)packet->buffer[packet->pos++] << 40;
 
	n += (uint64)packet->buffer[packet->pos++] << 48;
 
	n += (uint64)packet->buffer[packet->pos++] << 56;
 
	return n;
 
}
 

	
 
/** Reads a string till it finds a '\0' in the stream */
 
void NetworkRecv_string(NetworkClientState *cs, Packet *p, char *buffer, size_t size)
 
{
 
	PacketSize pos;
 
	char *bufp = buffer;
 

	
 
	/* Don't allow reading from a closed socket */
 
	if (HasClientQuit(cs)) return;
 

	
 
	pos = p->pos;
 
	while (--size > 0 && pos < p->size && (*buffer++ = p->buffer[pos++]) != '\0') {}
 

	
 
	if (size == 0 || pos == p->size) {
 
		*buffer = '\0';
 
		/* If size was sooner to zero then the string in the stream
 
		 *  skip till the \0, so than packet can be read out correctly for the rest */
 
		while (pos < p->size && p->buffer[pos] != '\0') pos++;
 
		pos++;
 
	}
 
	p->pos = pos;
 

	
 
	str_validate(bufp);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/core/tcp.c
Show inline comments
 
deleted file
src/network/core/tcp.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../../stdafx.h"
 
#include "../../debug.h"
 
#include "../../openttd.h"
 
#include "../../variables.h"
 
#include "table/strings.h"
 
#include "../../functions.h"
 

	
 
#include "../network_data.h"
 
#include "packet.h"
 
#include "tcp.h"
 

	
 
/**
 
 * @file tcp.c Basic functions to receive and send TCP packets.
 
 */
 

	
 
/**
 
 * Functions to help NetworkRecv_Packet/NetworkSend_Packet a bit
 
 *  A socket can make errors. When that happens this handles what to do.
 
 * For clients: close connection and drop back to main-menu
 
 * For servers: close connection and that is it
 
 * @param cs the client to close the connection of
 
 * @return the new status
 
 */
 
NetworkRecvStatus CloseConnection(NetworkClientState *cs)
 
{
 
	NetworkCloseClient(cs);
 

	
 
	/* Clients drop back to the main menu */
 
	if (!_network_server && _networking) {
 
		_switch_mode = SM_MENU;
 
		_networking = false;
 
		_switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION;
 

	
 
		return NETWORK_RECV_STATUS_CONN_LOST;
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Whether the client has quit or not (used in packet.c)
 
 * @param cs the client to check
 
 * @return true if the client has quit
 
 */
 
bool HasClientQuit(const NetworkClientState *cs)
 
{
 
	return cs->has_quit;
 
}
 

	
 
/**
 
 * This function puts the packet in the send-queue and it is send as
 
 * soon as possible. This is the next tick, or maybe one tick later
 
 * if the OS-network-buffer is full)
 
 * @param packet the packet to send
 
 * @param cs     the client to send to
 
 */
 
void NetworkSend_Packet(Packet *packet, NetworkClientState *cs)
 
{
 
	Packet *p;
 
	assert(packet != NULL);
 

	
 
	packet->pos = 0;
 
	packet->next = NULL;
 

	
 
	NetworkSend_FillPacketSize(packet);
 

	
 
	/* Locate last packet buffered for the client */
 
	p = cs->packet_queue;
 
	if (p == NULL) {
 
		/* No packets yet */
 
		cs->packet_queue = packet;
 
	} else {
 
		/* Skip to the last packet */
 
		while (p->next != NULL) p = p->next;
 
		p->next = packet;
 
	}
 
}
 

	
 
/**
 
 * Sends all the buffered packets out for this client. It stops when:
 
 *   1) all packets are send (queue is empty)
 
 *   2) the OS reports back that it can not send any more
 
 *      data right now (full network-buffer, it happens ;))
 
 *   3) sending took too long
 
 * @param cs the client to send the packets for
 
 */
 
bool NetworkSend_Packets(NetworkClientState *cs)
 
{
 
	ssize_t res;
 
	Packet *p;
 

	
 
	/* We can not write to this socket!! */
 
	if (!cs->writable) return false;
 
	if (cs->socket == INVALID_SOCKET) return false;
 

	
 
	p = cs->packet_queue;
 
	while (p != NULL) {
 
		res = send(cs->socket, p->buffer + p->pos, p->size - p->pos, 0);
 
		if (res == -1) {
 
			int err = GET_LAST_ERROR();
 
			if (err != EWOULDBLOCK) {
 
				/* Something went wrong.. close client! */
 
				DEBUG(net, 0, "send failed with error %d", err);
 
				CloseConnection(cs);
 
				return false;
 
			}
 
			return true;
 
		}
 
		if (res == 0) {
 
			/* Client/server has left us :( */
 
			CloseConnection(cs);
 
			return false;
 
		}
 

	
 
		p->pos += res;
 

	
 
		/* Is this packet sent? */
 
		if (p->pos == p->size) {
 
			/* Go to the next packet */
 
			cs->packet_queue = p->next;
 
			free(p);
 
			p = cs->packet_queue;
 
		} else {
 
			return true;
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Receives a packet for the given client
 
 * @param cs     the client to (try to) receive a packet for
 
 * @param status the variable to store the status into
 
 * @return the received packet (or NULL when it didn't receive one)
 
 */
 
Packet *NetworkRecv_Packet(NetworkClientState *cs, NetworkRecvStatus *status)
 
{
 
	ssize_t res;
 
	Packet *p;
 

	
 
	*status = NETWORK_RECV_STATUS_OKAY;
 

	
 
	if (cs->socket == INVALID_SOCKET) return NULL;
 

	
 
	if (cs->packet_recv == NULL) {
 
		cs->packet_recv = malloc(sizeof(Packet));
 
		if (cs->packet_recv == NULL) error("Failed to allocate packet");
 
		/* Set pos to zero! */
 
		cs->packet_recv->pos = 0;
 
		cs->packet_recv->size = 0; // Can be ommited, just for safety reasons
 
	}
 

	
 
	p = cs->packet_recv;
 

	
 
	/* Read packet size */
 
	if (p->pos < sizeof(PacketSize)) {
 
		while (p->pos < sizeof(PacketSize)) {
 
			/* Read the size of the packet */
 
			res = recv(cs->socket, p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0);
 
			if (res == -1) {
 
				int err = GET_LAST_ERROR();
 
				if (err != EWOULDBLOCK) {
 
					/* Something went wrong... (104 is connection reset by peer) */
 
					if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
 
					*status = CloseConnection(cs);
 
					return NULL;
 
				}
 
				/* Connection would block, so stop for now */
 
				return NULL;
 
			}
 
			if (res == 0) {
 
				/* Client/server has left */
 
				*status = CloseConnection(cs);
 
				return NULL;
 
			}
 
			p->pos += res;
 
		}
 

	
 
		NetworkRecv_ReadPacketSize(p);
 

	
 
		if (p->size > SEND_MTU) {
 
			*status = CloseConnection(cs);
 
			return NULL;
 
		}
 
	}
 

	
 
	/* Read rest of packet */
 
	while (p->pos < p->size) {
 
		res = recv(cs->socket, p->buffer + p->pos, p->size - p->pos, 0);
 
		if (res == -1) {
 
			int err = GET_LAST_ERROR();
 
			if (err != EWOULDBLOCK) {
 
				/* Something went wrong... (104 is connection reset by peer) */
 
				if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
 
				*status = CloseConnection(cs);
 
				return NULL;
 
			}
 
			/* Connection would block */
 
			return NULL;
 
		}
 
		if (res == 0) {
 
			/* Client/server has left */
 
			*status = CloseConnection(cs);
 
			return NULL;
 
		}
 

	
 
		p->pos += res;
 
	}
 

	
 
	/* We have a complete packet, return it! */
 
	p->pos = 2;
 
	p->next = NULL; // Should not be needed, but who knows...
 

	
 
	/* Prepare for receiving a new packet */
 
	cs->packet_recv = NULL;
 

	
 
	return p;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/core/udp.c
Show inline comments
 
deleted file
src/network/core/udp.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../../stdafx.h"
 
#include "../../debug.h"
 
#include "../../macros.h"
 
#include "packet.h"
 
#include "udp.h"
 

	
 
/**
 
 * @file udp.c Basic functions to receive and send UDP packets.
 
 */
 

	
 
/**
 
 * Start listening on the given host and port.
 
 * @param udp       the place where the (references to the) UDP are stored
 
 * @param host      the host (ip) to listen on
 
 * @param port      the port to listen on
 
 * @param broadcast whether to allow broadcast sending/receiving
 
 * @return true if the listening succeeded
 
 */
 
bool NetworkUDPListen(SOCKET *udp, const uint32 host, const uint16 port, const bool broadcast)
 
{
 
	struct sockaddr_in sin;
 

	
 
	/* Make sure socket is closed */
 
	NetworkUDPClose(udp);
 

	
 
	*udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 
	if (*udp == INVALID_SOCKET) {
 
		DEBUG(net, 0, "[udp] failed to start UDP listener");
 
		return false;
 
	}
 

	
 
	/* set nonblocking mode for socket */
 
	{
 
		unsigned long blocking = 1;
 
#ifndef BEOS_NET_SERVER
 
		ioctlsocket(*udp, FIONBIO, &blocking);
 
#else
 
		setsockopt(*udp, SOL_SOCKET, SO_NONBLOCK, &blocking, NULL);
 
#endif
 
	}
 

	
 
	sin.sin_family = AF_INET;
 
	/* Listen on all IPs */
 
	sin.sin_addr.s_addr = host;
 
	sin.sin_port = htons(port);
 

	
 
	if (bind(*udp, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
 
		DEBUG(net, 0, "[udp] bind failed on %s:%i", inet_ntoa(*(struct in_addr *)&host), port);
 
		return false;
 
	}
 

	
 
	if (broadcast) {
 
		/* Enable broadcast */
 
		unsigned long val = 1;
 
#ifndef BEOS_NET_SERVER // will work around this, some day; maybe.
 
		setsockopt(*udp, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val));
 
#endif
 
	}
 

	
 
	DEBUG(net, 1, "[udp] listening on port %s:%d", inet_ntoa(*(struct in_addr *)&host), port);
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Close the given UDP socket
 
 * @param udp the socket to close
 
 */
 
void NetworkUDPClose(SOCKET *udp)
 
{
 
	if (*udp == INVALID_SOCKET) return;
 

	
 
	closesocket(*udp);
 
	*udp = INVALID_SOCKET;
 
}
 

	
 

	
 
/**
 
 * Send a packet over UDP
 
 * @param udp  the socket to send over
 
 * @param p    the packet to send
 
 * @param recv the receiver (target) of the packet
 
 */
 
void NetworkSendUDP_Packet(const SOCKET udp, Packet *p, const struct sockaddr_in *recv)
 
{
 
	int res;
 

	
 
	NetworkSend_FillPacketSize(p);
 

	
 
	/* Send the buffer */
 
	res = sendto(udp, p->buffer, p->size, 0, (struct sockaddr *)recv, sizeof(*recv));
 

	
 
	/* Check for any errors, but ignore it otherwise */
 
	if (res == -1) DEBUG(net, 1, "[udp] sendto failed with: %i", GET_LAST_ERROR());
 
}
 

	
 
/**
 
 * Receive a packet at UDP level
 
 * @param udp the socket to receive the packet on
 
 */
 
void NetworkUDPReceive(const SOCKET udp)
 
{
 
	struct sockaddr_in client_addr;
 
	socklen_t client_len;
 
	int nbytes;
 
	Packet p;
 
	int packet_len;
 

	
 
	packet_len = sizeof(p.buffer);
 
	client_len = sizeof(client_addr);
 

	
 
	/* Try to receive anything */
 
	nbytes = recvfrom(udp, p.buffer, packet_len, 0, (struct sockaddr *)&client_addr, &client_len);
 

	
 
	/* We got some bytes for the base header of the packet. */
 
	if (nbytes > 2) {
 
		NetworkRecv_ReadPacketSize(&p);
 

	
 
		/* If the size does not match the packet must be corrupted.
 
		 * Otherwise it will be marked as corrupted later on. */
 
		if (nbytes != p.size) {
 
			DEBUG(net, 1, "received a packet with mismatching size from %s:%d",
 
					inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
 

	
 
			return;
 
		}
 

	
 
		/* Put the position on the right place */
 
		p.pos = 2;
 
		p.next = NULL;
 

	
 
		/* Handle the packet */
 
		NetworkHandleUDPPacket(udp, &p, &client_addr);
 
	}
 
}
 

	
 

	
 
/**
 
 * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet
 
 * @param p the packet to write the data to
 
 * @param c the configuration to write the GRF ID and MD5 checksum from
 
 */
 
void NetworkSend_GRFIdentifier(Packet *p, const GRFConfig *c)
 
{
 
	uint j;
 
	NetworkSend_uint32(p, c->grfid);
 
	for (j = 0; j < sizeof(c->md5sum); j++) {
 
		NetworkSend_uint8 (p, c->md5sum[j]);
 
	}
 
}
 

	
 
/**
 
 * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet
 
 * @param cs the client state (for closing connect on out-of-bounds reading etc)
 
 * @param p  the packet to read the data from
 
 * @param c  the configuration to write the GRF ID and MD5 checksum to
 
 */
 
void NetworkRecv_GRFIdentifier(NetworkClientState *cs, Packet *p, GRFConfig *c)
 
{
 
	uint j;
 
	c->grfid = NetworkRecv_uint32(cs, p);
 
	for (j = 0; j < sizeof(c->md5sum); j++) {
 
		c->md5sum[j] = NetworkRecv_uint8(cs, p);
 
	}
 
}
 

	
 

	
 
/**
 
 * Serializes the NetworkGameInfo struct to the packet
 
 * @param p    the packet to write the data to
 
 * @param info the NetworkGameInfo struct to serialize
 
 */
 
void NetworkSend_NetworkGameInfo(Packet *p, const NetworkGameInfo *info)
 
{
 
	NetworkSend_uint8 (p, NETWORK_GAME_INFO_VERSION);
 

	
 
	/*
 
	 *              Please observe the order.
 
	 * The parts must be read in the same order as they are sent!
 
	 */
 

	
 
	/* Update the documentation in udp.h on changes
 
	 * to the NetworkGameInfo wire-protocol! */
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 4 */
 
	{
 
		/* Only send the GRF Identification (GRF_ID and MD5 checksum) of
 
		 * the GRFs that are needed, i.e. the ones that the server has
 
		 * selected in the NewGRF GUI and not the ones that are used due
 
		 * to the fact that they are in [newgrf-static] in openttd.cfg */
 
		const GRFConfig *c;
 
		uint count = 0;
 

	
 
		/* Count number of GRFs to send information about */
 
		for (c = info->grfconfig; c != NULL; c = c->next) {
 
			if (!HASBIT(c->flags, GCF_STATIC)) count++;
 
		}
 
		NetworkSend_uint8 (p, count); // Send number of GRFs
 

	
 
		/* Send actual GRF Identifications */
 
		for (c = info->grfconfig; c != NULL; c = c->next) {
 
			if (!HASBIT(c->flags, GCF_STATIC)) NetworkSend_GRFIdentifier(p, c);
 
		}
 
	}
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 3 */
 
	NetworkSend_uint32(p, info->game_date);
 
	NetworkSend_uint32(p, info->start_date);
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 2 */
 
	NetworkSend_uint8 (p, info->companies_max);
 
	NetworkSend_uint8 (p, info->companies_on);
 
	NetworkSend_uint8 (p, info->spectators_max);
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 1 */
 
	NetworkSend_string(p, info->server_name);
 
	NetworkSend_string(p, info->server_revision);
 
	NetworkSend_uint8 (p, info->server_lang);
 
	NetworkSend_uint8 (p, info->use_password);
 
	NetworkSend_uint8 (p, info->clients_max);
 
	NetworkSend_uint8 (p, info->clients_on);
 
	NetworkSend_uint8 (p, info->spectators_on);
 
	NetworkSend_string(p, info->map_name);
 
	NetworkSend_uint16(p, info->map_width);
 
	NetworkSend_uint16(p, info->map_height);
 
	NetworkSend_uint8 (p, info->map_set);
 
	NetworkSend_uint8 (p, info->dedicated);
 
}
 

	
 
/**
 
 * Deserializes the NetworkGameInfo struct from the packet
 
 * @param cs   the client state (for closing connect on out-of-bounds reading etc)
 
 * @param p    the packet to read the data from
 
 * @param info the NetworkGameInfo to deserialize into
 
 */
 
void NetworkRecv_NetworkGameInfo(NetworkClientState *cs, Packet *p, NetworkGameInfo *info)
 
{
 
	info->game_info_version = NetworkRecv_uint8(cs, p);
 

	
 
	/*
 
	 *              Please observe the order.
 
	 * The parts must be read in the same order as they are sent!
 
	 */
 

	
 
	/* Update the documentation in udp.h on changes
 
	 * to the NetworkGameInfo wire-protocol! */
 

	
 
	switch (info->game_info_version) {
 
		case 4: {
 
			GRFConfig *c, **dst = &info->grfconfig;
 
			uint i;
 
			uint num_grfs = NetworkRecv_uint8(cs, p);
 

	
 
			for (i = 0; i < num_grfs; i++) {
 
				c = calloc(1, sizeof(*c));
 
				NetworkRecv_GRFIdentifier(cs, p, c);
 
				HandleIncomingNetworkGameInfoGRFConfig(c);
 

	
 
				/* Append GRFConfig to the list */
 
				*dst = c;
 
				dst = &c->next;
 
			}
 
		} /* Fallthrough */
 
		case 3:
 
			info->game_date      = NetworkRecv_uint32(cs, p);
 
			info->start_date     = NetworkRecv_uint32(cs, p);
 
			/* Fallthrough */
 
		case 2:
 
			info->companies_max  = NetworkRecv_uint8 (cs, p);
 
			info->companies_on   = NetworkRecv_uint8 (cs, p);
 
			info->spectators_max = NetworkRecv_uint8 (cs, p);
 
			/* Fallthrough */
 
		case 1:
 
			NetworkRecv_string(cs, p, info->server_name,     sizeof(info->server_name));
 
			NetworkRecv_string(cs, p, info->server_revision, sizeof(info->server_revision));
 
			info->server_lang    = NetworkRecv_uint8 (cs, p);
 
			info->use_password   = NetworkRecv_uint8 (cs, p);
 
			info->clients_max    = NetworkRecv_uint8 (cs, p);
 
			info->clients_on     = NetworkRecv_uint8 (cs, p);
 
			info->spectators_on  = NetworkRecv_uint8 (cs, p);
 
			if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier
 
				info->game_date    = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR;
 
				info->start_date   = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR;
 
			}
 
			NetworkRecv_string(cs, p, info->map_name, sizeof(info->map_name));
 
			info->map_width      = NetworkRecv_uint16(cs, p);
 
			info->map_height     = NetworkRecv_uint16(cs, p);
 
			info->map_set        = NetworkRecv_uint8 (cs, p);
 
			info->dedicated      = NetworkRecv_uint8 (cs, p);
 
	}
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/network.c
Show inline comments
 
deleted file
src/network/network.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 
#include "network_data.h"
 

	
 
#if defined(WITH_REV)
 
	extern const char _openttd_revision[];
 
#elif defined(WITH_REV_HACK)
 
	#define WITH_REV
 
	const char _openttd_revision[] = WITH_REV_HACK;
 
#else
 
	const char _openttd_revision[] = NOREV_STRING;
 
#endif
 

	
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../openttd.h"
 
#include "../debug.h"
 
#include "../functions.h"
 
#include "../string.h"
 
#include "../strings.h"
 
#include "../map.h"
 
#include "../command.h"
 
#include "../variables.h"
 
#include "../date.h"
 
#include "../newgrf_config.h"
 
#include "table/strings.h"
 
#include "network_client.h"
 
#include "network_server.h"
 
#include "network_udp.h"
 
#include "network_gamelist.h"
 
#include "core/udp.h"
 
#include "core/tcp.h"
 
#include "core/core.h"
 
#include "network_gui.h"
 
#include "../console.h" /* IConsoleCmdExec */
 
#include <stdarg.h> /* va_list */
 
#include "../md5.h"
 

	
 
// The listen socket for the server
 
static SOCKET _listensocket;
 

	
 
// The amount of clients connected
 
static byte _network_clients_connected = 0;
 
// The index counter for new clients (is never decreased)
 
static uint16 _network_client_index = NETWORK_SERVER_INDEX + 1;
 

	
 
/* Some externs / forwards */
 
extern void StateGameLoop(void);
 

	
 
// Function that looks up the CI for a given client-index
 
NetworkClientInfo *NetworkFindClientInfoFromIndex(uint16 client_index)
 
{
 
	NetworkClientInfo *ci;
 

	
 
	for (ci = _network_client_info; ci != endof(_network_client_info); ci++) {
 
		if (ci->client_index == client_index) return ci;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/** Return the CI for a given IP
 
 * @param ip IP of the client we are looking for. This must be in string-format
 
 * @return return a pointer to the corresponding NetworkClientInfo struct or NULL on failure */
 
NetworkClientInfo *NetworkFindClientInfoFromIP(const char *ip)
 
{
 
	NetworkClientInfo *ci;
 
	uint32 ip_number = inet_addr(ip);
 

	
 
	for (ci = _network_client_info; ci != endof(_network_client_info); ci++) {
 
		if (ci->client_ip == ip_number) return ci;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
// Function that looks up the CS for a given client-index
 
NetworkClientState *NetworkFindClientStateFromIndex(uint16 client_index)
 
{
 
	NetworkClientState *cs;
 

	
 
	for (cs = _clients; cs != endof(_clients); cs++) {
 
		if (cs->index == client_index) return cs;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
// NetworkGetClientName is a server-safe function to get the name of the client
 
//  if the user did not send it yet, Client #<no> is used.
 
void NetworkGetClientName(char *client_name, size_t size, const NetworkClientState *cs)
 
{
 
	const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
 

	
 
	if (ci->client_name[0] == '\0') {
 
		snprintf(client_name, size, "Client #%4d", cs->index);
 
	} else {
 
		ttd_strlcpy(client_name, ci->client_name, size);
 
	}
 
}
 

	
 
byte NetworkSpectatorCount(void)
 
{
 
	const NetworkClientState *cs;
 
	byte count = 0;
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		if (DEREF_CLIENT_INFO(cs)->client_playas == PLAYER_SPECTATOR) count++;
 
	}
 

	
 
	return count;
 
}
 

	
 
// This puts a text-message to the console, or in the future, the chat-box,
 
//  (to keep it all a bit more general)
 
// If 'self_send' is true, this is the client who is sending the message
 
void CDECL NetworkTextMessage(NetworkAction action, uint16 color, bool self_send, const char *name, const char *str, ...)
 
{
 
	char buf[1024];
 
	va_list va;
 
	const int duration = 10; // Game days the messages stay visible
 
	char message[1024];
 
	char temp[1024];
 

	
 
	va_start(va, str);
 
	vsnprintf(buf, lengthof(buf), str, va);
 
	va_end(va);
 

	
 
	switch (action) {
 
		case NETWORK_ACTION_SERVER_MESSAGE:
 
			color = 1;
 
			snprintf(message, sizeof(message), "*** %s", buf);
 
			break;
 
		case NETWORK_ACTION_JOIN:
 
			color = 1;
 
			GetString(temp, STR_NETWORK_CLIENT_JOINED, lastof(temp));
 
			snprintf(message, sizeof(message), "*** %s %s", name, temp);
 
			break;
 
		case NETWORK_ACTION_LEAVE:
 
			color = 1;
 
			GetString(temp, STR_NETWORK_ERR_LEFT, lastof(temp));
 
			snprintf(message, sizeof(message), "*** %s %s (%s)", name, temp, buf);
 
			break;
 
		case NETWORK_ACTION_GIVE_MONEY:
 
			if (self_send) {
 
				SetDParamStr(0, name);
 
				SetDParam(1, atoi(buf));
 
				GetString(temp, STR_NETWORK_GAVE_MONEY_AWAY, lastof(temp));
 
				snprintf(message, sizeof(message), "*** %s", temp);
 
			} else {
 
				SetDParam(0, atoi(buf));
 
				GetString(temp, STR_NETWORK_GIVE_MONEY, lastof(temp));
 
				snprintf(message, sizeof(message), "*** %s %s", name, temp);
 
			}
 
			break;
 
		case NETWORK_ACTION_NAME_CHANGE:
 
			GetString(temp, STR_NETWORK_NAME_CHANGE, lastof(temp));
 
			snprintf(message, sizeof(message), "*** %s %s %s", name, temp, buf);
 
			break;
 
		case NETWORK_ACTION_CHAT_COMPANY:
 
			SetDParamStr(0, name);
 
			SetDParamStr(1, buf);
 
			GetString(temp, self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY, lastof(temp));
 
			ttd_strlcpy(message, temp, sizeof(message));
 
			break;
 
		case NETWORK_ACTION_CHAT_CLIENT:
 
			SetDParamStr(0, name);
 
			SetDParamStr(1, buf);
 
			GetString(temp, self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT, lastof(temp));
 
			ttd_strlcpy(message, temp, sizeof(message));
 
			break;
 
		default:
 
			SetDParamStr(0, name);
 
			SetDParamStr(1, buf);
 
			GetString(temp, STR_NETWORK_CHAT_ALL, lastof(temp));
 
			ttd_strlcpy(message, temp, sizeof(message));
 
			break;
 
	}
 

	
 
	IConsolePrintF(color, "%s", message);
 
	AddTextMessage(color, duration, "%s", message);
 
}
 

	
 
// Calculate the frame-lag of a client
 
uint NetworkCalculateLag(const NetworkClientState *cs)
 
{
 
	int lag = cs->last_frame_server - cs->last_frame;
 
	// This client has missed his ACK packet after 1 DAY_TICKS..
 
	//  so we increase his lag for every frame that passes!
 
	// The packet can be out by a max of _net_frame_freq
 
	if (cs->last_frame_server + DAY_TICKS + _network_frame_freq < _frame_counter)
 
		lag += _frame_counter - (cs->last_frame_server + DAY_TICKS + _network_frame_freq);
 

	
 
	return lag;
 
}
 

	
 

	
 
// There was a non-recoverable error, drop back to the main menu with a nice
 
//  error
 
static void NetworkError(StringID error_string)
 
{
 
	_switch_mode = SM_MENU;
 
	_switch_mode_errorstr = error_string;
 
}
 

	
 
static void ClientStartError(const char *error)
 
{
 
	DEBUG(net, 0, "[client] could not start network: %s",error);
 
	NetworkError(STR_NETWORK_ERR_CLIENT_START);
 
}
 

	
 
static void ServerStartError(const char *error)
 
{
 
	DEBUG(net, 0, "[server] could not start network: %s",error);
 
	NetworkError(STR_NETWORK_ERR_SERVER_START);
 
}
 

	
 
static void NetworkClientError(NetworkRecvStatus res, NetworkClientState* cs)
 
{
 
	// First, send a CLIENT_ERROR to the server, so he knows we are
 
	//  disconnection (and why!)
 
	NetworkErrorCode errorno;
 

	
 
	// We just want to close the connection..
 
	if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) {
 
		cs->has_quit = true;
 
		NetworkCloseClient(cs);
 
		_networking = false;
 

	
 
		DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
		return;
 
	}
 

	
 
	switch (res) {
 
		case NETWORK_RECV_STATUS_DESYNC:   errorno = NETWORK_ERROR_DESYNC; break;
 
		case NETWORK_RECV_STATUS_SAVEGAME: errorno = NETWORK_ERROR_SAVEGAME_FAILED; break;
 
		default:                           errorno = NETWORK_ERROR_GENERAL; break;
 
	}
 
	// This means we fucked up and the server closed the connection
 
	if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL &&
 
			res != NETWORK_RECV_STATUS_SERVER_BANNED) {
 
		SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno);
 

	
 
		// Dequeue all commands before closing the socket
 
		NetworkSend_Packets(DEREF_CLIENT(0));
 
	}
 

	
 
	_switch_mode = SM_MENU;
 
	NetworkCloseClient(cs);
 
	_networking = false;
 
}
 

	
 
/** Retrieve a string representation of an internal error number
 
 * @param buf buffer where the error message will be stored
 
 * @param err NetworkErrorCode
 
 * @return returns a pointer to the error message (buf) */
 
char* GetNetworkErrorMsg(char* buf, NetworkErrorCode err, const char* last)
 
{
 
	/* List of possible network errors, used by
 
	 * PACKET_SERVER_ERROR and PACKET_CLIENT_ERROR */
 
	static const StringID network_error_strings[] = {
 
		STR_NETWORK_ERR_CLIENT_GENERAL,
 
		STR_NETWORK_ERR_CLIENT_DESYNC,
 
		STR_NETWORK_ERR_CLIENT_SAVEGAME,
 
		STR_NETWORK_ERR_CLIENT_CONNECTION_LOST,
 
		STR_NETWORK_ERR_CLIENT_PROTOCOL_ERROR,
 
		STR_NETWORK_ERR_CLIENT_NOT_AUTHORIZED,
 
		STR_NETWORK_ERR_CLIENT_NOT_EXPECTED,
 
		STR_NETWORK_ERR_CLIENT_WRONG_REVISION,
 
		STR_NETWORK_ERR_CLIENT_NAME_IN_USE,
 
		STR_NETWORK_ERR_CLIENT_WRONG_PASSWORD,
 
		STR_NETWORK_ERR_CLIENT_PLAYER_MISMATCH,
 
		STR_NETWORK_ERR_CLIENT_KICKED,
 
		STR_NETWORK_ERR_CLIENT_CHEATER,
 
		STR_NETWORK_ERR_CLIENT_SERVER_FULL
 
	};
 

	
 
	if (err >= lengthof(network_error_strings)) err = 0;
 

	
 
	return GetString(buf, network_error_strings[err], last);
 
}
 

	
 
/* Count the number of active clients connected */
 
static uint NetworkCountPlayers(void)
 
{
 
	const NetworkClientState *cs;
 
	uint count = 0;
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
 
		if (IsValidPlayer(ci->client_playas)) count++;
 
	}
 

	
 
	return count;
 
}
 

	
 
static bool _min_players_paused = false;
 

	
 
/* Check if the minimum number of players has been reached and pause or unpause the game as appropriate */
 
void CheckMinPlayers(void)
 
{
 
	if (!_network_dedicated) return;
 

	
 
	if (NetworkCountPlayers() < _network_min_players) {
 
		if (_min_players_paused) return;
 

	
 
		_min_players_paused = true;
 
		DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
 
		NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game paused (not enough players)", NETWORK_SERVER_INDEX);
 
	} else {
 
		if (!_min_players_paused) return;
 

	
 
		_min_players_paused = false;
 
		DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
 
		NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused (enough players)", NETWORK_SERVER_INDEX);
 
	}
 
}
 

	
 
// Find all IP-aliases for this host
 
static void NetworkFindIPs(void)
 
{
 
	int i;
 

	
 
#if defined(BEOS_NET_SERVER) /* doesn't have neither getifaddrs or net/if.h */
 
	/* Based on Andrew Bachmann's netstat+.c. Big thanks to him! */
 
	int _netstat(int fd, char **output, int verbose);
 

	
 
	int seek_past_header(char **pos, const char *header) {
 
		char *new_pos = strstr(*pos, header);
 
		if (new_pos == 0) {
 
			return B_ERROR;
 
		}
 
		*pos += strlen(header) + new_pos - *pos + 1;
 
		return B_OK;
 
	}
 

	
 
	int output_length;
 
	char *output_pointer = NULL;
 
	char **output;
 
	int sock = socket(AF_INET, SOCK_DGRAM, 0);
 
	i = 0;
 

	
 
	// If something fails, make sure the list is empty
 
	_broadcast_list[0] = 0;
 

	
 
	if (sock < 0) {
 
		DEBUG(net, 0, "[core] error creating socket");
 
		return;
 
	}
 

	
 
	output_length = _netstat(sock, &output_pointer, 1);
 
	if (output_length < 0) {
 
		DEBUG(net, 0, "[core] error running _netstat");
 
		return;
 
	}
 

	
 
	output = &output_pointer;
 
	if (seek_past_header(output, "IP Interfaces:") == B_OK) {
 
		for (;;) {
 
			uint32 n, fields, read;
 
			uint8 i1, i2, i3, i4, j1, j2, j3, j4;
 
			struct in_addr inaddr;
 
			uint32 ip;
 
			uint32 netmask;
 

	
 
			fields = sscanf(*output, "%u: %hhu.%hhu.%hhu.%hhu, netmask %hhu.%hhu.%hhu.%hhu%n",
 
												&n, &i1,&i2,&i3,&i4, &j1,&j2,&j3,&j4, &read);
 
			read += 1;
 
			if (fields != 9) {
 
				break;
 
			}
 

	
 
			ip      = (uint32)i1 << 24 | (uint32)i2 << 16 | (uint32)i3 << 8 | (uint32)i4;
 
			netmask = (uint32)j1 << 24 | (uint32)j2 << 16 | (uint32)j3 << 8 | (uint32)j4;
 

	
 
			if (ip != INADDR_LOOPBACK && ip != INADDR_ANY) {
 
				inaddr.s_addr = htonl(ip | ~netmask);
 
				_broadcast_list[i] = inaddr.s_addr;
 
				i++;
 
			}
 
			if (read < 0) {
 
				break;
 
			}
 
			*output += read;
 
		}
 
		/* XXX - Using either one of these crashes openttd heavily? - wber */
 
		/*free(output_pointer);*/
 
		/*free(output);*/
 
		closesocket(sock);
 
	}
 
#elif defined(HAVE_GETIFADDRS)
 
	struct ifaddrs *ifap, *ifa;
 

	
 
	// If something fails, make sure the list is empty
 
	_broadcast_list[0] = 0;
 

	
 
	if (getifaddrs(&ifap) != 0)
 
		return;
 

	
 
	i = 0;
 
	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
 
		if (!(ifa->ifa_flags & IFF_BROADCAST)) continue;
 
		if (ifa->ifa_broadaddr == NULL) continue;
 
		if (ifa->ifa_broadaddr->sa_family != AF_INET) continue;
 
		_broadcast_list[i] = ((struct sockaddr_in*)ifa->ifa_broadaddr)->sin_addr.s_addr;
 
		i++;
 
	}
 
	freeifaddrs(ifap);
 

	
 
#else /* not HAVE_GETIFADDRS */
 
	SOCKET sock;
 
#ifdef WIN32
 
	DWORD len = 0;
 
	INTERFACE_INFO ifo[MAX_INTERFACES];
 
	uint j;
 
#else
 
	char buf[4 * 1024]; // Arbitrary buffer size
 
	struct ifconf ifconf;
 
	const char* buf_end;
 
	const char* p;
 
#endif
 

	
 
	// If something fails, make sure the list is empty
 
	_broadcast_list[0] = 0;
 

	
 
	sock = socket(AF_INET, SOCK_DGRAM, 0);
 
	if (sock == INVALID_SOCKET) return;
 

	
 
#ifdef WIN32
 
	memset(&ifo[0], 0, sizeof(ifo));
 
	if ((WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, &ifo[0], sizeof(ifo), &len, NULL, NULL)) != 0) {
 
		closesocket(sock);
 
		return;
 
	}
 

	
 
	i = 0;
 
	for (j = 0; j < len / sizeof(*ifo); j++) {
 
		if (ifo[j].iiFlags & IFF_LOOPBACK) continue;
 
		if (!(ifo[j].iiFlags & IFF_BROADCAST)) continue;
 
		/* iiBroadcast is unusable, because it always seems to be set to
 
		 * 255.255.255.255.
 
		 */
 
		_broadcast_list[i++] =
 
			 ifo[j].iiAddress.AddressIn.sin_addr.s_addr |
 
			~ifo[j].iiNetmask.AddressIn.sin_addr.s_addr;
 
	}
 
#else
 
	ifconf.ifc_len = sizeof(buf);
 
	ifconf.ifc_buf = buf;
 
	if (ioctl(sock, SIOCGIFCONF, &ifconf) == -1) {
 
		closesocket(sock);
 
		return;
 
	}
 

	
 
	i = 0;
 
	buf_end = buf + ifconf.ifc_len;
 
	for (p = buf; p < buf_end;) {
 
		const struct ifreq* req = (const struct ifreq*)p;
 

	
 
		if (req->ifr_addr.sa_family == AF_INET) {
 
			struct ifreq r;
 

	
 
			strncpy(r.ifr_name, req->ifr_name, lengthof(r.ifr_name));
 
			if (ioctl(sock, SIOCGIFFLAGS, &r) != -1 &&
 
					r.ifr_flags & IFF_BROADCAST &&
 
					ioctl(sock, SIOCGIFBRDADDR, &r) != -1) {
 
				_broadcast_list[i++] =
 
					((struct sockaddr_in*)&r.ifr_broadaddr)->sin_addr.s_addr;
 
			}
 
		}
 

	
 
		p += sizeof(struct ifreq);
 
#ifdef AF_LINK
 
		p += req->ifr_addr.sa_len - sizeof(struct sockaddr);
 
#endif
 
	}
 
#endif
 

	
 
	closesocket(sock);
 
#endif /* not HAVE_GETIFADDRS */
 

	
 
	_broadcast_list[i] = 0;
 

	
 
	DEBUG(net, 3, "Detected broadcast addresses:");
 
	// Now display to the debug all the detected ips
 
	for (i = 0; _broadcast_list[i] != 0; i++) {
 
		DEBUG(net, 3, "%d) %s", i, inet_ntoa(*(struct in_addr *)&_broadcast_list[i]));//inet_ntoa(inaddr));
 
	}
 
}
 

	
 
// Resolve a hostname to a inet_addr
 
unsigned long NetworkResolveHost(const char *hostname)
 
{
 
	in_addr_t ip;
 

	
 
	// First try: is it an ip address?
 
	ip = inet_addr(hostname);
 

	
 
	// If not try to resolve the name
 
	if (ip == INADDR_NONE) {
 
		struct hostent *he = gethostbyname(hostname);
 
		if (he == NULL) {
 
			DEBUG(net, 0, "Cannot resolve '%s'", hostname);
 
		} else {
 
			struct in_addr addr = *(struct in_addr *)he->h_addr_list[0];
 
			DEBUG(net, 1, "Resolved '%s' to %s", hostname, inet_ntoa(addr));
 
			ip = addr.s_addr;
 
		}
 
	}
 
	return ip;
 
}
 

	
 
// Converts a string to ip/port/player
 
//  Format: IP#player:port
 
//
 
// connection_string will be re-terminated to seperate out the hostname, and player and port will
 
// be set to the player and port strings given by the user, inside the memory area originally
 
// occupied by connection_string.
 
void ParseConnectionString(const char **player, const char **port, char *connection_string)
 
{
 
	char *p;
 
	for (p = connection_string; *p != '\0'; p++) {
 
		if (*p == '#') {
 
			*p = '\0';
 
			*player = ++p;
 
			while (IsValidChar(*p, CS_NUMERAL)) p++;
 
			if (*p == '\0') break;
 
		} else if (*p == ':') {
 
			*port = p + 1;
 
			*p = '\0';
 
		}
 
	}
 
}
 

	
 
// Creates a new client from a socket
 
//   Used both by the server and the client
 
static NetworkClientState *NetworkAllocClient(SOCKET s)
 
{
 
	NetworkClientState *cs;
 
	byte client_no = 0;
 

	
 
	if (_network_server) {
 
		// Can we handle a new client?
 
		if (_network_clients_connected >= MAX_CLIENTS) return NULL;
 
		if (_network_game_info.clients_on >= _network_game_info.clients_max) return NULL;
 

	
 
		// Register the login
 
		client_no = _network_clients_connected++;
 
	}
 

	
 
	cs = DEREF_CLIENT(client_no);
 
	memset(cs, 0, sizeof(*cs));
 
	cs->socket = s;
 
	cs->last_frame = 0;
 
	cs->has_quit = false;
 

	
 
	cs->last_frame = _frame_counter;
 
	cs->last_frame_server = _frame_counter;
 

	
 
	if (_network_server) {
 
		NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
 
		memset(ci, 0, sizeof(*ci));
 

	
 
		cs->index = _network_client_index++;
 
		ci->client_index = cs->index;
 
		ci->client_playas = PLAYER_INACTIVE_CLIENT;
 
		ci->join_date = _date;
 

	
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 
	}
 

	
 
	return cs;
 
}
 

	
 
// Close a connection
 
void NetworkCloseClient(NetworkClientState *cs)
 
{
 
	NetworkClientInfo *ci;
 
	// Socket is already dead
 
	if (cs->socket == INVALID_SOCKET) {
 
		cs->has_quit = true;
 
		return;
 
	}
 

	
 
	DEBUG(net, 1, "Closed client connection %d", cs->index);
 

	
 
	if (!cs->has_quit && _network_server && cs->status > STATUS_INACTIVE) {
 
		// We did not receive a leave message from this client...
 
		NetworkErrorCode errorno = NETWORK_ERROR_CONNECTION_LOST;
 
		char str[100];
 
		char client_name[NETWORK_CLIENT_NAME_LENGTH];
 
		NetworkClientState *new_cs;
 

	
 
		NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
		GetNetworkErrorMsg(str, errorno, lastof(str));
 

	
 
		NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str);
 

	
 
		// Inform other clients of this... strange leaving ;)
 
		FOR_ALL_CLIENTS(new_cs) {
 
			if (new_cs->status > STATUS_AUTH && cs != new_cs) {
 
				SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->index, errorno);
 
			}
 
		}
 
	}
 

	
 
	/* When the client was PRE_ACTIVE, the server was in pause mode, so unpause */
 
	if (cs->status == STATUS_PRE_ACTIVE && _network_pause_on_join) {
 
		DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
 
		NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused", NETWORK_SERVER_INDEX);
 
	}
 

	
 
	closesocket(cs->socket);
 
	cs->writable = false;
 
	cs->has_quit = true;
 

	
 
	// Free all pending and partially received packets
 
	while (cs->packet_queue != NULL) {
 
		Packet *p = cs->packet_queue->next;
 
		free(cs->packet_queue);
 
		cs->packet_queue = p;
 
	}
 
	free(cs->packet_recv);
 
	cs->packet_recv = NULL;
 

	
 
	while (cs->command_queue != NULL) {
 
		CommandPacket *p = cs->command_queue->next;
 
		free(cs->command_queue);
 
		cs->command_queue = p;
 
	}
 

	
 
	// Close the gap in the client-list
 
	ci = DEREF_CLIENT_INFO(cs);
 

	
 
	if (_network_server) {
 
		// We just lost one client :(
 
		if (cs->status > STATUS_INACTIVE) _network_game_info.clients_on--;
 
		_network_clients_connected--;
 

	
 
		while ((cs + 1) != DEREF_CLIENT(MAX_CLIENTS) && (cs + 1)->socket != INVALID_SOCKET) {
 
			*cs = *(cs + 1);
 
			*ci = *(ci + 1);
 
			cs++;
 
			ci++;
 
		}
 

	
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 
	}
 

	
 
	// Reset the status of the last socket
 
	cs->socket = INVALID_SOCKET;
 
	cs->status = STATUS_INACTIVE;
 
	cs->index = NETWORK_EMPTY_INDEX;
 
	ci->client_index = NETWORK_EMPTY_INDEX;
 

	
 
	CheckMinPlayers();
 
}
 

	
 
// A client wants to connect to a server
 
static bool NetworkConnect(const char *hostname, int port)
 
{
 
	SOCKET s;
 
	struct sockaddr_in sin;
 

	
 
	DEBUG(net, 1, "Connecting to %s %d", hostname, port);
 

	
 
	s = socket(AF_INET, SOCK_STREAM, 0);
 
	if (s == INVALID_SOCKET) {
 
		ClientStartError("socket() failed");
 
		return false;
 
	}
 

	
 
	if (!SetNoDelay(s)) DEBUG(net, 1, "Setting TCP_NODELAY failed");
 

	
 
	sin.sin_family = AF_INET;
 
	sin.sin_addr.s_addr = NetworkResolveHost(hostname);
 
	sin.sin_port = htons(port);
 
	_network_last_host_ip = sin.sin_addr.s_addr;
 

	
 
	/* We failed to connect for which reason what so ever */
 
	if (connect(s, (struct sockaddr*) &sin, sizeof(sin)) != 0) return false;
 

	
 
	if (!SetNonBlocking(s)) DEBUG(net, 0, "Setting non-blocking mode failed"); // XXX should this be an error?
 

	
 
	// in client mode, only the first client field is used. it's pointing to the server.
 
	NetworkAllocClient(s);
 

	
 
	_network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
 
	ShowJoinStatusWindow();
 

	
 
	return true;
 
}
 

	
 
// For the server, to accept new clients
 
static void NetworkAcceptClients(void)
 
{
 
	struct sockaddr_in sin;
 
	NetworkClientState *cs;
 
	uint i;
 
	bool banned;
 

	
 
	// Should never ever happen.. is it possible??
 
	assert(_listensocket != INVALID_SOCKET);
 

	
 
	for (;;) {
 
		socklen_t sin_len = sizeof(sin);
 
		SOCKET s = accept(_listensocket, (struct sockaddr*)&sin, &sin_len);
 
		if (s == INVALID_SOCKET) return;
 

	
 
		SetNonBlocking(s); // XXX error handling?
 

	
 
		DEBUG(net, 1, "Client connected from %s on frame %d", inet_ntoa(sin.sin_addr), _frame_counter);
 

	
 
		SetNoDelay(s); // XXX error handling?
 

	
 
		/* Check if the client is banned */
 
		banned = false;
 
		for (i = 0; i < lengthof(_network_ban_list); i++) {
 
			if (_network_ban_list[i] == NULL) continue;
 

	
 
			if (sin.sin_addr.s_addr == inet_addr(_network_ban_list[i])) {
 
				Packet *p = NetworkSend_Init(PACKET_SERVER_BANNED);
 

	
 
				DEBUG(net, 1, "Banned ip tried to join (%s), refused", _network_ban_list[i]);
 

	
 
				p->buffer[0] = p->size & 0xFF;
 
				p->buffer[1] = p->size >> 8;
 

	
 
				send(s, p->buffer, p->size, 0);
 
				closesocket(s);
 

	
 
				free(p);
 

	
 
				banned = true;
 
				break;
 
			}
 
		}
 
		/* If this client is banned, continue with next client */
 
		if (banned) continue;
 

	
 
		cs = NetworkAllocClient(s);
 
		if (cs == NULL) {
 
			// no more clients allowed?
 
			// Send to the client that we are full!
 
			Packet *p = NetworkSend_Init(PACKET_SERVER_FULL);
 

	
 
			p->buffer[0] = p->size & 0xFF;
 
			p->buffer[1] = p->size >> 8;
 

	
 
			send(s, p->buffer, p->size, 0);
 
			closesocket(s);
 

	
 
			free(p);
 

	
 
			continue;
 
		}
 

	
 
		// a new client has connected. We set him at inactive for now
 
		//  maybe he is only requesting server-info. Till he has sent a PACKET_CLIENT_MAP_OK
 
		//  the client stays inactive
 
		cs->status = STATUS_INACTIVE;
 

	
 
		DEREF_CLIENT_INFO(cs)->client_ip = sin.sin_addr.s_addr; // Save the IP of the client
 
	}
 
}
 

	
 
// Set up the listen socket for the server
 
static bool NetworkListen(void)
 
{
 
	SOCKET ls;
 
	struct sockaddr_in sin;
 

	
 
	DEBUG(net, 1, "Listening on %s:%d", _network_server_bind_ip_host, _network_server_port);
 

	
 
	ls = socket(AF_INET, SOCK_STREAM, 0);
 
	if (ls == INVALID_SOCKET) {
 
		ServerStartError("socket() on listen socket failed");
 
		return false;
 
	}
 

	
 
	{ // reuse the socket
 
		int reuse = 1;
 
		// The (const char*) cast is needed for windows!!
 
		if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) {
 
			ServerStartError("setsockopt() on listen socket failed");
 
			return false;
 
		}
 
	}
 

	
 
	if (!SetNonBlocking(ls)) DEBUG(net, 0, "Setting non-blocking mode failed"); // XXX should this be an error?
 

	
 
	sin.sin_family = AF_INET;
 
	sin.sin_addr.s_addr = _network_server_bind_ip;
 
	sin.sin_port = htons(_network_server_port);
 

	
 
	if (bind(ls, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
 
		ServerStartError("bind() failed");
 
		return false;
 
	}
 

	
 
	if (listen(ls, 1) != 0) {
 
		ServerStartError("listen() failed");
 
		return false;
 
	}
 

	
 
	_listensocket = ls;
 

	
 
	return true;
 
}
 

	
 
// Close all current connections
 
static void NetworkClose(void)
 
{
 
	NetworkClientState *cs;
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		if (!_network_server) {
 
			SEND_COMMAND(PACKET_CLIENT_QUIT)("leaving");
 
			NetworkSend_Packets(cs);
 
		}
 
		NetworkCloseClient(cs);
 
	}
 

	
 
	if (_network_server) {
 
		// We are a server, also close the listensocket
 
		closesocket(_listensocket);
 
		_listensocket = INVALID_SOCKET;
 
		DEBUG(net, 1, "Closed listener");
 
		NetworkUDPStop();
 
	}
 
}
 

	
 
// Inits the network (cleans sockets and stuff)
 
static void NetworkInitialize(void)
 
{
 
	NetworkClientState *cs;
 

	
 
	_local_command_queue = NULL;
 

	
 
	// Clean all client-sockets
 
	memset(_clients, 0, sizeof(_clients));
 
	for (cs = _clients; cs != &_clients[MAX_CLIENTS]; cs++) {
 
		cs->socket = INVALID_SOCKET;
 
		cs->status = STATUS_INACTIVE;
 
		cs->command_queue = NULL;
 
	}
 

	
 
	// Clean the client_info memory
 
	memset(&_network_client_info, 0, sizeof(_network_client_info));
 
	memset(&_network_player_info, 0, sizeof(_network_player_info));
 

	
 
	_sync_frame = 0;
 
	_network_first_time = true;
 

	
 
	_network_reconnect = 0;
 

	
 
	NetworkUDPInitialize();
 
}
 

	
 
// Query a server to fetch his game-info
 
//  If game_info is true, only the gameinfo is fetched,
 
//   else only the client_info is fetched
 
NetworkGameList *NetworkQueryServer(const char* host, unsigned short port, bool game_info)
 
{
 
	if (!_network_available) return NULL;
 

	
 
	NetworkDisconnect();
 

	
 
	if (game_info) return NetworkUDPQueryServer(host, port);
 

	
 
	NetworkInitialize();
 

	
 
	_network_server = false;
 

	
 
	// Try to connect
 
	_networking = NetworkConnect(host, port);
 

	
 
	// We are connected
 
	if (_networking) {
 
		SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)();
 
	} else { // No networking, close everything down again
 
		NetworkDisconnect();
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/* Validates an address entered as a string and adds the server to
 
 * the list. If you use this function, the games will be marked
 
 * as manually added. */
 
void NetworkAddServer(const char *b)
 
{
 
	if (*b != '\0') {
 
		NetworkGameList *item;
 
		const char *port = NULL;
 
		const char *player = NULL;
 
		char host[NETWORK_HOSTNAME_LENGTH];
 
		uint16 rport;
 

	
 
		ttd_strlcpy(host, b, lengthof(host));
 

	
 
		ttd_strlcpy(_network_default_ip, b, lengthof(_network_default_ip));
 
		rport = NETWORK_DEFAULT_PORT;
 

	
 
		ParseConnectionString(&player, &port, host);
 
		if (port != NULL) rport = atoi(port);
 

	
 
		item = NetworkQueryServer(host, rport, true);
 
		item->manually = true;
 
	}
 
}
 

	
 
/* Generates the list of manually added hosts from NetworkGameList and
 
 * dumps them into the array _network_host_list. This array is needed
 
 * by the function that generates the config file. */
 
void NetworkRebuildHostList(void)
 
{
 
	uint i = 0;
 
	const NetworkGameList *item = _network_game_list;
 
	while (item != NULL && i != lengthof(_network_host_list)) {
 
		if (item->manually) {
 
			free(_network_host_list[i]);
 
			_network_host_list[i++] = str_fmt("%s:%i", item->info.hostname, item->port);
 
		}
 
		item = item->next;
 
	}
 

	
 
	for (; i < lengthof(_network_host_list); i++) {
 
		free(_network_host_list[i]);
 
		_network_host_list[i] = NULL;
 
	}
 
}
 

	
 
// Used by clients, to connect to a server
 
bool NetworkClientConnectGame(const char *host, uint16 port)
 
{
 
	if (!_network_available) return false;
 

	
 
	if (port == 0) return false;
 

	
 
	ttd_strlcpy(_network_last_host, host, sizeof(_network_last_host));
 
	_network_last_port = port;
 

	
 
	NetworkDisconnect();
 
	NetworkUDPStop();
 
	NetworkInitialize();
 

	
 
	// Try to connect
 
	_networking = NetworkConnect(host, port);
 

	
 
	// We are connected
 
	if (_networking) {
 
		IConsoleCmdExec("exec scripts/on_client.scr 0");
 
		NetworkClient_Connected();
 
	} else {
 
		// Connecting failed
 
		NetworkError(STR_NETWORK_ERR_NOCONNECTION);
 
	}
 

	
 
	return _networking;
 
}
 

	
 
static void NetworkInitGameInfo(void)
 
{
 
	NetworkClientInfo *ci;
 

	
 
	ttd_strlcpy(_network_game_info.server_name, _network_server_name, sizeof(_network_game_info.server_name));
 
	ttd_strlcpy(_network_game_info.server_password, _network_server_password, sizeof(_network_server_password));
 
	ttd_strlcpy(_network_game_info.rcon_password, _network_rcon_password, sizeof(_network_rcon_password));
 
	if (_network_game_info.server_name[0] == '\0')
 
		snprintf(_network_game_info.server_name, sizeof(_network_game_info.server_name), "Unnamed Server");
 

	
 
	ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision));
 

	
 
	// The server is a client too ;)
 
	if (_network_dedicated) {
 
		_network_game_info.clients_on = 0;
 
		_network_game_info.companies_on = 0;
 
		_network_game_info.dedicated = true;
 
	} else {
 
		_network_game_info.clients_on = 1;
 
		_network_game_info.companies_on = 1;
 
		_network_game_info.dedicated = false;
 
	}
 

	
 
	_network_game_info.spectators_on = 0;
 

	
 
	_network_game_info.game_date = _date;
 
	_network_game_info.start_date = ConvertYMDToDate(_patches.starting_year, 0, 1);
 
	_network_game_info.map_width = MapSizeX();
 
	_network_game_info.map_height = MapSizeY();
 
	_network_game_info.map_set = _opt.landscape;
 

	
 
	_network_game_info.use_password = (_network_server_password[0] != '\0');
 

	
 
	// We use _network_client_info[MAX_CLIENT_INFO - 1] to store the server-data in it
 
	//  The index is NETWORK_SERVER_INDEX ( = 1)
 
	ci = &_network_client_info[MAX_CLIENT_INFO - 1];
 
	memset(ci, 0, sizeof(*ci));
 

	
 
	ci->client_index = NETWORK_SERVER_INDEX;
 
	ci->client_playas = _network_dedicated ? PLAYER_SPECTATOR : _local_player;
 

	
 
	ttd_strlcpy(ci->client_name, _network_player_name, sizeof(ci->client_name));
 
	ttd_strlcpy(ci->unique_id, _network_unique_id, sizeof(ci->unique_id));
 
}
 

	
 
bool NetworkServerStart(void)
 
{
 
	if (!_network_available) return false;
 

	
 
	/* Call the pre-scripts */
 
	IConsoleCmdExec("exec scripts/pre_server.scr 0");
 
	if (_network_dedicated) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0");
 

	
 
	NetworkInitialize();
 
	if (!NetworkListen()) return false;
 

	
 
	// Try to start UDP-server
 
	_network_udp_server = true;
 
	_network_udp_server = NetworkUDPListen(&_udp_server_socket, _network_server_bind_ip, _network_server_port, false);
 

	
 
	_network_server = true;
 
	_networking = true;
 
	_frame_counter = 0;
 
	_frame_counter_server = 0;
 
	_frame_counter_max = 0;
 
	_last_sync_frame = 0;
 
	_network_own_client_index = NETWORK_SERVER_INDEX;
 

	
 
	/* Non-dedicated server will always be player #1 */
 
	if (!_network_dedicated) _network_playas = 0;
 

	
 
	_network_clients_connected = 0;
 

	
 
	NetworkInitGameInfo();
 

	
 
	// execute server initialization script
 
	IConsoleCmdExec("exec scripts/on_server.scr 0");
 
	// if the server is dedicated ... add some other script
 
	if (_network_dedicated) IConsoleCmdExec("exec scripts/on_dedicated.scr 0");
 

	
 
	_min_players_paused = false;
 
	CheckMinPlayers();
 

	
 
	/* Try to register us to the master server */
 
	_network_last_advertise_frame = 0;
 
	_network_need_advertise = true;
 
	NetworkUDPAdvertise();
 
	return true;
 
}
 

	
 
// The server is rebooting...
 
// The only difference with NetworkDisconnect, is the packets that is sent
 
void NetworkReboot(void)
 
{
 
	if (_network_server) {
 
		NetworkClientState *cs;
 
		FOR_ALL_CLIENTS(cs) {
 
			SEND_COMMAND(PACKET_SERVER_NEWGAME)(cs);
 
			NetworkSend_Packets(cs);
 
		}
 
	}
 

	
 
	NetworkClose();
 

	
 
	// Free all queued commands
 
	while (_local_command_queue != NULL) {
 
		CommandPacket *p = _local_command_queue;
 
		_local_command_queue = _local_command_queue->next;
 
		free(p);
 
	}
 

	
 
	_networking = false;
 
	_network_server = false;
 
}
 

	
 
// We want to disconnect from the host/clients
 
void NetworkDisconnect(void)
 
{
 
	if (_network_server) {
 
		NetworkClientState *cs;
 
		FOR_ALL_CLIENTS(cs) {
 
			SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs);
 
			NetworkSend_Packets(cs);
 
		}
 
	}
 

	
 
	if (_network_advertise) NetworkUDPRemoveAdvertise();
 

	
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	NetworkClose();
 

	
 
	// Free all queued commands
 
	while (_local_command_queue != NULL) {
 
		CommandPacket *p = _local_command_queue;
 
		_local_command_queue = _local_command_queue->next;
 
		free(p);
 
	}
 

	
 
	_networking = false;
 
	_network_server = false;
 
}
 

	
 
// Receives something from the network
 
static bool NetworkReceive(void)
 
{
 
	NetworkClientState *cs;
 
	int n;
 
	fd_set read_fd, write_fd;
 
	struct timeval tv;
 

	
 
	FD_ZERO(&read_fd);
 
	FD_ZERO(&write_fd);
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		FD_SET(cs->socket, &read_fd);
 
		FD_SET(cs->socket, &write_fd);
 
	}
 

	
 
	// take care of listener port
 
	if (_network_server) FD_SET(_listensocket, &read_fd);
 

	
 
	tv.tv_sec = tv.tv_usec = 0; // don't block at all.
 
#if !defined(__MORPHOS__) && !defined(__AMIGA__)
 
	n = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
 
#else
 
	n = WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
 
#endif
 
	if (n == -1 && !_network_server) NetworkError(STR_NETWORK_ERR_LOSTCONNECTION);
 

	
 
	// accept clients..
 
	if (_network_server && FD_ISSET(_listensocket, &read_fd)) NetworkAcceptClients();
 

	
 
	// read stuff from clients
 
	FOR_ALL_CLIENTS(cs) {
 
		cs->writable = !!FD_ISSET(cs->socket, &write_fd);
 
		if (FD_ISSET(cs->socket, &read_fd)) {
 
			if (_network_server) {
 
				NetworkServer_ReadPackets(cs);
 
			} else {
 
				NetworkRecvStatus res;
 

	
 
				// The client already was quiting!
 
				if (cs->has_quit) return false;
 

	
 
				res = NetworkClient_ReadPackets(cs);
 
				if (res != NETWORK_RECV_STATUS_OKAY) {
 
					// The client made an error of which we can not recover
 
					//   close the client and drop back to main menu
 
					NetworkClientError(res, cs);
 
					return false;
 
				}
 
			}
 
		}
 
	}
 
	return true;
 
}
 

	
 
// This sends all buffered commands (if possible)
 
static void NetworkSend(void)
 
{
 
	NetworkClientState *cs;
 
	FOR_ALL_CLIENTS(cs) {
 
		if (cs->writable) {
 
			NetworkSend_Packets(cs);
 

	
 
			if (cs->status == STATUS_MAP) {
 
				// This client is in the middle of a map-send, call the function for that
 
				SEND_COMMAND(PACKET_SERVER_MAP)(cs);
 
			}
 
		}
 
	}
 
}
 

	
 
// Handle the local-command-queue
 
static void NetworkHandleLocalQueue(void)
 
{
 
	CommandPacket *cp, **cp_prev;
 

	
 
	cp_prev = &_local_command_queue;
 

	
 
	while ( (cp = *cp_prev) != NULL) {
 

	
 
		// The queue is always in order, which means
 
		// that the first element will be executed first.
 
		if (_frame_counter < cp->frame) break;
 

	
 
		if (_frame_counter > cp->frame) {
 
			// If we reach here, it means for whatever reason, we've already executed
 
			// past the command we need to execute.
 
			DEBUG(net, 0, "Trying to execute a packet in the past!");
 
			assert(0);
 
		}
 

	
 
		// We can execute this command
 
		NetworkExecuteCommand(cp);
 

	
 
		*cp_prev = cp->next;
 
		free(cp);
 
	}
 

	
 
	// Just a safety check, to be removed in the future.
 
	// Make sure that no older command appears towards the end of the queue
 
	// In that case we missed executing it. This will never happen.
 
	for (cp = _local_command_queue; cp; cp = cp->next) {
 
		assert(_frame_counter < cp->frame);
 
	}
 

	
 
}
 

	
 
static bool NetworkDoClientLoop(void)
 
{
 
	_frame_counter++;
 

	
 
	NetworkHandleLocalQueue();
 

	
 
	StateGameLoop();
 

	
 
	// Check if we are in sync!
 
	if (_sync_frame != 0) {
 
		if (_sync_frame == _frame_counter) {
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
			if (_sync_seed_1 != _random_seeds[0][0] || _sync_seed_2 != _random_seeds[0][1]) {
 
#else
 
			if (_sync_seed_1 != _random_seeds[0][0]) {
 
#endif
 
				NetworkError(STR_NETWORK_ERR_DESYNC);
 
				DEBUG(net, 0, "Sync error detected!");
 
				NetworkClientError(NETWORK_RECV_STATUS_DESYNC, DEREF_CLIENT(0));
 
				return false;
 
			}
 

	
 
			// If this is the first time we have a sync-frame, we
 
			//   need to let the server know that we are ready and at the same
 
			//   frame as he is.. so we can start playing!
 
			if (_network_first_time) {
 
				_network_first_time = false;
 
				SEND_COMMAND(PACKET_CLIENT_ACK)();
 
			}
 

	
 
			_sync_frame = 0;
 
		} else if (_sync_frame < _frame_counter) {
 
			DEBUG(net, 1, "Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter);
 
			_sync_frame = 0;
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
// We have to do some UDP checking
 
void NetworkUDPGameLoop(void)
 
{
 
	if (_network_udp_server) {
 
		NetworkUDPReceive(_udp_server_socket);
 
		if (_udp_master_socket != INVALID_SOCKET) {
 
			NetworkUDPReceive(_udp_master_socket);
 
		}
 
	} else if (_udp_client_socket != INVALID_SOCKET) {
 
		NetworkUDPReceive(_udp_client_socket);
 
		if (_network_udp_broadcast > 0) _network_udp_broadcast--;
 
	}
 
}
 

	
 
// The main loop called from ttd.c
 
//  Here we also have to do StateGameLoop if needed!
 
void NetworkGameLoop(void)
 
{
 
	if (!_networking) return;
 

	
 
	if (!NetworkReceive()) return;
 

	
 
	if (_network_server) {
 
		bool send_frame = false;
 

	
 
		// We first increase the _frame_counter
 
		_frame_counter++;
 
		// Update max-frame-counter
 
		if (_frame_counter > _frame_counter_max) {
 
			_frame_counter_max = _frame_counter + _network_frame_freq;
 
			send_frame = true;
 
		}
 

	
 
		NetworkHandleLocalQueue();
 

	
 
		// Then we make the frame
 
		StateGameLoop();
 

	
 
		_sync_seed_1 = _random_seeds[0][0];
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
		_sync_seed_2 = _random_seeds[0][1];
 
#endif
 

	
 
		NetworkServer_Tick(send_frame);
 
	} else {
 
		// Client
 

	
 
		// Make sure we are at the frame were the server is (quick-frames)
 
		if (_frame_counter_server > _frame_counter) {
 
			while (_frame_counter_server > _frame_counter) {
 
				if (!NetworkDoClientLoop()) break;
 
			}
 
		} else {
 
			// Else, keep on going till _frame_counter_max
 
			if (_frame_counter_max > _frame_counter) NetworkDoClientLoop();
 
		}
 
	}
 

	
 
	NetworkSend();
 
}
 

	
 
static void NetworkGenerateUniqueId(void)
 
{
 
	md5_state_t state;
 
	md5_byte_t digest[16];
 
	char hex_output[16*2 + 1];
 
	char coding_string[NETWORK_NAME_LENGTH];
 
	int di;
 

	
 
	snprintf(coding_string, sizeof(coding_string), "%d%s", (uint)Random(), "OpenTTD Unique ID");
 

	
 
	/* Generate the MD5 hash */
 
	md5_init(&state);
 
	md5_append(&state, (const md5_byte_t*)coding_string, strlen(coding_string));
 
	md5_finish(&state, digest);
 

	
 
	for (di = 0; di < 16; ++di)
 
		sprintf(hex_output + di * 2, "%02x", digest[di]);
 

	
 
	/* _network_unique_id is our id */
 
	snprintf(_network_unique_id, sizeof(_network_unique_id), "%s", hex_output);
 
}
 

	
 
/** This tries to launch the network for a given OS */
 
void NetworkStartUp(void)
 
{
 
	DEBUG(net, 3, "[core] starting network...");
 

	
 
	/* Network is available */
 
	_network_available = NetworkCoreInitialize();;
 
	_network_dedicated = false;
 
	_network_last_advertise_frame = 0;
 
	_network_need_advertise = true;
 
	_network_advertise_retries = 0;
 

	
 
	/* Load the ip from the openttd.cfg */
 
	_network_server_bind_ip = inet_addr(_network_server_bind_ip_host);
 
	/* And put the data back in it in case it was an invalid ip */
 
	snprintf(_network_server_bind_ip_host, sizeof(_network_server_bind_ip_host), "%s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip));
 

	
 
	/* Generate an unique id when there is none yet */
 
	if (_network_unique_id[0] == '\0') NetworkGenerateUniqueId();
 

	
 
	{
 
		byte cl_max = _network_game_info.clients_max;
 
		byte cp_max = _network_game_info.companies_max;
 
		byte sp_max = _network_game_info.spectators_max;
 

	
 
		memset(&_network_game_info, 0, sizeof(_network_game_info));
 
		_network_game_info.clients_max = cl_max;
 
		_network_game_info.companies_max = cp_max;
 
		_network_game_info.spectators_max = sp_max;
 
	}
 

	
 

	
 
	NetworkInitialize();
 
	DEBUG(net, 3, "[core] network online, multiplayer available");
 
	NetworkFindIPs();
 
}
 

	
 
/** This shuts the network down */
 
void NetworkShutDown(void)
 
{
 
	NetworkDisconnect();
 
	NetworkUDPStop();
 

	
 
	DEBUG(net, 3, "[core] shutting down network");
 

	
 
	_network_available = false;
 

	
 
	NetworkCoreShutdown();
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/network_client.c
Show inline comments
 
deleted file
src/network/network_client.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "../string.h"
 
#include "../strings.h"
 
#include "network_data.h"
 
#include "core/tcp.h"
 
#include "../date.h"
 
#include "table/strings.h"
 
#include "../functions.h"
 
#include "network_client.h"
 
#include "network_gamelist.h"
 
#include "network_gui.h"
 
#include "../saveload.h"
 
#include "../command.h"
 
#include "../window.h"
 
#include "../console.h"
 
#include "../variables.h"
 
#include "../ai/ai.h"
 

	
 

	
 
// This file handles all the client-commands
 

	
 

	
 
// So we don't make too much typos ;)
 
#define MY_CLIENT DEREF_CLIENT(0)
 

	
 
static uint32 last_ack_frame;
 

	
 
// **********
 
// Sending functions
 
//   DEF_CLIENT_SEND_COMMAND has no parameters
 
// **********
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)
 
{
 
	//
 
	// Packet: CLIENT_COMPANY_INFO
 
	// Function: Request company-info (in detail)
 
	// Data:
 
	//    <none>
 
	//
 
	Packet *p;
 
	_network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO;
 
	InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	p = NetworkSend_Init(PACKET_CLIENT_COMPANY_INFO);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_JOIN)
 
{
 
	//
 
	// Packet: CLIENT_JOIN
 
	// Function: Try to join the server
 
	// Data:
 
	//    String: OpenTTD Revision (norev000 if no revision)
 
	//    String: Player Name (max NETWORK_NAME_LENGTH)
 
	//    uint8:  Play as Player id (1..MAX_PLAYERS)
 
	//    uint8:  Language ID
 
	//    String: Unique id to find the player back in server-listing
 
	//
 

	
 
	extern const char _openttd_revision[];
 
	Packet *p;
 
	_network_join_status = NETWORK_JOIN_STATUS_AUTHORIZING;
 
	InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	p = NetworkSend_Init(PACKET_CLIENT_JOIN);
 
	NetworkSend_string(p, _openttd_revision);
 
	NetworkSend_string(p, _network_player_name); // Player name
 
	NetworkSend_uint8(p, _network_playas); // PlayAs
 
	NetworkSend_uint8(p, NETLANG_ANY); // Language
 
	NetworkSend_string(p, _network_unique_id);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_PASSWORD)(NetworkPasswordType type, const char *password)
 
{
 
	//
 
	// Packet: CLIENT_PASSWORD
 
	// Function: Send a password to the server to authorize
 
	// Data:
 
	//    uint8:  NetworkPasswordType
 
	//    String: Password
 
	//
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_PASSWORD);
 
	NetworkSend_uint8(p, type);
 
	NetworkSend_string(p, password);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_GETMAP)
 
{
 
	//
 
	// Packet: CLIENT_GETMAP
 
	// Function: Request the map from the server
 
	// Data:
 
	//    <none>
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_GETMAP);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_MAP_OK)
 
{
 
	//
 
	// Packet: CLIENT_MAP_OK
 
	// Function: Tell the server that we are done receiving/loading the map
 
	// Data:
 
	//    <none>
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_MAP_OK);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_ACK)
 
{
 
	//
 
	// Packet: CLIENT_ACK
 
	// Function: Tell the server we are done with this frame
 
	// Data:
 
	//    uint32: current FrameCounter of the client
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_ACK);
 

	
 
	NetworkSend_uint32(p, _frame_counter);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
// Send a command packet to the server
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_COMMAND)(CommandPacket *cp)
 
{
 
	//
 
	// Packet: CLIENT_COMMAND
 
	// Function: Send a DoCommand to the Server
 
	// Data:
 
	//    uint8:  PlayerID (0..MAX_PLAYERS-1)
 
	//    uint32: CommandID (see command.h)
 
	//    uint32: P1 (free variables used in DoCommand)
 
	//    uint32: P2
 
	//    uint32: Tile
 
	//    string: text
 
	//    uint8:  CallBackID (see callback_table.c)
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_COMMAND);
 

	
 
	NetworkSend_uint8(p, cp->player);
 
	NetworkSend_uint32(p, cp->cmd);
 
	NetworkSend_uint32(p, cp->p1);
 
	NetworkSend_uint32(p, cp->p2);
 
	NetworkSend_uint32(p, (uint32)cp->tile);
 
	NetworkSend_string(p, cp->text);
 
	NetworkSend_uint8(p, cp->callback);
 

	
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
// Send a chat-packet over the network
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_CHAT)(NetworkAction action, DestType type, int dest, const char *msg)
 
{
 
	//
 
	// Packet: CLIENT_CHAT
 
	// Function: Send a chat-packet to the serve
 
	// Data:
 
	//    uint8:  ActionID (see network_data.h, NetworkAction)
 
	//    uint8:  Destination Type (see network_data.h, DestType);
 
	//    uint8:  Destination Player (1..MAX_PLAYERS)
 
	//    String: Message (max MAX_TEXT_MSG_LEN)
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_CHAT);
 

	
 
	NetworkSend_uint8(p, action);
 
	NetworkSend_uint8(p, type);
 
	NetworkSend_uint8(p, dest);
 
	NetworkSend_string(p, msg);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
// Send an error-packet over the network
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_ERROR)(NetworkErrorCode errorno)
 
{
 
	//
 
	// Packet: CLIENT_ERROR
 
	// Function: The client made an error and is quiting the game
 
	// Data:
 
	//    uint8:  ErrorID (see network_data.h, NetworkErrorCode)
 
	//
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_ERROR);
 

	
 
	NetworkSend_uint8(p, errorno);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_PASSWORD)(const char *password)
 
{
 
	//
 
	// Packet: PACKET_CLIENT_SET_PASSWORD
 
	// Function: Set the password for the clients current company
 
	// Data:
 
	//    String: Password
 
	//
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_SET_PASSWORD);
 

	
 
	NetworkSend_string(p, password);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_NAME)(const char *name)
 
{
 
	//
 
	// Packet: PACKET_CLIENT_SET_NAME
 
	// Function: Gives the player a new name
 
	// Data:
 
	//    String: Name
 
	//
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_SET_NAME);
 

	
 
	NetworkSend_string(p, name);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
// Send an quit-packet over the network
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_QUIT)(const char *leavemsg)
 
{
 
	//
 
	// Packet: CLIENT_QUIT
 
	// Function: The client is quiting the game
 
	// Data:
 
	//    String: leave-message
 
	//
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_QUIT);
 

	
 
	NetworkSend_string(p, leavemsg);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_RCON)(const char *pass, const char *command)
 
{
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_RCON);
 
	NetworkSend_string(p, pass);
 
	NetworkSend_string(p, command);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 

	
 
// **********
 
// Receiving functions
 
//   DEF_CLIENT_RECEIVE_COMMAND has parameter: Packet *p
 
// **********
 

	
 
extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm);
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FULL)
 
{
 
	// We try to join a server which is full
 
	_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_FULL;
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	return NETWORK_RECV_STATUS_SERVER_FULL;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_BANNED)
 
{
 
	// We try to join a server where we are banned
 
	_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_BANNED;
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	return NETWORK_RECV_STATUS_SERVER_BANNED;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMPANY_INFO)
 
{
 
	byte company_info_version;
 
	int i;
 

	
 
	company_info_version = NetworkRecv_uint8(MY_CLIENT, p);
 

	
 
	if (!MY_CLIENT->has_quit && company_info_version == NETWORK_COMPANY_INFO_VERSION) {
 
		byte total;
 
		byte current;
 

	
 
		total = NetworkRecv_uint8(MY_CLIENT, p);
 

	
 
		// There is no data at all..
 
		if (total == 0) return NETWORK_RECV_STATUS_CLOSE_QUERY;
 

	
 
		current = NetworkRecv_uint8(MY_CLIENT, p);
 
		if (!IsValidPlayer(current)) return NETWORK_RECV_STATUS_CLOSE_QUERY;
 

	
 
		NetworkRecv_string(MY_CLIENT, p, _network_player_info[current].company_name, sizeof(_network_player_info[current].company_name));
 
		_network_player_info[current].inaugurated_year = NetworkRecv_uint32(MY_CLIENT, p);
 
		_network_player_info[current].company_value = NetworkRecv_uint64(MY_CLIENT, p);
 
		_network_player_info[current].money = NetworkRecv_uint64(MY_CLIENT, p);
 
		_network_player_info[current].income = NetworkRecv_uint64(MY_CLIENT, p);
 
		_network_player_info[current].performance = NetworkRecv_uint16(MY_CLIENT, p);
 
		_network_player_info[current].use_password = NetworkRecv_uint8(MY_CLIENT, p);
 
		for (i = 0; i < NETWORK_VEHICLE_TYPES; i++)
 
			_network_player_info[current].num_vehicle[i] = NetworkRecv_uint16(MY_CLIENT, p);
 
		for (i = 0; i < NETWORK_STATION_TYPES; i++)
 
			_network_player_info[current].num_station[i] = NetworkRecv_uint16(MY_CLIENT, p);
 

	
 
		NetworkRecv_string(MY_CLIENT, p, _network_player_info[current].players, sizeof(_network_player_info[current].players));
 

	
 
		InvalidateWindow(WC_NETWORK_WINDOW, 0);
 

	
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 

	
 
	return NETWORK_RECV_STATUS_CLOSE_QUERY;
 
}
 

	
 
// This packet contains info about the client (playas and name)
 
//  as client we save this in NetworkClientInfo, linked via 'index'
 
//  which is always an unique number on a server.
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO)
 
{
 
	NetworkClientInfo *ci;
 
	uint16 index = NetworkRecv_uint16(MY_CLIENT, p);
 
	PlayerID playas = NetworkRecv_uint8(MY_CLIENT, p);
 
	char name[NETWORK_NAME_LENGTH];
 
	char unique_id[NETWORK_NAME_LENGTH];
 

	
 
	NetworkRecv_string(MY_CLIENT, p, name, sizeof(name));
 
	NetworkRecv_string(MY_CLIENT, p, unique_id, sizeof(unique_id));
 

	
 
	if (MY_CLIENT->has_quit) return NETWORK_RECV_STATUS_CONN_LOST;
 

	
 
	/* Do we receive a change of data? Most likely we changed playas */
 
	if (index == _network_own_client_index) _network_playas = playas;
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL) {
 
		if (playas == ci->client_playas && strcmp(name, ci->client_name) != 0) {
 
			// Client name changed, display the change
 
			NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, 1, false, ci->client_name, "%s", name);
 
		} else if (playas != ci->client_playas) {
 
			// The player changed from client-player..
 
			// Do not display that for now
 
		}
 

	
 
		ci->client_playas = playas;
 
		ttd_strlcpy(ci->client_name, name, sizeof(ci->client_name));
 

	
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 

	
 
	// We don't have this index yet, find an empty index, and put the data there
 
	ci = NetworkFindClientInfoFromIndex(NETWORK_EMPTY_INDEX);
 
	if (ci != NULL) {
 
		ci->client_index = index;
 
		ci->client_playas = playas;
 

	
 
		ttd_strlcpy(ci->client_name, name, sizeof(ci->client_name));
 
		ttd_strlcpy(ci->unique_id, unique_id, sizeof(ci->unique_id));
 

	
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 

	
 
	// Here the program should never ever come.....
 
	return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR)
 
{
 
	NetworkErrorCode error = NetworkRecv_uint8(MY_CLIENT, p);
 

	
 
	switch (error) {
 
		/* We made an error in the protocol, and our connection is closed.... */
 
		case NETWORK_ERROR_NOT_AUTHORIZED:
 
		case NETWORK_ERROR_NOT_EXPECTED:
 
		case NETWORK_ERROR_PLAYER_MISMATCH:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_ERROR;
 
			break;
 
		case NETWORK_ERROR_FULL:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_FULL;
 
			break;
 
		case NETWORK_ERROR_WRONG_REVISION:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_WRONG_REVISION;
 
			break;
 
		case NETWORK_ERROR_WRONG_PASSWORD:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_WRONG_PASSWORD;
 
			break;
 
		case NETWORK_ERROR_KICKED:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_KICKED;
 
			break;
 
		case NETWORK_ERROR_CHEATER:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_CHEATER;
 
			break;
 
		default:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION;
 
	}
 

	
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	return NETWORK_RECV_STATUS_SERVER_ERROR;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD)
 
{
 
	NetworkPasswordType type = NetworkRecv_uint8(MY_CLIENT, p);
 

	
 
	switch (type) {
 
		case NETWORK_GAME_PASSWORD:
 
		case NETWORK_COMPANY_PASSWORD:
 
			ShowNetworkNeedPassword(type);
 
			return NETWORK_RECV_STATUS_OKAY;
 

	
 
		default: return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
	}
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WELCOME)
 
{
 
	_network_own_client_index = NetworkRecv_uint16(MY_CLIENT, p);
 

	
 
	// Start receiving the map
 
	SEND_COMMAND(PACKET_CLIENT_GETMAP)();
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WAIT)
 
{
 
	_network_join_status = NETWORK_JOIN_STATUS_WAITING;
 
	_network_join_waiting = NetworkRecv_uint8(MY_CLIENT, p);
 
	InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	// We are put on hold for receiving the map.. we need GUI for this ;)
 
	DEBUG(net, 1, "The server is currently busy sending the map to someone else, please wait..." );
 
	DEBUG(net, 1, "There are %d clients in front of you", _network_join_waiting);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP)
 
{
 
	static char filename[256];
 
	static FILE *file_pointer;
 

	
 
	byte maptype;
 

	
 
	maptype = NetworkRecv_uint8(MY_CLIENT, p);
 

	
 
	if (MY_CLIENT->has_quit) return NETWORK_RECV_STATUS_CONN_LOST;
 

	
 
	// First packet, init some stuff
 
	if (maptype == MAP_PACKET_START) {
 
		// The name for the temp-map
 
		snprintf(filename, lengthof(filename), "%s%snetwork_client.tmp",  _paths.autosave_dir, PATHSEP);
 

	
 
		file_pointer = fopen(filename, "wb");
 
		if (file_pointer == NULL) {
 
			_switch_mode_errorstr = STR_NETWORK_ERR_SAVEGAMEERROR;
 
			return NETWORK_RECV_STATUS_SAVEGAME;
 
		}
 

	
 
		_frame_counter = _frame_counter_server = _frame_counter_max = NetworkRecv_uint32(MY_CLIENT, p);
 

	
 
		_network_join_status = NETWORK_JOIN_STATUS_DOWNLOADING;
 
		_network_join_kbytes = 0;
 
		_network_join_kbytes_total = NetworkRecv_uint32(MY_CLIENT, p) / 1024;
 
		InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
		// The first packet does not contain any more data
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 

	
 
	if (maptype == MAP_PACKET_NORMAL) {
 
		// We are still receiving data, put it to the file
 
		fwrite(p->buffer + p->pos, 1, p->size - p->pos, file_pointer);
 

	
 
		_network_join_kbytes = ftell(file_pointer) / 1024;
 
		InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 
	}
 

	
 
	// Check if this was the last packet
 
	if (maptype == MAP_PACKET_END) {
 
		fclose(file_pointer);
 

	
 
		_network_join_status = NETWORK_JOIN_STATUS_PROCESSING;
 
		InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
		/* The map is done downloading, load it */
 
		if (!SafeSaveOrLoad(filename, SL_LOAD, GM_NORMAL)) {
 
			DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
			_switch_mode_errorstr = STR_NETWORK_ERR_SAVEGAMEERROR;
 
			return NETWORK_RECV_STATUS_SAVEGAME;
 
		}
 
		/* If the savegame has successfully loaded, ALL windows have been removed,
 
		 * only toolbar/statusbar and gamefield are visible */
 

	
 
		_opt_ptr = &_opt; // during a network game you are always in-game
 

	
 
		// Say we received the map and loaded it correctly!
 
		SEND_COMMAND(PACKET_CLIENT_MAP_OK)();
 

	
 
		/* New company/spectator (invalid player) or company we want to join is not active
 
		 * Switch local player to spectator and await the server's judgement */
 
		if (_network_playas == PLAYER_NEW_COMPANY || !IsValidPlayer(_network_playas) ||
 
				!GetPlayer(_network_playas)->is_active) {
 

	
 
			SetLocalPlayer(PLAYER_SPECTATOR);
 

	
 
			if (_network_playas != PLAYER_SPECTATOR) {
 
				/* We have arrived and ready to start playing; send a command to make a new player;
 
				 * the server will give us a client-id and let us in */
 
				_network_join_status = NETWORK_JOIN_STATUS_REGISTERING;
 
				ShowJoinStatusWindow();
 
				NetworkSend_Command(0, 0, 0, CMD_PLAYER_CTRL, NULL);
 
			}
 
		} else {
 
			// take control over an existing company
 
			SetLocalPlayer(_network_playas);
 
		}
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FRAME)
 
{
 
	_frame_counter_server = NetworkRecv_uint32(MY_CLIENT, p);
 
	_frame_counter_max = NetworkRecv_uint32(MY_CLIENT, p);
 
#ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
	// Test if the server supports this option
 
	//  and if we are at the frame the server is
 
	if (p->pos < p->size) {
 
		_sync_frame = _frame_counter_server;
 
		_sync_seed_1 = NetworkRecv_uint32(MY_CLIENT, p);
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
		_sync_seed_2 = NetworkRecv_uint32(MY_CLIENT, p);
 
#endif
 
	}
 
#endif
 
	DEBUG(net, 5, "Received FRAME %d", _frame_counter_server);
 

	
 
	// Let the server know that we received this frame correctly
 
	//  We do this only once per day, to save some bandwidth ;)
 
	if (!_network_first_time && last_ack_frame < _frame_counter) {
 
		last_ack_frame = _frame_counter + DAY_TICKS;
 
		DEBUG(net, 4, "Sent ACK at %d", _frame_counter);
 
		SEND_COMMAND(PACKET_CLIENT_ACK)();
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_SYNC)
 
{
 
	_sync_frame = NetworkRecv_uint32(MY_CLIENT, p);
 
	_sync_seed_1 = NetworkRecv_uint32(MY_CLIENT, p);
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
	_sync_seed_2 = NetworkRecv_uint32(MY_CLIENT, p);
 
#endif
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMMAND)
 
{
 
	CommandPacket *cp = malloc(sizeof(CommandPacket));
 
	cp->player = NetworkRecv_uint8(MY_CLIENT, p);
 
	cp->cmd = NetworkRecv_uint32(MY_CLIENT, p);
 
	cp->p1 = NetworkRecv_uint32(MY_CLIENT, p);
 
	cp->p2 = NetworkRecv_uint32(MY_CLIENT, p);
 
	cp->tile = NetworkRecv_uint32(MY_CLIENT, p);
 
	NetworkRecv_string(MY_CLIENT, p, cp->text, sizeof(cp->text));
 
	cp->callback = NetworkRecv_uint8(MY_CLIENT, p);
 
	cp->frame = NetworkRecv_uint32(MY_CLIENT, p);
 
	cp->next = NULL;
 

	
 
	// The server did send us this command..
 
	//  queue it in our own queue, so we can handle it in the upcoming frame!
 

	
 
	if (_local_command_queue == NULL) {
 
		_local_command_queue = cp;
 
	} else {
 
		// Find last packet
 
		CommandPacket *c = _local_command_queue;
 
		while (c->next != NULL) c = c->next;
 
		c->next = cp;
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CHAT)
 
{
 
	char name[NETWORK_NAME_LENGTH], msg[MAX_TEXT_MSG_LEN];
 
	const NetworkClientInfo *ci = NULL, *ci_to;
 

	
 
	NetworkAction action = NetworkRecv_uint8(MY_CLIENT, p);
 
	uint16 index = NetworkRecv_uint16(MY_CLIENT, p);
 
	bool self_send = NetworkRecv_uint8(MY_CLIENT, p);
 
	NetworkRecv_string(MY_CLIENT, p, msg, MAX_TEXT_MSG_LEN);
 

	
 
	ci_to = NetworkFindClientInfoFromIndex(index);
 
	if (ci_to == NULL) return NETWORK_RECV_STATUS_OKAY;
 

	
 
	/* Did we initiate the action locally? */
 
	if (self_send) {
 
		switch (action) {
 
			case NETWORK_ACTION_CHAT_CLIENT:
 
				/* For speaking to client we need the client-name */
 
				snprintf(name, sizeof(name), "%s", ci_to->client_name);
 
				ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
 
				break;
 

	
 
			/* For speaking to company or giving money, we need the player-name */
 
			case NETWORK_ACTION_GIVE_MONEY:
 
				if (!IsValidPlayer(ci_to->client_playas)) return NETWORK_RECV_STATUS_OKAY;
 
				/* fallthrough */
 
			case NETWORK_ACTION_CHAT_COMPANY: {
 
				StringID str = IsValidPlayer(ci_to->client_playas) ? GetPlayer(ci_to->client_playas)->name_1 : STR_NETWORK_SPECTATORS;
 

	
 
				GetString(name, str, lastof(name));
 
				ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
 
			} break;
 

	
 
			default: NOT_REACHED(); break;
 
		}
 
	} else {
 
		/* Display message from somebody else */
 
		snprintf(name, sizeof(name), "%s", ci_to->client_name);
 
		ci = ci_to;
 
	}
 

	
 
	if (ci != NULL)
 
		NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas), self_send, name, "%s", msg);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT)
 
{
 
	char str[100];
 
	uint16 index;
 
	NetworkClientInfo *ci;
 

	
 
	index = NetworkRecv_uint16(MY_CLIENT, p);
 
	GetNetworkErrorMsg(str, NetworkRecv_uint8(MY_CLIENT, p), lastof(str));
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL) {
 
		NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, ci->client_name, "%s", str);
 

	
 
		// The client is gone, give the NetworkClientInfo free
 
		ci->client_index = NETWORK_EMPTY_INDEX;
 
	}
 

	
 
	InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_QUIT)
 
{
 
	char str[100];
 
	uint16 index;
 
	NetworkClientInfo *ci;
 

	
 
	index = NetworkRecv_uint16(MY_CLIENT, p);
 
	NetworkRecv_string(MY_CLIENT, p, str, lengthof(str));
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL) {
 
		NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, ci->client_name, "%s", str);
 

	
 
		// The client is gone, give the NetworkClientInfo free
 
		ci->client_index = NETWORK_EMPTY_INDEX;
 
	} else {
 
		DEBUG(net, 0, "Unknown client (%d) is leaving the game", index);
 
	}
 

	
 
	InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
	// If we come here it means we could not locate the client.. strange :s
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_JOIN)
 
{
 
	uint16 index;
 
	NetworkClientInfo *ci;
 

	
 
	index = NetworkRecv_uint16(MY_CLIENT, p);
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL)
 
		NetworkTextMessage(NETWORK_ACTION_JOIN, 1, false, ci->client_name, "");
 

	
 
	InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_SHUTDOWN)
 
{
 
	_switch_mode_errorstr = STR_NETWORK_SERVER_SHUTDOWN;
 

	
 
	return NETWORK_RECV_STATUS_SERVER_ERROR;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEWGAME)
 
{
 
	// To trottle the reconnects a bit, every clients waits
 
	//  his _local_player value before reconnecting
 
	// PLAYER_SPECTATOR is currently 255, so to avoid long wait periods
 
	//  set the max to 10.
 
	_network_reconnect = min(_local_player + 1, 10);
 
	_switch_mode_errorstr = STR_NETWORK_SERVER_REBOOT;
 

	
 
	return NETWORK_RECV_STATUS_SERVER_ERROR;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_RCON)
 
{
 
	char rcon_out[NETWORK_RCONCOMMAND_LENGTH];
 
	uint16 color_code;
 

	
 
	color_code = NetworkRecv_uint16(MY_CLIENT, p);
 
	NetworkRecv_string(MY_CLIENT, p, rcon_out, sizeof(rcon_out));
 

	
 
	IConsolePrint(color_code, rcon_out);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 

	
 

	
 
// The layout for the receive-functions by the client
 
typedef NetworkRecvStatus NetworkClientPacket(Packet *p);
 

	
 
// This array matches PacketType. At an incoming
 
//  packet it is matches against this array
 
//  and that way the right function to handle that
 
//  packet is found.
 
static NetworkClientPacket* const _network_client_packet[] = {
 
	RECEIVE_COMMAND(PACKET_SERVER_FULL),
 
	RECEIVE_COMMAND(PACKET_SERVER_BANNED),
 
	NULL, /*PACKET_CLIENT_JOIN,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_ERROR),
 
	NULL, /*PACKET_CLIENT_COMPANY_INFO,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_COMPANY_INFO),
 
	RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO),
 
	RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD),
 
	NULL, /*PACKET_CLIENT_PASSWORD,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_WELCOME),
 
	NULL, /*PACKET_CLIENT_GETMAP,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_WAIT),
 
	RECEIVE_COMMAND(PACKET_SERVER_MAP),
 
	NULL, /*PACKET_CLIENT_MAP_OK,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_JOIN),
 
	RECEIVE_COMMAND(PACKET_SERVER_FRAME),
 
	RECEIVE_COMMAND(PACKET_SERVER_SYNC),
 
	NULL, /*PACKET_CLIENT_ACK,*/
 
	NULL, /*PACKET_CLIENT_COMMAND,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_COMMAND),
 
	NULL, /*PACKET_CLIENT_CHAT,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_CHAT),
 
	NULL, /*PACKET_CLIENT_SET_PASSWORD,*/
 
	NULL, /*PACKET_CLIENT_SET_NAME,*/
 
	NULL, /*PACKET_CLIENT_QUIT,*/
 
	NULL, /*PACKET_CLIENT_ERROR,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_QUIT),
 
	RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT),
 
	RECEIVE_COMMAND(PACKET_SERVER_SHUTDOWN),
 
	RECEIVE_COMMAND(PACKET_SERVER_NEWGAME),
 
	RECEIVE_COMMAND(PACKET_SERVER_RCON),
 
	NULL, /*PACKET_CLIENT_RCON,*/
 
};
 

	
 
// If this fails, check the array above with network_data.h
 
assert_compile(lengthof(_network_client_packet) == PACKET_END);
 

	
 
// Is called after a client is connected to the server
 
void NetworkClient_Connected(void)
 
{
 
	// Set the frame-counter to 0 so nothing happens till we are ready
 
	_frame_counter = 0;
 
	_frame_counter_server = 0;
 
	last_ack_frame = 0;
 
	// Request the game-info
 
	SEND_COMMAND(PACKET_CLIENT_JOIN)();
 
}
 

	
 
// Reads the packets from the socket-stream, if available
 
NetworkRecvStatus NetworkClient_ReadPackets(NetworkClientState *cs)
 
{
 
	Packet *p;
 
	NetworkRecvStatus res = NETWORK_RECV_STATUS_OKAY;
 

	
 
	while (res == NETWORK_RECV_STATUS_OKAY && (p = NetworkRecv_Packet(cs, &res)) != NULL) {
 
		byte type = NetworkRecv_uint8(MY_CLIENT, p);
 
		if (type < PACKET_END && _network_client_packet[type] != NULL && !MY_CLIENT->has_quit) {
 
			res = _network_client_packet[type](p);
 
		} else {
 
			res = NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
			DEBUG(net, 0, "[client] received invalid packet type %d", type);
 
		}
 

	
 
		free(p);
 
	}
 

	
 
	return res;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/network_data.c
Show inline comments
 
deleted file
src/network/network_data.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "network_data.h"
 
#include "../string.h"
 
#include "network_client.h"
 
#include "../command.h"
 
#include "../callback_table.h"
 

	
 
// Add a command to the local command queue
 
void NetworkAddCommandQueue(NetworkClientState *cs, CommandPacket *cp)
 
{
 
	CommandPacket* new_cp = malloc(sizeof(*new_cp));
 

	
 
	*new_cp = *cp;
 

	
 
	if (cs->command_queue == NULL) {
 
		cs->command_queue = new_cp;
 
	} else {
 
		CommandPacket *c = cs->command_queue;
 
		while (c->next != NULL) c = c->next;
 
		c->next = new_cp;
 
	}
 
}
 

	
 
// Prepare a DoCommand to be send over the network
 
void NetworkSend_Command(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback)
 
{
 
	CommandPacket *c = malloc(sizeof(CommandPacket));
 
	byte temp_callback;
 

	
 
	c->player = _local_player;
 
	c->next = NULL;
 
	c->tile = tile;
 
	c->p1 = p1;
 
	c->p2 = p2;
 
	c->cmd = cmd;
 
	c->callback = 0;
 

	
 
	temp_callback = 0;
 

	
 
	while (temp_callback < _callback_table_count && _callback_table[temp_callback] != callback)
 
		temp_callback++;
 
	if (temp_callback == _callback_table_count) {
 
		DEBUG(net, 0, "Unknown callback. (Pointer: %p) No callback sent", callback);
 
		temp_callback = 0; /* _callback_table[0] == NULL */
 
	}
 

	
 
	if (_network_server) {
 
		// We are the server, so set the command to be executed next possible frame
 
		c->frame = _frame_counter_max + 1;
 
	} else {
 
		c->frame = 0; // The client can't tell which frame, so just make it 0
 
	}
 

	
 
	ttd_strlcpy(c->text, (_cmd_text != NULL) ? _cmd_text : "", lengthof(c->text));
 

	
 
	if (_network_server) {
 
		// If we are the server, we queue the command in our 'special' queue.
 
		//   In theory, we could execute the command right away, but then the
 
		//   client on the server can do everything 1 tick faster than others.
 
		//   So to keep the game fair, we delay the command with 1 tick
 
		//   which gives about the same speed as most clients.
 
		NetworkClientState *cs;
 

	
 
		// And we queue it for delivery to the clients
 
		FOR_ALL_CLIENTS(cs) {
 
			if (cs->status > STATUS_AUTH) NetworkAddCommandQueue(cs, c);
 
		}
 

	
 
		// Only the server gets the callback, because clients should not get them
 
		c->callback = temp_callback;
 
		if (_local_command_queue == NULL) {
 
			_local_command_queue = c;
 
		} else {
 
			// Find last packet
 
			CommandPacket *cp = _local_command_queue;
 
			while (cp->next != NULL) cp = cp->next;
 
			cp->next = c;
 
		}
 

	
 
		return;
 
	}
 

	
 
	// Clients send their command to the server and forget all about the packet
 
	c->callback = temp_callback;
 
	SEND_COMMAND(PACKET_CLIENT_COMMAND)(c);
 
}
 

	
 
// Execute a DoCommand we received from the network
 
void NetworkExecuteCommand(CommandPacket *cp)
 
{
 
	_current_player = cp->player;
 
	_cmd_text = cp->text;
 
	/* cp->callback is unsigned. so we don't need to do lower bounds checking. */
 
	if (cp->callback > _callback_table_count) {
 
		DEBUG(net, 0, "Received out-of-bounds callback (%d)", cp->callback);
 
		cp->callback = 0;
 
	}
 
	DoCommandP(cp->tile, cp->p1, cp->p2, _callback_table[cp->callback], cp->cmd | CMD_NETWORK_COMMAND);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/network_gamelist.c
Show inline comments
 
deleted file
src/network/network_gamelist.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "network_data.h"
 
#include "../newgrf_config.h"
 

	
 
// This file handles the GameList
 
// Also, it handles the request to a server for data about the server
 

	
 
/** Add a new item to the linked gamelist. If the IP and Port match
 
 * return the existing item instead of adding it again
 
 * @param ip the IP-address (inet_addr) of the to-be added item
 
 * @param port the port the server is running on
 
 * @return a point to the newly added or already existing item */
 
NetworkGameList *NetworkGameListAddItem(uint32 ip, uint16 port)
 
{
 
	NetworkGameList *item, *prev_item;
 

	
 
	prev_item = NULL;
 
	for (item = _network_game_list; item != NULL; item = item->next) {
 
		if (item->ip == ip && item->port == port) return item;
 
		prev_item = item;
 
	}
 

	
 
	item = malloc(sizeof(*item));
 
	memset(item, 0, sizeof(*item));
 
	item->next = NULL;
 
	item->ip = ip;
 
	item->port = port;
 

	
 
	if (prev_item == NULL) {
 
		_network_game_list = item;
 
	} else {
 
		prev_item->next = item;
 
	}
 
	DEBUG(net, 4, "[gamelist] added server to list");
 

	
 
	UpdateNetworkGameWindow(false);
 

	
 
	return item;
 
}
 

	
 
/** Remove an item from the gamelist linked list
 
 * @param remove pointer to the item to be removed */
 
void NetworkGameListRemoveItem(NetworkGameList *remove)
 
{
 
	NetworkGameList *item, *prev_item;
 

	
 
	prev_item = NULL;
 
	for (item = _network_game_list; item != NULL; item = item->next) {
 
		if (remove == item) {
 
			if (prev_item == NULL) {
 
				_network_game_list = remove->next;
 
			} else {
 
				prev_item->next = remove->next;
 
			}
 

	
 
			/* Remove GRFConfig information */
 
			ClearGRFConfigList(&remove->info.grfconfig);
 
			free(remove);
 
			remove = NULL;
 

	
 
			DEBUG(net, 4, "[gamelist] removed server from list");
 
			UpdateNetworkGameWindow(false);
 
			return;
 
		}
 
		prev_item = item;
 
	}
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/network_gui.c
Show inline comments
 
deleted file
src/network/network_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "../string.h"
 
#include "../strings.h"
 
#include "../table/sprites.h"
 
#include "network.h"
 
#include "../date.h"
 

	
 
#include "../fios.h"
 
#include "table/strings.h"
 
#include "../functions.h"
 
#include "network_data.h"
 
#include "network_client.h"
 
#include "network_gui.h"
 
#include "network_gamelist.h"
 
#include "../window.h"
 
#include "../gui.h"
 
#include "../gfx.h"
 
#include "../command.h"
 
#include "../variables.h"
 
#include "network_server.h"
 
#include "network_udp.h"
 
#include "../settings.h"
 
#include "../string.h"
 
#include "../town.h"
 
#include "../newgrf.h"
 

	
 
#define BGC 5
 
#define BTC 15
 

	
 
typedef struct network_d {
 
	PlayerID company;        // select company in network lobby
 
	byte field;              // select text-field in start-server and game-listing
 
	NetworkGameList *server; // selected server in lobby and game-listing
 
	FiosItem *map;           // selected map in start-server
 
} network_d;
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_d));
 

	
 
typedef struct network_ql_d {
 
	network_d n;                 // see above; general stuff
 
	querystr_d q;                // text-input in start-server and game-listing
 
	NetworkGameList **sort_list; // list of games (sorted)
 
	list_d l;                    // accompanying list-administration
 
} network_ql_d;
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_ql_d));
 

	
 
/* Global to remember sorting after window has been closed */
 
static Listing _ng_sorting;
 

	
 
static char _edit_str_buf[150];
 
static bool _chat_tab_completion_active;
 

	
 
static void ShowNetworkStartServerWindow(void);
 
static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
 
extern void SwitchMode(int new_mode);
 

	
 
static const StringID _connection_types_dropdown[] = {
 
	STR_NETWORK_LAN_INTERNET,
 
	STR_NETWORK_INTERNET_ADVERTISE,
 
	INVALID_STRING_ID
 
};
 

	
 
static const StringID _lan_internet_types_dropdown[] = {
 
	STR_NETWORK_LAN,
 
	STR_NETWORK_INTERNET,
 
	INVALID_STRING_ID
 
};
 

	
 
static const StringID _players_dropdown[] = {
 
	STR_NETWORK_0_PLAYERS,
 
	STR_NETWORK_1_PLAYERS,
 
	STR_NETWORK_2_PLAYERS,
 
	STR_NETWORK_3_PLAYERS,
 
	STR_NETWORK_4_PLAYERS,
 
	STR_NETWORK_5_PLAYERS,
 
	STR_NETWORK_6_PLAYERS,
 
	STR_NETWORK_7_PLAYERS,
 
	STR_NETWORK_8_PLAYERS,
 
	STR_NETWORK_9_PLAYERS,
 
	STR_NETWORK_10_PLAYERS,
 
	INVALID_STRING_ID
 
};
 

	
 
static const StringID _language_dropdown[] = {
 
	STR_NETWORK_LANG_ANY,
 
	STR_NETWORK_LANG_ENGLISH,
 
	STR_NETWORK_LANG_GERMAN,
 
	STR_NETWORK_LANG_FRENCH,
 
	INVALID_STRING_ID
 
};
 

	
 
enum {
 
	NET_PRC__OFFSET_TOP_WIDGET          = 54,
 
	NET_PRC__OFFSET_TOP_WIDGET_COMPANY  = 52,
 
	NET_PRC__SIZE_OF_ROW                = 14,
 
};
 

	
 
/** Update the network new window because a new server is
 
 * found on the network.
 
 * @param unselect unselect the currently selected item */
 
void UpdateNetworkGameWindow(bool unselect)
 
{
 
	SendWindowMessage(WC_NETWORK_WINDOW, 0, unselect, 0, 0);
 
}
 

	
 
static bool _internal_sort_order; // Used for Qsort order-flipping
 
typedef int CDECL NGameNameSortFunction(const void*, const void*);
 

	
 
/** Qsort function to sort by name. */
 
static int CDECL NGameNameSorter(const void *a, const void *b)
 
{
 
	const NetworkGameList *cmp1 = *(const NetworkGameList**)a;
 
	const NetworkGameList *cmp2 = *(const NetworkGameList**)b;
 
	int r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
/** Qsort function to sort by the amount of clients online on a
 
 * server. If the two servers have the same amount, the one with the
 
 * higher maximum is preferred. */
 
static int CDECL NGameClientSorter(const void *a, const void *b)
 
{
 
	const NetworkGameList *cmp1 = *(const NetworkGameList**)a;
 
	const NetworkGameList *cmp2 = *(const NetworkGameList**)b;
 
	/* Reverse as per default we are interested in most-clients first */
 
	int r = cmp1->info.clients_on - cmp2->info.clients_on;
 

	
 
	if (r == 0) r = cmp1->info.clients_max - cmp2->info.clients_max;
 
	if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
/** Qsort function to sort by joinability. If both servers are the
 
 * same, prefer the non-passworded server first. */
 
static int CDECL NGameAllowedSorter(const void *a, const void *b)
 
{
 
	const NetworkGameList *cmp1 = *(const NetworkGameList**)a;
 
	const NetworkGameList *cmp2 = *(const NetworkGameList**)b;
 
	/* Reverse default as we are interested in compatible clients first */
 
	int r = cmp2->info.compatible - cmp1->info.compatible;
 

	
 
	if (r == 0) r = cmp1->info.use_password - cmp2->info.use_password;
 
	if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
/** (Re)build the network game list as its amount has changed because
 
 * an item has been added or deleted for example
 
 * @param ngl list_d struct that contains all necessary information for sorting */
 
static void BuildNetworkGameList(network_ql_d *nqld)
 
{
 
	NetworkGameList *ngl_temp;
 
	uint n = 0;
 

	
 
	if (!(nqld->l.flags & VL_REBUILD)) return;
 

	
 
	/* Count the number of games in the list */
 
	for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++;
 
	if (n == 0) return;
 

	
 
	/* Create temporary array of games to use for listing */
 
	free(nqld->sort_list);
 
	nqld->sort_list = malloc(n * sizeof(nqld->sort_list[0]));
 
	if (nqld->sort_list == NULL) error("Could not allocate memory for the network-game-sorting-list");
 
	nqld->l.list_length = n;
 

	
 
	for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) {
 
		nqld->sort_list[n++] = ngl_temp;
 
	}
 

	
 
	/* Force resort */
 
	nqld->l.flags &= ~VL_REBUILD;
 
	nqld->l.flags |= VL_RESORT;
 
}
 

	
 
static void SortNetworkGameList(network_ql_d *nqld)
 
{
 
	static NGameNameSortFunction * const ngame_sorter[] = {
 
		&NGameNameSorter,
 
		&NGameClientSorter,
 
		&NGameAllowedSorter
 
	};
 

	
 
	NetworkGameList *item;
 
	uint i;
 

	
 
	if (!(nqld->l.flags & VL_RESORT)) return;
 
	if (nqld->l.list_length == 0) return;
 

	
 
	_internal_sort_order = !!(nqld->l.flags & VL_DESC);
 
	qsort(nqld->sort_list, nqld->l.list_length, sizeof(nqld->sort_list[0]), ngame_sorter[nqld->l.sort_type]);
 

	
 
	/* After sorting ngl->sort_list contains the sorted items. Put these back
 
	 * into the original list. Basically nothing has changed, we are only
 
	 * shuffling the ->next pointers */
 
	_network_game_list = nqld->sort_list[0];
 
	for (item = _network_game_list, i = 1; i != nqld->l.list_length; i++) {
 
		item->next = nqld->sort_list[i];
 
		item = item->next;
 
	}
 
	item->next = NULL;
 

	
 
	nqld->l.flags &= ~VL_RESORT;
 
}
 

	
 
/* Uses network_ql_d (network_d, querystr_d and list_d) WP macro */
 
static void NetworkGameWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	network_d *nd = &WP(w, network_ql_d).n;
 
	list_d *ld = &WP(w, network_ql_d).l;
 

	
 
	switch (e->event) {
 
	case WE_CREATE: /* Focus input box */
 
		nd->field = 3;
 
		nd->server = NULL;
 

	
 
		WP(w, network_ql_d).sort_list = NULL;
 
		ld->flags = VL_REBUILD | (_ng_sorting.order << (VL_DESC - 1));
 
		ld->sort_type = _ng_sorting.criteria;
 
		break;
 

	
 
	case WE_PAINT: {
 
		const NetworkGameList *sel = nd->server;
 
		const char *arrow = (ld->flags & VL_DESC) ? DOWNARROW : UPARROW;
 

	
 
		if (ld->flags & VL_REBUILD) {
 
			BuildNetworkGameList(&WP(w, network_ql_d));
 
			SetVScrollCount(w, ld->list_length);
 
		}
 
		if (ld->flags & VL_RESORT) SortNetworkGameList(&WP(w, network_ql_d));
 

	
 
		SetWindowWidgetDisabledState(w, 17, sel == NULL);
 
		/* Join Button disabling conditions */
 
		SetWindowWidgetDisabledState(w, 16, sel == NULL || // no Selected Server
 
				!sel->online || // Server offline
 
				sel->info.clients_on >= sel->info.clients_max || // Server full
 
				!sel->info.compatible); // Revision mismatch
 

	
 
		SetWindowWidgetHiddenState(w, 18, sel == NULL ||
 
				!sel->online ||
 
				sel->info.grfconfig == NULL);
 

	
 
		SetDParam(0, 0x00);
 
		SetDParam(7, _lan_internet_types_dropdown[_network_lan_internet]);
 
		DrawWindowWidgets(w);
 

	
 
		DrawEditBox(w, &WP(w, network_ql_d).q, 3);
 

	
 
		DrawString(9, 23, STR_NETWORK_CONNECTION, 2);
 
		DrawString(210, 23, STR_NETWORK_PLAYER_NAME, 2);
 

	
 
		/* Sort based on widgets: name, clients, compatibility */
 
		switch (ld->sort_type) {
 
			case 6 - 6: DoDrawString(arrow, w->widget[6].right - 10, 42, 0x10); break;
 
			case 7 - 6: DoDrawString(arrow, w->widget[7].right - 10, 42, 0x10); break;
 
			case 8 - 6: DoDrawString(arrow, w->widget[8].right - 10, 42, 0x10); break;
 
		}
 

	
 
		{ // draw list of games
 
			uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
 
			int32 n = 0;
 
			int32 pos = w->vscroll.pos;
 
			uint max_name_width = w->widget[6].right - w->widget[6].left - 5;
 
			const NetworkGameList *cur_item = _network_game_list;
 

	
 
			while (pos > 0 && cur_item != NULL) {
 
				pos--;
 
				cur_item = cur_item->next;
 
			}
 

	
 
			while (cur_item != NULL) {
 
				// show highlighted item with a different colour
 
				if (cur_item == sel) GfxFillRect(w->widget[6].left + 1, y - 2, w->widget[8].right - 1, y + 9, 10);
 

	
 
				SetDParamStr(0, cur_item->info.server_name);
 
				DrawStringTruncated(w->widget[6].left + 5, y, STR_02BD, 16, max_name_width);
 

	
 
				SetDParam(0, cur_item->info.clients_on);
 
				SetDParam(1, cur_item->info.clients_max);
 
				SetDParam(2, cur_item->info.companies_on);
 
				SetDParam(3, cur_item->info.companies_max);
 
				DrawStringCentered(210, y, STR_NETWORK_GENERAL_ONLINE, 2);
 

	
 
				// only draw icons if the server is online
 
				if (cur_item->online) {
 
					// draw a lock if the server is password protected.
 
					if (cur_item->info.use_password) DrawSprite(SPR_LOCK, w->widget[8].left + 5, y - 1);
 

	
 
					// draw red or green icon, depending on compatibility with server.
 
					DrawSprite(SPR_BLOT | (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[8].left + 15, y);
 

	
 
					// draw flag according to server language
 
					DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, w->widget[8].left + 25, y);
 
				}
 

	
 
				cur_item = cur_item->next;
 
				y += NET_PRC__SIZE_OF_ROW;
 
				if (++n == w->vscroll.cap) break; // max number of games in the window
 
			}
 
		}
 

	
 
		/* Draw the right menu */
 
		GfxFillRect(311, 43, 539, 92, 157);
 
		if (sel == NULL) {
 
			DrawStringCentered(425, 58, STR_NETWORK_GAME_INFO, 0);
 
		} else if (!sel->online) {
 
			SetDParamStr(0, sel->info.server_name);
 
			DrawStringCentered(425, 68, STR_ORANGE, 0); // game name
 

	
 
			DrawStringCentered(425, 132, STR_NETWORK_SERVER_OFFLINE, 0); // server offline
 
		} else { // show game info
 
			uint16 y = 100;
 
			const uint16 x = w->widget[15].left + 5;
 

	
 
			DrawStringCentered(425, 48, STR_NETWORK_GAME_INFO, 0);
 

	
 

	
 
			SetDParamStr(0, sel->info.server_name);
 
			DrawStringCenteredTruncated(w->widget[15].left, w->widget[15].right, 62, STR_ORANGE, 16); // game name
 

	
 
			SetDParamStr(0, sel->info.map_name);
 
			DrawStringCenteredTruncated(w->widget[15].left, w->widget[15].right, 74, STR_02BD, 16); // map name
 

	
 
			SetDParam(0, sel->info.clients_on);
 
			SetDParam(1, sel->info.clients_max);
 
			SetDParam(2, sel->info.companies_on);
 
			SetDParam(3, sel->info.companies_max);
 
			DrawString(x, y, STR_NETWORK_CLIENTS, 2);
 
			y += 10;
 

	
 
			SetDParam(0, _language_dropdown[sel->info.server_lang]);
 
			DrawString(x, y, STR_NETWORK_LANGUAGE, 2); // server language
 
			y += 10;
 

	
 
			SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set);
 
			DrawString(x, y, STR_NETWORK_TILESET, 2); // tileset
 
			y += 10;
 

	
 
			SetDParam(0, sel->info.map_width);
 
			SetDParam(1, sel->info.map_height);
 
			DrawString(x, y, STR_NETWORK_MAP_SIZE, 2); // map size
 
			y += 10;
 

	
 
			SetDParamStr(0, sel->info.server_revision);
 
			DrawString(x, y, STR_NETWORK_SERVER_VERSION, 2); // server version
 
			y += 10;
 

	
 
			SetDParamStr(0, sel->info.hostname);
 
			SetDParam(1, sel->port);
 
			DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, 2); // server address
 
			y += 10;
 

	
 
			SetDParam(0, sel->info.start_date);
 
			DrawString(x, y, STR_NETWORK_START_DATE, 2); // start date
 
			y += 10;
 

	
 
			SetDParam(0, sel->info.game_date);
 
			DrawString(x, y, STR_NETWORK_CURRENT_DATE, 2); // current date
 
			y += 10;
 

	
 
			y += 2;
 

	
 
			if (!sel->info.compatible) {
 
				DrawStringCentered(425, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, 0); // server mismatch
 
			} else if (sel->info.clients_on == sel->info.clients_max) {
 
				// Show: server full, when clients_on == clients_max
 
				DrawStringCentered(425, y, STR_NETWORK_SERVER_FULL, 0); // server full
 
			} else if (sel->info.use_password) {
 
				DrawStringCentered(425, y, STR_NETWORK_PASSWORD, 0); // password warning
 
			}
 

	
 
			y += 10;
 
		}
 
	}	break;
 

	
 
	case WE_CLICK:
 
		nd->field = e->we.click.widget;
 
		switch (e->we.click.widget) {
 
		case 0: case 14: /* Close 'X' | Cancel button */
 
			DeleteWindowById(WC_NETWORK_WINDOW, 0);
 
			break;
 
		case 4: case 5:
 
			ShowDropDownMenu(w, _lan_internet_types_dropdown, _network_lan_internet, 5, 0, 0); // do it for widget 5
 
			break;
 
		case 6: /* Sort by name */
 
		case 7: /* Sort by connected clients */
 
		case 8: /* Connectivity (green dot) */
 
			if (ld->sort_type == e->we.click.widget - 6) ld->flags ^= VL_DESC;
 
			ld->flags |= VL_RESORT;
 
			ld->sort_type = e->we.click.widget - 6;
 

	
 
			_ng_sorting.order = !!(ld->flags & VL_DESC);
 
			_ng_sorting.criteria = ld->sort_type;
 
			SetWindowDirty(w);
 
			break;
 
		case 9: { /* Matrix to show networkgames */
 
			NetworkGameList *cur_item;
 
			uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW;
 

	
 
			if (id_v >= w->vscroll.cap) return; // click out of bounds
 
			id_v += w->vscroll.pos;
 

	
 
			cur_item = _network_game_list;
 
			for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next;
 

	
 
			nd->server = cur_item;
 
			SetWindowDirty(w);
 
		} break;
 
		case 11: /* Find server automatically */
 
			switch (_network_lan_internet) {
 
				case 0: NetworkUDPSearchGame(); break;
 
				case 1: NetworkUDPQueryMasterServer(); break;
 
			}
 
			break;
 
		case 12: { // Add a server
 
				ShowQueryString(
 
				BindCString(_network_default_ip),
 
				STR_NETWORK_ENTER_IP,
 
				31 | 0x1000,  // maximum number of characters OR
 
				250, // characters up to this width pixels, whichever is satisfied first
 
				w, CS_ALPHANUMERAL);
 
		} break;
 
		case 13: /* Start server */
 
			ShowNetworkStartServerWindow();
 
			break;
 
		case 16: /* Join Game */
 
			if (nd->server != NULL) {
 
				snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&nd->server->ip));
 
				_network_last_port = nd->server->port;
 
				ShowNetworkLobbyWindow(nd->server);
 
			}
 
			break;
 
		case 17: // Refresh
 
			if (nd->server != NULL)
 
				NetworkQueryServer(nd->server->info.hostname, nd->server->port, true);
 
			break;
 
		case 18: // NewGRF Settings
 
			if (nd->server != NULL) ShowNewGRFSettings(false, false, false, &nd->server->info.grfconfig);
 
			break;
 

	
 
	}	break;
 

	
 
	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
		switch (e->we.dropdown.button) {
 
			case 5:
 
				_network_lan_internet = e->we.dropdown.index;
 
				break;
 
		}
 

	
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		if (nd->field == 3) HandleEditBox(w, &WP(w, network_ql_d).q, 3);
 
		break;
 

	
 
	case WE_MESSAGE:
 
		if (e->we.message.msg != 0) nd->server = NULL;
 
		ld->flags |= VL_REBUILD;
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		if (nd->field != 3) {
 
			if (nd->server != NULL) {
 
				if (e->we.keypress.keycode == WKC_DELETE) { /* Press 'delete' to remove servers */
 
					NetworkGameListRemoveItem(nd->server);
 
					NetworkRebuildHostList();
 
					nd->server = NULL;
 
				}
 
			}
 
			break;
 
		}
 

	
 
		if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, 3, e) == 1) break; // enter pressed
 

	
 
		// The name is only allowed when it starts with a letter!
 
		if (_edit_str_buf[0] != '\0' && _edit_str_buf[0] != ' ') {
 
			ttd_strlcpy(_network_player_name, _edit_str_buf, lengthof(_network_player_name));
 
		} else {
 
			ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name));
 
		}
 

	
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT:
 
		NetworkAddServer(e->we.edittext.str);
 
		NetworkRebuildHostList();
 
		break;
 

	
 
	case WE_DESTROY: /* Nicely clean up the sort-list */
 
		free(WP(w, network_ql_d).sort_list);
 
		break;
 
	}
 
}
 

	
 
static const Widget _network_game_window_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                    STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   549,     0,    13, STR_NETWORK_MULTIPLAYER,     STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,     0,   549,    14,   263, 0x0,                         STR_NULL},
 

	
 
/* LEFT SIDE */
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,   310,   461,    22,    33, 0x0,                         STR_NETWORK_ENTER_NAME_TIP},
 

	
 
{      WWT_INSET,   RESIZE_NONE,   BGC,    90,   181,    22,    33, STR_NETWORK_COMBO1,          STR_NETWORK_CONNECTION_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   170,   180,    23,    32, STR_0225,                    STR_NETWORK_CONNECTION_TIP},
 

	
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    10,   170,    42,    53, STR_NETWORK_GAME_NAME,       STR_NETWORK_GAME_NAME_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   171,   250,    42,    53, STR_NETWORK_CLIENTS_CAPTION, STR_NETWORK_CLIENTS_CAPTION_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   251,   290,    42,    53, STR_EMPTY,                   STR_NETWORK_INFO_ICONS_TIP},
 

	
 
{     WWT_MATRIX,   RESIZE_NONE,   BGC,    10,   290,    54,   236, (13 << 8) + 1,               STR_NETWORK_CLICK_GAME_TO_SELECT},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,   BGC,   291,   302,    42,   236, STR_NULL,                    STR_0190_SCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    30,   130,   246,   257, STR_NETWORK_FIND_SERVER,     STR_NETWORK_FIND_SERVER_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   180,   280,   246,   257, STR_NETWORK_ADD_SERVER,      STR_NETWORK_ADD_SERVER_TIP},
 

	
 
/* RIGHT SIDE */
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   315,   415,   246,   257, STR_NETWORK_START_SERVER,    STR_NETWORK_START_SERVER_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   430,   535,   246,   257, STR_012E_CANCEL,             STR_NULL},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,   310,   540,    42,   236, 0x0,                         STR_NULL},
 

	
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   315,   415,   215,   226, STR_NETWORK_JOIN_GAME,       STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   430,   535,   215,   226, STR_NETWORK_REFRESH,         STR_NETWORK_REFRESH_TIP},
 

	
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   430,   535,   197,   208, STR_NEWGRF_SETTINGS_BUTTON,  STR_NULL},
 

	
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_game_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 550, 264,
 
	WC_NETWORK_WINDOW,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_network_game_window_widgets,
 
	NetworkGameWindowWndProc,
 
};
 

	
 
void ShowNetworkGameWindow(void)
 
{
 
	static bool first = true;
 
	Window *w;
 
	DeleteWindowById(WC_NETWORK_WINDOW, 0);
 

	
 
	/* Only show once */
 
	if (first) {
 
		char* const *srv;
 

	
 
		first = false;
 
		// add all servers from the config file to our list
 
		for (srv = &_network_host_list[0]; srv != endof(_network_host_list) && *srv != NULL; srv++) {
 
			NetworkAddServer(*srv);
 
		}
 

	
 
		_ng_sorting.criteria = 2; // sort default by collectivity (green-dots on top)
 
		_ng_sorting.order = 0;    // sort ascending by default
 
	}
 

	
 
	w = AllocateWindowDesc(&_network_game_window_desc);
 
	if (w != NULL) {
 
		querystr_d *querystr = &WP(w, network_ql_d).q;
 

	
 
		ttd_strlcpy(_edit_str_buf, _network_player_name, lengthof(_edit_str_buf));
 
		w->vscroll.cap = 13;
 

	
 
		querystr->afilter = CS_ALPHANUMERAL;
 
		InitializeTextBuffer(&querystr->text, _edit_str_buf, lengthof(_edit_str_buf), 120);
 

	
 
		UpdateNetworkGameWindow(true);
 
	}
 
}
 

	
 
enum {
 
	NSSWND_START = 64,
 
	NSSWND_ROWSIZE = 12
 
};
 

	
 
/* Uses network_ql_d (network_d, querystr_d and list_d) WP macro */
 
static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	network_d *nd = &WP(w, network_ql_d).n;
 

	
 
	switch (e->event) {
 
	case WE_CREATE: /* focus input box */
 
		nd->field = 3;
 
		_network_game_info.use_password = (_network_server_password[0] != '\0');
 
		break;
 

	
 
	case WE_PAINT: {
 
		int y = NSSWND_START, pos;
 
		const FiosItem *item;
 

	
 
		SetDParam( 7, _connection_types_dropdown[_network_advertise]);
 
		SetDParam( 9, _players_dropdown[_network_game_info.clients_max]);
 
		SetDParam(11, _players_dropdown[_network_game_info.companies_max]);
 
		SetDParam(13, _players_dropdown[_network_game_info.spectators_max]);
 
		SetDParam(15, _language_dropdown[_network_game_info.server_lang]);
 
		DrawWindowWidgets(w);
 

	
 
		GfxFillRect(11, 63, 258, 215, 0xD7);
 
		DrawEditBox(w, &WP(w, network_ql_d).q, 3);
 

	
 
		DrawString(10, 22, STR_NETWORK_NEW_GAME_NAME, 2);
 

	
 
		DrawString(10, 43, STR_NETWORK_SELECT_MAP, 2);
 

	
 
		DrawString(280,  63, STR_NETWORK_CONNECTION, 2);
 
		DrawString(280,  95, STR_NETWORK_NUMBER_OF_CLIENTS, 2);
 
		DrawString(280, 127, STR_NETWORK_NUMBER_OF_COMPANIES, 2);
 
		DrawString(280, 159, STR_NETWORK_NUMBER_OF_SPECTATORS, 2);
 
		DrawString(280, 191, STR_NETWORK_LANGUAGE_SPOKEN, 2);
 

	
 
		if (_network_game_info.use_password) DoDrawString("*", 408, 23, 3);
 

	
 
		// draw list of maps
 
		pos = w->vscroll.pos;
 
		while (pos < _fios_num + 1) {
 
			item = _fios_list + pos - 1;
 
			if (item == nd->map || (pos == 0 && nd->map == NULL))
 
				GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour
 

	
 
			if (pos == 0) {
 
				DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, 9);
 
			} else {
 
				DoDrawString(item->title, 14, y, _fios_colors[item->type] );
 
			}
 
			pos++;
 
			y += NSSWND_ROWSIZE;
 

	
 
			if (y >= w->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break;
 
		}
 
	}	break;
 

	
 
	case WE_CLICK:
 
		nd->field = e->we.click.widget;
 
		switch (e->we.click.widget) {
 
		case 0: /* Close 'X' */
 
		case 19: /* Cancel button */
 
			ShowNetworkGameWindow();
 
			break;
 

	
 
		case 4: /* Set password button */
 
			ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, w, CS_ALPHANUMERAL);
 
			break;
 

	
 
		case 5: { /* Select map */
 
			int y = (e->we.click.pt.y - NSSWND_START) / NSSWND_ROWSIZE;
 

	
 
			y += w->vscroll.pos;
 
			if (y >= w->vscroll.count) return;
 

	
 
			nd->map = (y == 0) ? NULL : _fios_list + y - 1;
 
			SetWindowDirty(w);
 
			} break;
 
		case 7: case 8: /* Connection type */
 
			ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, 8, 0, 0); // do it for widget 8
 
			break;
 
		case 9: case 10: /* Number of Players (hide 0 and 1 players) */
 
			ShowDropDownMenu(w, _players_dropdown, _network_game_info.clients_max, 10, 0, 3);
 
			break;
 
		case 11: case 12: /* Number of Companies (hide 0, 9 and 10 companies; max is 8) */
 
			ShowDropDownMenu(w, _players_dropdown, _network_game_info.companies_max, 12, 0, 1537);
 
			break;
 
		case 13: case 14: /* Number of Spectators */
 
			ShowDropDownMenu(w, _players_dropdown, _network_game_info.spectators_max, 14, 0, 0);
 
			break;
 
		case 15: case 16: /* Language */
 
			ShowDropDownMenu(w, _language_dropdown, _network_game_info.server_lang, 16, 0, 0);
 
			break;
 
		case 17: /* Start game */
 
			_is_network_server = true;
 

	
 
			if (nd->map == NULL) { // start random new game
 
				ShowGenerateLandscape();
 
			} else { // load a scenario
 
				char *name = FiosBrowseTo(nd->map);
 
				if (name != NULL) {
 
					SetFiosType(nd->map->type);
 
					ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
 
					ttd_strlcpy(_file_to_saveload.title, nd->map->title, sizeof(_file_to_saveload.title));
 

	
 
					DeleteWindow(w);
 
					SwitchMode(SM_START_SCENARIO);
 
				}
 
			}
 
			break;
 
		case 18: /* Load game */
 
			_is_network_server = true;
 
			/* XXX - WC_NETWORK_WINDOW should stay, but if it stays, it gets
 
			 * copied all the elements of 'load game' and upon closing that, it segfaults */
 
			DeleteWindowById(WC_NETWORK_WINDOW, 0);
 
			ShowSaveLoadDialog(SLD_LOAD_GAME);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
		switch (e->we.dropdown.button) {
 
			case  8: _network_advertise                = (e->we.dropdown.index != 0); break;
 
			case 10: _network_game_info.clients_max    = e->we.dropdown.index;        break;
 
			case 12: _network_game_info.companies_max  = e->we.dropdown.index;        break;
 
			case 14: _network_game_info.spectators_max = e->we.dropdown.index;        break;
 
			case 16: _network_game_info.server_lang    = e->we.dropdown.index;        break;
 
		}
 

	
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		if (nd->field == 3) HandleEditBox(w, &WP(w, network_ql_d).q, 3);
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		if (nd->field == 3) {
 
			if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, 3, e) == 1) break; // enter pressed
 

	
 
			ttd_strlcpy(_network_server_name, WP(w, network_ql_d).q.text.buf, sizeof(_network_server_name));
 
			UpdateTextBufferSize(&WP(w, network_ql_d).q.text);
 
		}
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
		ttd_strlcpy(_network_server_password, e->we.edittext.str, lengthof(_network_server_password));
 
		_network_game_info.use_password = (_network_server_password[0] != '\0');
 
		SetWindowDirty(w);
 
	} break;
 
	}
 
}
 

	
 
static const Widget _network_start_server_window_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                      STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   419,     0,    13, STR_NETWORK_START_GAME_WINDOW, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,     0,   419,    14,   243, 0x0,                           STR_NULL},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,   100,   272,    22,    33, 0x0,                           STR_NETWORK_NEW_GAME_NAME_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   285,   405,    22,    33, STR_NETWORK_SET_PASSWORD,      STR_NETWORK_PASSWORD_TIP},
 

	
 
{      WWT_INSET,   RESIZE_NONE,   BGC,    10,   271,    62,   216, 0x0,                           STR_NETWORK_SELECT_MAP_TIP},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,   BGC,   259,   270,    63,   215, 0x0,                           STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
/* Combo boxes to control Connection Type / Max Clients / Max Companies / Max Observers / Language */
 
{      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,    77,    88, STR_NETWORK_COMBO1,            STR_NETWORK_CONNECTION_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,    78,    87, STR_0225,                      STR_NETWORK_CONNECTION_TIP},
 
{      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   109,   120, STR_NETWORK_COMBO2,            STR_NETWORK_NUMBER_OF_CLIENTS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   110,   119, STR_0225,                      STR_NETWORK_NUMBER_OF_CLIENTS_TIP},
 
{      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   141,   152, STR_NETWORK_COMBO3,            STR_NETWORK_NUMBER_OF_COMPANIES_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   142,   151, STR_0225,                      STR_NETWORK_NUMBER_OF_COMPANIES_TIP},
 
{      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   173,   184, STR_NETWORK_COMBO4,            STR_NETWORK_NUMBER_OF_SPECTATORS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   174,   183, STR_0225,                      STR_NETWORK_NUMBER_OF_SPECTATORS_TIP},
 
{      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   205,   216, STR_NETWORK_COMBO5,            STR_NETWORK_LANGUAGE_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   206,   215, STR_0225,                      STR_NETWORK_LANGUAGE_TIP},
 

	
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    40,   140,   224,   235, STR_NETWORK_START_GAME,        STR_NETWORK_START_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   150,   250,   224,   235, STR_NETWORK_LOAD_GAME,         STR_NETWORK_LOAD_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   260,   360,   224,   235, STR_012E_CANCEL,               STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_start_server_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 420, 244,
 
	WC_NETWORK_WINDOW,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_network_start_server_window_widgets,
 
	NetworkStartServerWindowWndProc,
 
};
 

	
 
static void ShowNetworkStartServerWindow(void)
 
{
 
	Window *w;
 
	DeleteWindowById(WC_NETWORK_WINDOW, 0);
 

	
 
	w = AllocateWindowDesc(&_network_start_server_window_desc);
 
	ttd_strlcpy(_edit_str_buf, _network_server_name, lengthof(_edit_str_buf));
 

	
 
	_saveload_mode = SLD_NEW_GAME;
 
	BuildFileList();
 
	w->vscroll.cap = 12;
 
	w->vscroll.count = _fios_num+1;
 

	
 
	WP(w, network_ql_d).q.afilter = CS_ALPHANUMERAL;
 
	InitializeTextBuffer(&WP(w, network_ql_d).q.text, _edit_str_buf, lengthof(_edit_str_buf), 160);
 
}
 

	
 
static byte NetworkLobbyFindCompanyIndex(byte pos)
 
{
 
	byte i;
 

	
 
	/* Scroll through all _network_player_info and get the 'pos' item
 
	    that is not empty */
 
	for (i = 0; i < MAX_PLAYERS; i++) {
 
		if (_network_player_info[i].company_name[0] != '\0') {
 
			if (pos-- == 0) return i;
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
/* uses network_d WP macro */
 
static void NetworkLobbyWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	network_d *nd = &WP(w, network_d);
 

	
 
	switch (e->event) {
 
	case WE_CREATE:
 
		nd->company = (byte)-1;
 
		break;
 

	
 
	case WE_PAINT: {
 
		const NetworkGameInfo *gi = &nd->server->info;
 
		int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos;
 

	
 
		SetWindowWidgetDisabledState(w, 7, nd->company == (byte)-1);
 
		SetWindowWidgetDisabledState(w, 8, gi->companies_on >= gi->companies_max);
 
		/* You can not join a server as spectator when it has no companies active..
 
		 * it causes some nasty crashes */
 
		SetWindowWidgetDisabledState(w, 9, gi->spectators_on >= gi->spectators_max ||
 
				gi->companies_on == 0);
 

	
 
		DrawWindowWidgets(w);
 

	
 
		SetDParamStr(0, gi->server_name);
 
		DrawString(10, 22, STR_NETWORK_PREPARE_TO_JOIN, 2);
 

	
 
		/* Draw company list */
 
		pos = w->vscroll.pos;
 
		while (pos < gi->companies_on) {
 
			byte company = NetworkLobbyFindCompanyIndex(pos);
 
			bool income = false;
 
			if (nd->company == company)
 
				GfxFillRect(11, y - 1, 154, y + 10, 10); // show highlighted item with a different colour
 

	
 
			DoDrawStringTruncated(_network_player_info[company].company_name, 13, y, 16, 135 - 13);
 
			if (_network_player_info[company].use_password != 0) DrawSprite(SPR_LOCK, 135, y);
 

	
 
			/* If the company's income was positive puts a green dot else a red dot */
 
			if (_network_player_info[company].income >= 0) income = true;
 
			DrawSprite(SPR_BLOT | (income ? PALETTE_TO_GREEN : PALETTE_TO_RED), 145, y);
 

	
 
			pos++;
 
			y += NET_PRC__SIZE_OF_ROW;
 
			if (pos >= w->vscroll.cap) break;
 
		}
 

	
 
		/* Draw info about selected company when it is selected in the left window */
 
		GfxFillRect(174, 39, 403, 75, 157);
 
		DrawStringCentered(290, 50, STR_NETWORK_COMPANY_INFO, 0);
 
		if (nd->company != (byte)-1) {
 
			const uint x = 183;
 
			const uint trunc_width = w->widget[6].right - x;
 
			y = 80;
 

	
 
			SetDParam(0, nd->server->info.clients_on);
 
			SetDParam(1, nd->server->info.clients_max);
 
			SetDParam(2, nd->server->info.companies_on);
 
			SetDParam(3, nd->server->info.companies_max);
 
			DrawString(x, y, STR_NETWORK_CLIENTS, 2);
 
			y += 10;
 

	
 
			SetDParamStr(0, _network_player_info[nd->company].company_name);
 
			DrawStringTruncated(x, y, STR_NETWORK_COMPANY_NAME, 2, trunc_width);
 
			y += 10;
 

	
 
			SetDParam(0, _network_player_info[nd->company].inaugurated_year);
 
			DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, 2); // inauguration year
 
			y += 10;
 

	
 
			SetDParam64(0, _network_player_info[nd->company].company_value);
 
			DrawString(x, y, STR_NETWORK_VALUE, 2); // company value
 
			y += 10;
 

	
 
			SetDParam64(0, _network_player_info[nd->company].money);
 
			DrawString(x, y, STR_NETWORK_CURRENT_BALANCE, 2); // current balance
 
			y += 10;
 

	
 
			SetDParam64(0, _network_player_info[nd->company].income);
 
			DrawString(x, y, STR_NETWORK_LAST_YEARS_INCOME, 2); // last year's income
 
			y += 10;
 

	
 
			SetDParam(0, _network_player_info[nd->company].performance);
 
			DrawString(x, y, STR_NETWORK_PERFORMANCE, 2); // performance
 
			y += 10;
 

	
 
			SetDParam(0, _network_player_info[nd->company].num_vehicle[0]);
 
			SetDParam(1, _network_player_info[nd->company].num_vehicle[1]);
 
			SetDParam(2, _network_player_info[nd->company].num_vehicle[2]);
 
			SetDParam(3, _network_player_info[nd->company].num_vehicle[3]);
 
			SetDParam(4, _network_player_info[nd->company].num_vehicle[4]);
 
			DrawString(x, y, STR_NETWORK_VEHICLES, 2); // vehicles
 
			y += 10;
 

	
 
			SetDParam(0, _network_player_info[nd->company].num_station[0]);
 
			SetDParam(1, _network_player_info[nd->company].num_station[1]);
 
			SetDParam(2, _network_player_info[nd->company].num_station[2]);
 
			SetDParam(3, _network_player_info[nd->company].num_station[3]);
 
			SetDParam(4, _network_player_info[nd->company].num_station[4]);
 
			DrawString(x, y, STR_NETWORK_STATIONS, 2); // stations
 
			y += 10;
 

	
 
			SetDParamStr(0, _network_player_info[nd->company].players);
 
			DrawStringTruncated(x, y, STR_NETWORK_PLAYERS, 2, trunc_width); // players
 
		}
 
	}	break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 0: case 11: /* Close 'X' | Cancel button */
 
			ShowNetworkGameWindow();
 
			break;
 
		case 4: { /* Company list */
 
			uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW;
 

	
 
			if (id_v >= w->vscroll.cap) return;
 

	
 
			id_v += w->vscroll.pos;
 
			nd->company = (id_v >= nd->server->info.companies_on) ? (byte)-1 : NetworkLobbyFindCompanyIndex(id_v);
 
			SetWindowDirty(w);
 
		}	break;
 
		case 7: /* Join company */
 
			if (nd->company != (byte)-1) {
 
				_network_playas = nd->company;
 
				NetworkClientConnectGame(_network_last_host, _network_last_port);
 
			}
 
			break;
 
		case 8: /* New company */
 
			_network_playas = PLAYER_NEW_COMPANY;
 
			NetworkClientConnectGame(_network_last_host, _network_last_port);
 
			break;
 
		case 9: /* Spectate game */
 
			_network_playas = PLAYER_SPECTATOR;
 
			NetworkClientConnectGame(_network_last_host, _network_last_port);
 
			break;
 
		case 10: /* Refresh */
 
			NetworkQueryServer(_network_last_host, _network_last_port, false); // company info
 
			NetworkUDPQueryServer(_network_last_host, _network_last_port);     // general data
 
			break;
 
		}	break;
 

	
 
	case WE_MESSAGE:
 
		SetWindowDirty(w);
 
		break;
 
	}
 
}
 

	
 
static const Widget _network_lobby_window_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                  STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   419,     0,    13, STR_NETWORK_GAME_LOBBY,    STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,     0,   419,    14,   234, 0x0,                       STR_NULL},
 

	
 
// company list
 
{      WWT_PANEL,   RESIZE_NONE,   BTC,    10,   155,    38,    49, 0x0,                       STR_NULL},
 
{     WWT_MATRIX,   RESIZE_NONE,   BGC,    10,   155,    50,   190, (10 << 8) + 1,             STR_NETWORK_COMPANY_LIST_TIP},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,   BGC,   156,   167,    38,   190, STR_NULL,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
 

	
 
// company/player info
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,   173,   404,    38,   190, 0x0,                       STR_NULL},
 

	
 
// buttons
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    10,   151,   200,   211, STR_NETWORK_JOIN_COMPANY,  STR_NETWORK_JOIN_COMPANY_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    10,   151,   215,   226, STR_NETWORK_NEW_COMPANY,   STR_NETWORK_NEW_COMPANY_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   158,   268,   200,   211, STR_NETWORK_SPECTATE_GAME, STR_NETWORK_SPECTATE_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   158,   268,   215,   226, STR_NETWORK_REFRESH,       STR_NETWORK_REFRESH_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   278,   388,   200,   211, STR_012E_CANCEL,           STR_NULL},
 

	
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_lobby_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 420, 235,
 
	WC_NETWORK_WINDOW,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_network_lobby_window_widgets,
 
	NetworkLobbyWindowWndProc,
 
};
 

	
 
/* Show the networklobbywindow with the selected server
 
 * @param ngl Selected game pointer which is passed to the new window */
 
static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
 
{
 
	Window *w;
 
	DeleteWindowById(WC_NETWORK_WINDOW, 0);
 

	
 
	NetworkQueryServer(_network_last_host, _network_last_port, false); // company info
 
	NetworkUDPQueryServer(_network_last_host, _network_last_port);     // general data
 

	
 
	w = AllocateWindowDesc(&_network_lobby_window_desc);
 
	if (w != NULL) {
 
		WP(w, network_ql_d).n.server = ngl;
 
		strcpy(_edit_str_buf, "");
 
		w->vscroll.cap = 10;
 
	}
 
}
 

	
 
// The window below gives information about the connected clients
 
//  and also makes able to give money to them, kick them (if server)
 
//  and stuff like that.
 

	
 
extern void DrawPlayerIcon(PlayerID pid, int x, int y);
 

	
 
// Every action must be of this form
 
typedef void ClientList_Action_Proc(byte client_no);
 

	
 
// Max 10 actions per client
 
#define MAX_CLIENTLIST_ACTION 10
 

	
 
// Some standard bullshit.. defines variables ;)
 
static void ClientListWndProc(Window *w, WindowEvent *e);
 
static void ClientListPopupWndProc(Window *w, WindowEvent *e);
 
static byte _selected_clientlist_item = 255;
 
static byte _selected_clientlist_y = 0;
 
static char _clientlist_action[MAX_CLIENTLIST_ACTION][50];
 
static ClientList_Action_Proc *_clientlist_proc[MAX_CLIENTLIST_ACTION];
 

	
 
enum {
 
	CLNWND_OFFSET = 16,
 
	CLNWND_ROWSIZE = 10
 
};
 

	
 
static const Widget _client_list_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   249,     0,    13, STR_NETWORK_CLIENT_LIST,  STR_018C_WINDOW_TITLE_DRAG_THIS},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   249,    14,    14 + CLNWND_ROWSIZE + 1, 0x0, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _client_list_popup_widgets[] = {
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   99,     0,     0,     0, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static WindowDesc _client_list_desc = {
 
	WDP_AUTO, WDP_AUTO, 250, 1,
 
	WC_CLIENT_LIST,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_client_list_widgets,
 
	ClientListWndProc
 
};
 

	
 
// Finds the Xth client-info that is active
 
static const NetworkClientInfo *NetworkFindClientInfo(byte client_no)
 
{
 
	const NetworkClientInfo *ci;
 

	
 
	FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
 
		if (client_no == 0) return ci;
 
		client_no--;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
// Here we start to define the options out of the menu
 
static void ClientList_Kick(byte client_no)
 
{
 
	if (client_no < MAX_PLAYERS)
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(DEREF_CLIENT(client_no), NETWORK_ERROR_KICKED);
 
}
 

	
 
static void ClientList_Ban(byte client_no)
 
{
 
	uint i;
 
	uint32 ip = NetworkFindClientInfo(client_no)->client_ip;
 

	
 
	for (i = 0; i < lengthof(_network_ban_list); i++) {
 
		if (_network_ban_list[i] == NULL) {
 
			_network_ban_list[i] = strdup(inet_ntoa(*(struct in_addr *)&ip));
 
			break;
 
		}
 
	}
 

	
 
	if (client_no < MAX_PLAYERS)
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(DEREF_CLIENT(client_no), NETWORK_ERROR_KICKED);
 
}
 

	
 
static void ClientList_GiveMoney(byte client_no)
 
{
 
	if (NetworkFindClientInfo(client_no) != NULL)
 
		ShowNetworkGiveMoneyWindow(NetworkFindClientInfo(client_no)->client_playas);
 
}
 

	
 
static void ClientList_SpeakToClient(byte client_no)
 
{
 
	if (NetworkFindClientInfo(client_no) != NULL)
 
		ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, NetworkFindClientInfo(client_no)->client_index);
 
}
 

	
 
static void ClientList_SpeakToCompany(byte client_no)
 
{
 
	if (NetworkFindClientInfo(client_no) != NULL)
 
		ShowNetworkChatQueryWindow(DESTTYPE_TEAM, NetworkFindClientInfo(client_no)->client_playas);
 
}
 

	
 
static void ClientList_SpeakToAll(byte client_no)
 
{
 
	ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0);
 
}
 

	
 
static void ClientList_None(byte client_no)
 
{
 
	// No action ;)
 
}
 

	
 

	
 

	
 
// Help, a action is clicked! What do we do?
 
static void HandleClientListPopupClick(byte index, byte clientno) {
 
	// A click on the Popup of the ClientList.. handle the command
 
	if (index < MAX_CLIENTLIST_ACTION && _clientlist_proc[index] != NULL) {
 
		_clientlist_proc[index](clientno);
 
	}
 
}
 

	
 
// Finds the amount of clients and set the height correct
 
static bool CheckClientListHeight(Window *w)
 
{
 
	int num = 0;
 
	const NetworkClientInfo *ci;
 

	
 
	// Should be replaced with a loop through all clients
 
	FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
 
		num++;
 
	}
 

	
 
	num *= CLNWND_ROWSIZE;
 

	
 
	// If height is changed
 
	if (w->height != CLNWND_OFFSET + num + 1) {
 
		// XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1)
 
		SetWindowDirty(w);
 
		w->widget[2].bottom = w->widget[2].top + num + 2;
 
		w->height = CLNWND_OFFSET + num + 1;
 
		SetWindowDirty(w);
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
// Finds the amount of actions in the popup and set the height correct
 
static uint ClientListPopupHeigth(void) {
 
	int i, num = 0;
 

	
 
	// Find the amount of actions
 
	for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) {
 
		if (_clientlist_action[i][0] == '\0') continue;
 
		if (_clientlist_proc[i] == NULL) continue;
 
		num++;
 
	}
 

	
 
	num *= CLNWND_ROWSIZE;
 

	
 
	return num + 1;
 
}
 

	
 
// Show the popup (action list)
 
static Window *PopupClientList(Window *w, int client_no, int x, int y)
 
{
 
	int i, h;
 
	const NetworkClientInfo *ci;
 
	DeleteWindowById(WC_TOOLBAR_MENU, 0);
 

	
 
	// Clean the current actions
 
	for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) {
 
		_clientlist_action[i][0] = '\0';
 
		_clientlist_proc[i] = NULL;
 
	}
 

	
 
	// Fill the actions this client has
 
	// Watch is, max 50 chars long!
 

	
 
	ci = NetworkFindClientInfo(client_no);
 
	if (ci == NULL) return NULL;
 

	
 
	i = 0;
 
	if (_network_own_client_index != ci->client_index) {
 
		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT, lastof(_clientlist_action[i]));
 
		_clientlist_proc[i++] = &ClientList_SpeakToClient;
 
	}
 

	
 
	if (IsValidPlayer(ci->client_playas) || ci->client_playas == PLAYER_SPECTATOR) {
 
		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY, lastof(_clientlist_action[i]));
 
		_clientlist_proc[i++] = &ClientList_SpeakToCompany;
 
	}
 
	GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, lastof(_clientlist_action[i]));
 
	_clientlist_proc[i++] = &ClientList_SpeakToAll;
 

	
 
	if (_network_own_client_index != ci->client_index) {
 
		/* We are no spectator and the player we want to give money to is no spectator */
 
		if (IsValidPlayer(_network_playas) && IsValidPlayer(ci->client_playas)) {
 
			GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_GIVE_MONEY, lastof(_clientlist_action[i]));
 
			_clientlist_proc[i++] = &ClientList_GiveMoney;
 
		}
 
	}
 

	
 
	// A server can kick clients (but not himself)
 
	if (_network_server && _network_own_client_index != ci->client_index) {
 
		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_KICK, lastof(_clientlist_action[i]));
 
		_clientlist_proc[i++] = &ClientList_Kick;
 

	
 
		sprintf(_clientlist_action[i],"Ban"); // XXX GetString?
 
		_clientlist_proc[i++] = &ClientList_Ban;
 
	}
 

	
 
	if (i == 0) {
 
		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_NONE, lastof(_clientlist_action[i]));
 
		_clientlist_proc[i++] = &ClientList_None;
 
	}
 

	
 
	/* Calculate the height */
 
	h = ClientListPopupHeigth();
 

	
 
	// Allocate the popup
 
	w = AllocateWindow(x, y, 150, h + 1, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets);
 
	w->widget[0].bottom = w->widget[0].top + h;
 
	w->widget[0].right = w->widget[0].left + 150;
 

	
 
	w->flags4 &= ~WF_WHITE_BORDER_MASK;
 
	WP(w,menu_d).item_count = 0;
 
	// Save our client
 
	WP(w,menu_d).main_button = client_no;
 
	WP(w,menu_d).sel_index = 0;
 
	// We are a popup
 
	_popup_menu_active = true;
 

	
 
	return w;
 
}
 

	
 
/** Main handle for the client popup list
 
 * uses menu_d WP macro */
 
static void ClientListPopupWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		int i, y, sel;
 
		byte colour;
 
		DrawWindowWidgets(w);
 

	
 
		// Draw the actions
 
		sel = WP(w,menu_d).sel_index;
 
		y = 1;
 
		for (i = 0; i < MAX_CLIENTLIST_ACTION; i++, y += CLNWND_ROWSIZE) {
 
			if (_clientlist_action[i][0] == '\0') continue;
 
			if (_clientlist_proc[i] == NULL) continue;
 

	
 
			if (sel-- == 0) { // Selected item, highlight it
 
				GfxFillRect(1, y, 150 - 2, y + CLNWND_ROWSIZE - 1, 0);
 
				colour = 0xC;
 
			} else {
 
				colour = 0x10;
 
			}
 

	
 
			DoDrawString(_clientlist_action[i], 4, y, colour);
 
		}
 
	}	break;
 

	
 
	case WE_POPUPMENU_SELECT: {
 
		// We selected an action
 
		int index = (e->we.popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
 

	
 
		if (index >= 0 && e->we.popupmenu.pt.y >= w->top)
 
			HandleClientListPopupClick(index, WP(w,menu_d).main_button);
 

	
 
		DeleteWindowById(WC_TOOLBAR_MENU, 0);
 
	}	break;
 

	
 
	case WE_POPUPMENU_OVER: {
 
		// Our mouse hoovers over an action? Select it!
 
		int index = (e->we.popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
 

	
 
		if (index == -1 || index == WP(w,menu_d).sel_index) return;
 

	
 
		WP(w,menu_d).sel_index = index;
 
		SetWindowDirty(w);
 
	} break;
 

	
 
	}
 
}
 

	
 
// Main handle for clientlist
 
static void ClientListWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		NetworkClientInfo *ci;
 
		int y, i = 0;
 
		byte colour;
 

	
 
		// Check if we need to reset the height
 
		if (!CheckClientListHeight(w)) break;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		y = CLNWND_OFFSET;
 

	
 
		FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
 
			if (_selected_clientlist_item == i++) { // Selected item, highlight it
 
				GfxFillRect(1, y, 248, y + CLNWND_ROWSIZE - 1, 0);
 
				colour = 0xC;
 
			} else {
 
				colour = 0x10;
 
			}
 

	
 
			if (ci->client_index == NETWORK_SERVER_INDEX) {
 
				DrawString(4, y, STR_NETWORK_SERVER, colour);
 
			} else {
 
				DrawString(4, y, STR_NETWORK_CLIENT, colour);
 
			}
 

	
 
			// Filter out spectators
 
			if (IsValidPlayer(ci->client_playas)) DrawPlayerIcon(ci->client_playas, 64, y + 1);
 

	
 
			DoDrawString(ci->client_name, 81, y, colour);
 

	
 
			y += CLNWND_ROWSIZE;
 
		}
 
	}	break;
 

	
 
	case WE_CLICK:
 
		// Show the popup with option
 
		if (_selected_clientlist_item != 255) {
 
			PopupClientList(w, _selected_clientlist_item, e->we.click.pt.x + w->left, e->we.click.pt.y + w->top);
 
		}
 

	
 
		break;
 

	
 
	case WE_MOUSEOVER:
 
		// -1 means we left the current window
 
		if (e->we.mouseover.pt.y == -1) {
 
			_selected_clientlist_y = 0;
 
			_selected_clientlist_item = 255;
 
			SetWindowDirty(w);
 
			break;
 
		}
 
		// It did not change.. no update!
 
		if (e->we.mouseover.pt.y == _selected_clientlist_y) break;
 

	
 
		// Find the new selected item (if any)
 
		_selected_clientlist_y = e->we.mouseover.pt.y;
 
		if (e->we.mouseover.pt.y > CLNWND_OFFSET) {
 
			_selected_clientlist_item = (e->we.mouseover.pt.y - CLNWND_OFFSET) / CLNWND_ROWSIZE;
 
		} else {
 
			_selected_clientlist_item = 255;
 
		}
 

	
 
		// Repaint
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_DESTROY: case WE_CREATE:
 
		// When created or destroyed, data is reset
 
		_selected_clientlist_item = 255;
 
		_selected_clientlist_y = 0;
 
		break;
 
	}
 
}
 

	
 
void ShowClientList(void)
 
{
 
	AllocateWindowDescFront(&_client_list_desc, 0);
 
}
 

	
 

	
 
static NetworkPasswordType pw_type;
 

	
 

	
 
void ShowNetworkNeedPassword(NetworkPasswordType npt)
 
{
 
	StringID caption;
 

	
 
	pw_type = npt;
 
	switch (npt) {
 
		default: NOT_REACHED();
 
		case NETWORK_GAME_PASSWORD:    caption = STR_NETWORK_NEED_GAME_PASSWORD_CAPTION; break;
 
		case NETWORK_COMPANY_PASSWORD: caption = STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION; break;
 
	}
 
	ShowQueryString(STR_EMPTY, caption, 20, 180, FindWindowById(WC_NETWORK_STATUS_WINDOW, 0), CS_ALPHANUMERAL);
 
}
 

	
 

	
 
static void NetworkJoinStatusWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		uint8 progress; // used for progress bar
 
		DrawWindowWidgets(w);
 

	
 
		DrawStringCentered(125, 35, STR_NETWORK_CONNECTING_1 + _network_join_status, 14);
 
		switch (_network_join_status) {
 
			case NETWORK_JOIN_STATUS_CONNECTING: case NETWORK_JOIN_STATUS_AUTHORIZING:
 
			case NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO:
 
				progress = 10; // first two stages 10%
 
				break;
 
			case NETWORK_JOIN_STATUS_WAITING:
 
				SetDParam(0, _network_join_waiting);
 
				DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_WAITING, 14);
 
				progress = 15; // third stage is 15%
 
				break;
 
			case NETWORK_JOIN_STATUS_DOWNLOADING:
 
				SetDParam(0, _network_join_kbytes);
 
				SetDParam(1, _network_join_kbytes_total);
 
				DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_DOWNLOADING, 14);
 
				/* Fallthrough */
 
			default: /* Waiting is 15%, so the resting receivement of map is maximum 70% */
 
				progress = 15 + _network_join_kbytes * (100 - 15) / _network_join_kbytes_total;
 
		}
 

	
 
		/* Draw nice progress bar :) */
 
		DrawFrameRect(20, 18, (int)((w->width - 20) * progress / 100), 28, 10, 0);
 
	}	break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
			case 2: /* Disconnect button */
 
				NetworkDisconnect();
 
				DeleteWindow(w);
 
				SwitchMode(SM_MENU);
 
				ShowNetworkGameWindow();
 
				break;
 
		}
 
		break;
 

	
 
		/* If the server asks for a password, we need to fill it in */
 
		case WE_ON_EDIT_TEXT_CANCEL:
 
			NetworkDisconnect();
 
			ShowNetworkGameWindow();
 
			break;
 

	
 
		case WE_ON_EDIT_TEXT:
 
			SEND_COMMAND(PACKET_CLIENT_PASSWORD)(pw_type, e->we.edittext.str);
 
			break;
 
	}
 
}
 

	
 
static const Widget _network_join_status_window_widget[] = {
 
{    WWT_CAPTION,   RESIZE_NONE,    14,     0,   249,     0,    13, STR_NETWORK_CONNECTING, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   249,    14,    84, 0x0,                    STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    75,   175,    69,    80, STR_NETWORK_DISCONNECT, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_join_status_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 250, 85,
 
	WC_NETWORK_STATUS_WINDOW, 0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_MODAL,
 
	_network_join_status_window_widget,
 
	NetworkJoinStatusWindowWndProc,
 
};
 

	
 
void ShowJoinStatusWindow(void)
 
{
 
	Window *w;
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
	w = AllocateWindowDesc(&_network_join_status_window_desc);
 
	/* Parent the status window to the lobby */
 
	if (w != NULL) w->parent = FindWindowById(WC_NETWORK_WINDOW, 0);
 
}
 

	
 
static void SendChat(const char *buf, DestType type, byte dest)
 
{
 
	if (buf[0] == '\0') return;
 
	if (!_network_server) {
 
		SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT + type, type, dest, buf);
 
	} else {
 
		NetworkServer_HandleChat(NETWORK_ACTION_CHAT + type, type, dest, buf, NETWORK_SERVER_INDEX);
 
	}
 
}
 

	
 
/**
 
 * Find the next item of the list of things that can be auto-completed.
 
 * @param item The current indexed item to return. This function can, and most
 
 *     likely will, alter item, to skip empty items in the arrays.
 
 * @return Returns the char that matched to the index.
 
 */
 
static const char *ChatTabCompletionNextItem(uint *item)
 
{
 
	static char chat_tab_temp_buffer[64];
 

	
 
	/* First, try clients */
 
	if (*item < MAX_CLIENT_INFO) {
 
		/* Skip inactive clients */
 
		while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++;
 
		if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name;
 
	}
 

	
 
	/* Then, try townnames */
 
	/* Not that the following assumes all town indices are adjacent, ie no
 
	 * towns have been deleted. */
 
	if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) {
 
		const Town *t;
 

	
 
		FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
 
			/* Get the town-name via the string-system */
 
			SetDParam(0, t->townnameparts);
 
			GetString(chat_tab_temp_buffer, t->townnametype, lastof(chat_tab_temp_buffer));
 
			return &chat_tab_temp_buffer[0];
 
		}
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 * Find what text to complete. It scans for a space from the left and marks
 
 *  the word right from that as to complete. It also writes a \0 at the
 
 *  position of the space (if any). If nothing found, buf is returned.
 
 */
 
static char *ChatTabCompletionFindText(char *buf)
 
{
 
	char *p = strrchr(buf, ' ');
 
	if (p == NULL) return buf;
 

	
 
	*p = '\0';
 
	return p + 1;
 
}
 

	
 
/**
 
 * See if we can auto-complete the current text of the user.
 
 */
 
static void ChatTabCompletion(Window *w)
 
{
 
	static char _chat_tab_completion_buf[lengthof(_edit_str_buf)];
 
	Textbuf *tb = &WP(w, querystr_d).text;
 
	uint len, tb_len;
 
	uint item;
 
	char *tb_buf, *pre_buf;
 
	const char *cur_name;
 
	bool second_scan = false;
 

	
 
	item = 0;
 

	
 
	/* Copy the buffer so we can modify it without damaging the real data */
 
	pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf);
 

	
 
	tb_buf  = ChatTabCompletionFindText(pre_buf);
 
	tb_len  = strlen(tb_buf);
 

	
 
	while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) {
 
		item++;
 

	
 
		if (_chat_tab_completion_active) {
 
			/* We are pressing TAB again on the same name, is there an other name
 
			 *  that starts with this? */
 
			if (!second_scan) {
 
				uint offset;
 
				uint length;
 

	
 
				/* If we are completing at the begin of the line, skip the ': ' we added */
 
				if (tb_buf == pre_buf) {
 
					offset = 0;
 
					length = tb->length - 2;
 
				} else {
 
					/* Else, find the place we are completing at */
 
					offset = strlen(pre_buf) + 1;
 
					length = tb->length - offset;
 
				}
 

	
 
				/* Compare if we have a match */
 
				if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
 

	
 
				continue;
 
			}
 

	
 
			/* Now any match we make on _chat_tab_completion_buf after this, is perfect */
 
		}
 

	
 
		len = strlen(cur_name);
 
		if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) {
 
			/* Save the data it was before completion */
 
			if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf);
 
			_chat_tab_completion_active = true;
 

	
 
			/* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
 
			if (pre_buf == tb_buf) {
 
				snprintf(tb->buf, lengthof(_edit_str_buf), "%s: ", cur_name);
 
			} else {
 
				snprintf(tb->buf, lengthof(_edit_str_buf), "%s %s", pre_buf, cur_name);
 
			}
 

	
 
			/* Update the textbuffer */
 
			UpdateTextBufferSize(&WP(w, querystr_d).text);
 

	
 
			SetWindowDirty(w);
 
			free(pre_buf);
 
			return;
 
		}
 
	}
 

	
 
	if (second_scan) {
 
		/* We walked all posibilities, and the user presses tab again.. revert to original text */
 
		strcpy(tb->buf, _chat_tab_completion_buf);
 
		_chat_tab_completion_active = false;
 

	
 
		/* Update the textbuffer */
 
		UpdateTextBufferSize(&WP(w, querystr_d).text);
 

	
 
		SetWindowDirty(w);
 
	}
 
	free(pre_buf);
 
}
 

	
 
/* uses querystr_d WP macro
 
 * uses querystr_d->caption to store
 
 * - type of chat message (Private/Team/All) in bytes 0-7
 
 * - destination of chat message in the case of Team/Private in bytes 8-15 */
 
static void ChatWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE:
 
		SendWindowMessage(WC_NEWS_WINDOW, 0, WE_CREATE, w->height, 0);
 
		SETBIT(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys
 
		break;
 

	
 
	case WE_PAINT: {
 
		static const StringID chat_captions[] = {
 
			STR_NETWORK_CHAT_ALL_CAPTION,
 
			STR_NETWORK_CHAT_COMPANY_CAPTION,
 
			STR_NETWORK_CHAT_CLIENT_CAPTION
 
		};
 
		StringID msg;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		assert(GB(WP(w, querystr_d).caption, 0, 8) < lengthof(chat_captions));
 
		msg = chat_captions[GB(WP(w, querystr_d).caption, 0, 8)];
 
		DrawStringRightAligned(w->widget[2].left - 2, w->widget[2].top + 1, msg, 16);
 
		DrawEditBox(w, &WP(w, querystr_d), 2);
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
			case 3: { /* Send */
 
				DestType type = GB(WP(w, querystr_d).caption, 0, 8);
 
				byte dest = GB(WP(w, querystr_d).caption, 8, 8);
 
				SendChat(WP(w, querystr_d).text.buf, type, dest);
 
			} /* FALLTHROUGH */
 
			case 0: /* Cancel */ DeleteWindow(w); break;
 
		}
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		HandleEditBox(w, &WP(w, querystr_d), 2);
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		if (e->we.keypress.keycode == WKC_TAB) {
 
			ChatTabCompletion(w);
 
		} else {
 
			_chat_tab_completion_active = false;
 
			switch (HandleEditBoxKey(w, &WP(w, querystr_d), 2, e)) {
 
				case 1: { /* Return */
 
				DestType type = GB(WP(w, querystr_d).caption, 0, 8);
 
				byte dest = GB(WP(w, querystr_d).caption, 8, 8);
 
				SendChat(WP(w, querystr_d).text.buf, type, dest);
 
			} /* FALLTHROUGH */
 
				case 2: /* Escape */ DeleteWindow(w); break;
 
			}
 
		}
 
		break;
 

	
 
	case WE_DESTROY:
 
		SendWindowMessage(WC_NEWS_WINDOW, 0, WE_DESTROY, 0, 0);
 
		CLRBIT(_no_scroll, SCROLL_CHAT);
 
		break;
 
	}
 
}
 

	
 
static const Widget _chat_window_widgets[] = {
 
{   WWT_CLOSEBOX, RESIZE_NONE, 14,   0,  10,  0, 13, STR_00C5,         STR_018B_CLOSE_WINDOW},
 
{      WWT_PANEL, RESIZE_NONE, 14,  11, 639,  0, 13, 0x0,              STR_NULL}, // background
 
{      WWT_PANEL, RESIZE_NONE, 14,  75, 577,  1, 12, 0x0,              STR_NULL}, // text box
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 578, 639,  1, 12, STR_NETWORK_SEND, STR_NULL}, // send button
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _chat_window_desc = {
 
	WDP_CENTER, -26, 640, 14, // x, y, width, height
 
	WC_SEND_NETWORK_MSG,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
 
	_chat_window_widgets,
 
	ChatWindowWndProc
 
};
 

	
 
void ShowNetworkChatQueryWindow(DestType type, byte dest)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
 

	
 
	_edit_str_buf[0] = '\0';
 
	_chat_tab_completion_active = false;
 

	
 
	w = AllocateWindowDesc(&_chat_window_desc);
 

	
 
	LowerWindowWidget(w, 2);
 
	WP(w, querystr_d).caption = GB(type, 0, 8) | (dest << 8); // Misuse of caption
 
	WP(w, querystr_d).afilter = CS_ALPHANUMERAL;
 
	InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, lengthof(_edit_str_buf), 0);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/network_server.c
Show inline comments
 
deleted file
src/network/network_server.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h" // XXX StringID
 
#include "../debug.h"
 
#include "../string.h"
 
#include "../strings.h"
 
#include "network_data.h"
 
#include "core/tcp.h"
 
#include "../train.h"
 
#include "../date.h"
 
#include "table/strings.h"
 
#include "../functions.h"
 
#include "network_server.h"
 
#include "network_udp.h"
 
#include "../console.h"
 
#include "../command.h"
 
#include "../saveload.h"
 
#include "../vehicle.h"
 
#include "../station.h"
 
#include "../variables.h"
 
#include "../genworld.h"
 

	
 
// This file handles all the server-commands
 

	
 
static void NetworkHandleCommandQueue(NetworkClientState* cs);
 

	
 
// **********
 
// Sending functions
 
//   DEF_SERVER_SEND_COMMAND has parameter: NetworkClientState *cs
 
// **********
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_CLIENT_INFO)(NetworkClientState *cs, NetworkClientInfo *ci)
 
{
 
	//
 
	// Packet: SERVER_CLIENT_INFO
 
	// Function: Sends info about a client
 
	// Data:
 
	//    uint16:  The index of the client (always unique on a server. 1 = server)
 
	//    uint8:  As which player the client is playing
 
	//    String: The name of the client
 
	//    String: The unique id of the client
 
	//
 

	
 
	if (ci->client_index != NETWORK_EMPTY_INDEX) {
 
		Packet *p = NetworkSend_Init(PACKET_SERVER_CLIENT_INFO);
 
		NetworkSend_uint16(p, ci->client_index);
 
		NetworkSend_uint8 (p, ci->client_playas);
 
		NetworkSend_string(p, ci->client_name);
 
		NetworkSend_string(p, ci->unique_id);
 

	
 
		NetworkSend_Packet(p, cs);
 
	}
 
}
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_COMPANY_INFO)
 
{
 
//
 
	// Packet: SERVER_COMPANY_INFO
 
	// Function: Sends info about the companies
 
	// Data:
 
	//
 

	
 
	int i;
 

	
 
	Player *player;
 
	Packet *p;
 

	
 
	byte active = ActivePlayerCount();
 

	
 
	if (active == 0) {
 
		p = NetworkSend_Init(PACKET_SERVER_COMPANY_INFO);
 

	
 
		NetworkSend_uint8 (p, NETWORK_COMPANY_INFO_VERSION);
 
		NetworkSend_uint8 (p, active);
 

	
 
		NetworkSend_Packet(p, cs);
 
		return;
 
	}
 

	
 
	NetworkPopulateCompanyInfo();
 

	
 
	FOR_ALL_PLAYERS(player) {
 
		if (!player->is_active) continue;
 

	
 
		p = NetworkSend_Init(PACKET_SERVER_COMPANY_INFO);
 

	
 
		NetworkSend_uint8 (p, NETWORK_COMPANY_INFO_VERSION);
 
		NetworkSend_uint8 (p, active);
 
		NetworkSend_uint8 (p, player->index);
 

	
 
		NetworkSend_string(p, _network_player_info[player->index].company_name);
 
		NetworkSend_uint32(p, _network_player_info[player->index].inaugurated_year);
 
		NetworkSend_uint64(p, _network_player_info[player->index].company_value);
 
		NetworkSend_uint64(p, _network_player_info[player->index].money);
 
		NetworkSend_uint64(p, _network_player_info[player->index].income);
 
		NetworkSend_uint16(p, _network_player_info[player->index].performance);
 

	
 
		/* Send 1 if there is a passord for the company else send 0 */
 
		if (_network_player_info[player->index].password[0] != '\0') {
 
			NetworkSend_uint8(p, 1);
 
		} else {
 
			NetworkSend_uint8(p, 0);
 
		}
 

	
 
		for (i = 0; i < NETWORK_VEHICLE_TYPES; i++) {
 
			NetworkSend_uint16(p, _network_player_info[player->index].num_vehicle[i]);
 
		}
 

	
 
		for (i = 0; i < NETWORK_STATION_TYPES; i++) {
 
			NetworkSend_uint16(p, _network_player_info[player->index].num_station[i]);
 
		}
 

	
 
		if (_network_player_info[player->index].players[0] == '\0') {
 
			NetworkSend_string(p, "<none>");
 
		} else {
 
			NetworkSend_string(p, _network_player_info[player->index].players);
 
		}
 

	
 
		NetworkSend_Packet(p, cs);
 
	}
 

	
 
	p = NetworkSend_Init(PACKET_SERVER_COMPANY_INFO);
 

	
 
	NetworkSend_uint8 (p, NETWORK_COMPANY_INFO_VERSION);
 
	NetworkSend_uint8 (p, 0);
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR)(NetworkClientState *cs, NetworkErrorCode error)
 
{
 
	//
 
	// Packet: SERVER_ERROR
 
	// Function: The client made an error
 
	// Data:
 
	//    uint8:  ErrorID (see network_data.h, NetworkErrorCode)
 
	//
 

	
 
	char str[100];
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_ERROR);
 

	
 
	NetworkSend_uint8(p, error);
 
	NetworkSend_Packet(p, cs);
 

	
 
	GetNetworkErrorMsg(str, error, lastof(str));
 

	
 
	// Only send when the current client was in game
 
	if (cs->status > STATUS_AUTH) {
 
		NetworkClientState *new_cs;
 
		char client_name[NETWORK_CLIENT_NAME_LENGTH];
 

	
 
		NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
		DEBUG(net, 1, "'%s' made an error and has been disconnected. Reason: '%s'", client_name, str);
 

	
 
		NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str);
 

	
 
		FOR_ALL_CLIENTS(new_cs) {
 
			if (new_cs->status > STATUS_AUTH && new_cs != cs) {
 
				// Some errors we filter to a more general error. Clients don't have to know the real
 
				//  reason a joining failed.
 
				if (error == NETWORK_ERROR_NOT_AUTHORIZED || error == NETWORK_ERROR_NOT_EXPECTED || error == NETWORK_ERROR_WRONG_REVISION)
 
					error = NETWORK_ERROR_ILLEGAL_PACKET;
 

	
 
				SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->index, error);
 
			}
 
		}
 
	} else {
 
		DEBUG(net, 1, "Client %d made an error and has been disconnected. Reason: '%s'", cs->index, str);
 
	}
 

	
 
	cs->has_quit = true;
 

	
 
	// Make sure the data get's there before we close the connection
 
	NetworkSend_Packets(cs);
 

	
 
	// The client made a mistake, so drop his connection now!
 
	NetworkCloseClient(cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_NEED_PASSWORD)(NetworkClientState *cs, NetworkPasswordType type)
 
{
 
	//
 
	// Packet: SERVER_NEED_PASSWORD
 
	// Function: Indication to the client that the server needs a password
 
	// Data:
 
	//    uint8:  Type of password
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_NEED_PASSWORD);
 
	NetworkSend_uint8(p, type);
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_WELCOME)
 
{
 
	//
 
	// Packet: SERVER_WELCOME
 
	// Function: The client is joined and ready to receive his map
 
	// Data:
 
	//    uint16:  Own ClientID
 
	//
 

	
 
	Packet *p;
 
	const NetworkClientState *new_cs;
 

	
 
	// Invalid packet when status is AUTH or higher
 
	if (cs->status >= STATUS_AUTH) return;
 

	
 
	cs->status = STATUS_AUTH;
 
	_network_game_info.clients_on++;
 

	
 
	p = NetworkSend_Init(PACKET_SERVER_WELCOME);
 
	NetworkSend_uint16(p, cs->index);
 
	NetworkSend_Packet(p, cs);
 

	
 
		// Transmit info about all the active clients
 
	FOR_ALL_CLIENTS(new_cs) {
 
		if (new_cs != cs && new_cs->status > STATUS_AUTH)
 
			SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, DEREF_CLIENT_INFO(new_cs));
 
	}
 
	// Also send the info of the server
 
	SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX));
 
}
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_WAIT)
 
{
 
	//
 
	// Packet: PACKET_SERVER_WAIT
 
	// Function: The client can not receive the map at the moment because
 
	//             someone else is already receiving the map
 
	// Data:
 
	//    uint8:  Clients awaiting map
 
	//
 
	int waiting = 0;
 
	NetworkClientState *new_cs;
 
	Packet *p;
 

	
 
	// Count how many players are waiting in the queue
 
	FOR_ALL_CLIENTS(new_cs) {
 
		if (new_cs->status == STATUS_MAP_WAIT) waiting++;
 
	}
 

	
 
	p = NetworkSend_Init(PACKET_SERVER_WAIT);
 
	NetworkSend_uint8(p, waiting);
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
// This sends the map to the client
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_MAP)
 
{
 
	//
 
	// Packet: SERVER_MAP
 
	// Function: Sends the map to the client, or a part of it (it is splitted in
 
	//   a lot of multiple packets)
 
	// Data:
 
	//    uint8:  packet-type (MAP_PACKET_START, MAP_PACKET_NORMAL and MAP_PACKET_END)
 
	//  if MAP_PACKET_START:
 
	//    uint32: The current FrameCounter
 
	//  if MAP_PACKET_NORMAL:
 
	//    piece of the map (till max-size of packet)
 
	//  if MAP_PACKET_END:
 
	//    uint32: seed0 of player
 
	//    uint32: seed1 of player
 
	//      last 2 are repeated MAX_PLAYERS time
 
	//
 

	
 
	static FILE *file_pointer;
 
	static uint sent_packets; // How many packets we did send succecfully last time
 

	
 
	if (cs->status < STATUS_AUTH) {
 
		// Illegal call, return error and ignore the packet
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_AUTHORIZED);
 
		return;
 
	}
 

	
 
	if (cs->status == STATUS_AUTH) {
 
		char filename[256];
 
		Packet *p;
 

	
 
		// Make a dump of the current game
 
		snprintf(filename, lengthof(filename), "%s%snetwork_server.tmp",  _paths.autosave_dir, PATHSEP);
 
		if (SaveOrLoad(filename, SL_SAVE) != SL_OK) error("network savedump failed");
 

	
 
		file_pointer = fopen(filename, "rb");
 
		fseek(file_pointer, 0, SEEK_END);
 

	
 
		// Now send the _frame_counter and how many packets are coming
 
		p = NetworkSend_Init(PACKET_SERVER_MAP);
 
		NetworkSend_uint8(p, MAP_PACKET_START);
 
		NetworkSend_uint32(p, _frame_counter);
 
		NetworkSend_uint32(p, ftell(file_pointer));
 
		NetworkSend_Packet(p, cs);
 

	
 
		fseek(file_pointer, 0, SEEK_SET);
 

	
 
		sent_packets = 4; // We start with trying 4 packets
 

	
 
		cs->status = STATUS_MAP;
 
		/* Mark the start of download */
 
		cs->last_frame = _frame_counter;
 
		cs->last_frame_server = _frame_counter;
 
	}
 

	
 
	if (cs->status == STATUS_MAP) {
 
		uint i;
 
		int res;
 
		for (i = 0; i < sent_packets; i++) {
 
			Packet *p = NetworkSend_Init(PACKET_SERVER_MAP);
 
			NetworkSend_uint8(p, MAP_PACKET_NORMAL);
 
			res = (int)fread(p->buffer + p->size, 1, SEND_MTU - p->size, file_pointer);
 

	
 
			if (ferror(file_pointer)) error("Error reading temporary network savegame!");
 

	
 
			p->size += res;
 
			NetworkSend_Packet(p, cs);
 
			if (feof(file_pointer)) {
 
				// Done reading!
 
				Packet *p = NetworkSend_Init(PACKET_SERVER_MAP);
 
				NetworkSend_uint8(p, MAP_PACKET_END);
 
				NetworkSend_Packet(p, cs);
 

	
 
				// Set the status to DONE_MAP, no we will wait for the client
 
				//  to send it is ready (maybe that happens like never ;))
 
				cs->status = STATUS_DONE_MAP;
 
				fclose(file_pointer);
 

	
 
				{
 
					NetworkClientState *new_cs;
 
					bool new_map_client = false;
 
					// Check if there is a client waiting for receiving the map
 
					//  and start sending him the map
 
					FOR_ALL_CLIENTS(new_cs) {
 
						if (new_cs->status == STATUS_MAP_WAIT) {
 
							// Check if we already have a new client to send the map to
 
							if (!new_map_client) {
 
								// If not, this client will get the map
 
								new_cs->status = STATUS_AUTH;
 
								new_map_client = true;
 
								SEND_COMMAND(PACKET_SERVER_MAP)(new_cs);
 
							} else {
 
								// Else, send the other clients how many clients are in front of them
 
								SEND_COMMAND(PACKET_SERVER_WAIT)(new_cs);
 
							}
 
						}
 
					}
 
				}
 

	
 
				// There is no more data, so break the for
 
				break;
 
			}
 
		}
 

	
 
		// Send all packets (forced) and check if we have send it all
 
		NetworkSend_Packets(cs);
 
		if (cs->packet_queue == NULL) {
 
			// All are sent, increase the sent_packets
 
			sent_packets *= 2;
 
		} else {
 
			// Not everything is sent, decrease the sent_packets
 
			if (sent_packets > 1) sent_packets /= 2;
 
		}
 
	}
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_JOIN)(NetworkClientState *cs, uint16 client_index)
 
{
 
	//
 
	// Packet: SERVER_JOIN
 
	// Function: A client is joined (all active clients receive this after a
 
	//     PACKET_CLIENT_MAP_OK) Mostly what directly follows is a
 
	//     PACKET_SERVER_CLIENT_INFO
 
	// Data:
 
	//    uint16:  Client-Index
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_JOIN);
 

	
 
	NetworkSend_uint16(p, client_index);
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_FRAME)
 
{
 
	//
 
	// Packet: SERVER_FRAME
 
	// Function: Sends the current frame-counter to the client
 
	// Data:
 
	//    uint32: Frame Counter
 
	//    uint32: Frame Counter Max (how far may the client walk before the server?)
 
	//    [uint32: general-seed-1]
 
	//    [uint32: general-seed-2]
 
	//      (last two depends on compile-settings, and are not default settings)
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_FRAME);
 
	NetworkSend_uint32(p, _frame_counter);
 
	NetworkSend_uint32(p, _frame_counter_max);
 
#ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
	NetworkSend_uint32(p, _sync_seed_1);
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
	NetworkSend_uint32(p, _sync_seed_2);
 
#endif
 
#endif
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_SYNC)
 
{
 
	//
 
	// Packet: SERVER_SYNC
 
	// Function: Sends a sync-check to the client
 
	// Data:
 
	//    uint32: Frame Counter
 
	//    uint32: General-seed-1
 
	//    [uint32: general-seed-2]
 
	//      (last one depends on compile-settings, and are not default settings)
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_SYNC);
 
	NetworkSend_uint32(p, _frame_counter);
 
	NetworkSend_uint32(p, _sync_seed_1);
 

	
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
	NetworkSend_uint32(p, _sync_seed_2);
 
#endif
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_COMMAND)(NetworkClientState *cs, CommandPacket *cp)
 
{
 
	//
 
	// Packet: SERVER_COMMAND
 
	// Function: Sends a DoCommand to the client
 
	// Data:
 
	//    uint8:  PlayerID (0..MAX_PLAYERS-1)
 
	//    uint32: CommandID (see command.h)
 
	//    uint32: P1 (free variables used in DoCommand)
 
	//    uint32: P2
 
	//    uint32: Tile
 
	//    string: text
 
	//    uint8:  CallBackID (see callback_table.c)
 
	//    uint32: Frame of execution
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_COMMAND);
 

	
 
	NetworkSend_uint8(p, cp->player);
 
	NetworkSend_uint32(p, cp->cmd);
 
	NetworkSend_uint32(p, cp->p1);
 
	NetworkSend_uint32(p, cp->p2);
 
	NetworkSend_uint32(p, cp->tile);
 
	NetworkSend_string(p, cp->text);
 
	NetworkSend_uint8(p, cp->callback);
 
	NetworkSend_uint32(p, cp->frame);
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_CHAT)(NetworkClientState *cs, NetworkAction action, uint16 client_index, bool self_send, const char *msg)
 
{
 
	//
 
	// Packet: SERVER_CHAT
 
	// Function: Sends a chat-packet to the client
 
	// Data:
 
	//    uint8:  ActionID (see network_data.h, NetworkAction)
 
	//    uint16:  Client-index
 
	//    String: Message (max MAX_TEXT_MSG_LEN)
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_CHAT);
 

	
 
	NetworkSend_uint8(p, action);
 
	NetworkSend_uint16(p, client_index);
 
	NetworkSend_uint8(p, self_send);
 
	NetworkSend_string(p, msg);
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR_QUIT)(NetworkClientState *cs, uint16 client_index, NetworkErrorCode errorno)
 
{
 
	//
 
	// Packet: SERVER_ERROR_QUIT
 
	// Function: One of the clients made an error and is quiting the game
 
	//      This packet informs the other clients of that.
 
	// Data:
 
	//    uint16:  Client-index
 
	//    uint8:  ErrorID (see network_data.h, NetworkErrorCode)
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_ERROR_QUIT);
 

	
 
	NetworkSend_uint16(p, client_index);
 
	NetworkSend_uint8(p, errorno);
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_QUIT)(NetworkClientState *cs, uint16 client_index, const char *leavemsg)
 
{
 
	//
 
	// Packet: SERVER_ERROR_QUIT
 
	// Function: A client left the game, and this packets informs the other clients
 
	//      of that.
 
	// Data:
 
	//    uint16:  Client-index
 
	//    String: leave-message
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_QUIT);
 

	
 
	NetworkSend_uint16(p, client_index);
 
	NetworkSend_string(p, leavemsg);
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_SHUTDOWN)
 
{
 
	//
 
	// Packet: SERVER_SHUTDOWN
 
	// Function: Let the clients know that the server is closing
 
	// Data:
 
	//     <none>
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_SHUTDOWN);
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_NEWGAME)
 
{
 
	//
 
	// Packet: PACKET_SERVER_NEWGAME
 
	// Function: Let the clients know that the server is loading a new map
 
	// Data:
 
	//     <none>
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_NEWGAME);
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_RCON)(NetworkClientState *cs, uint16 color, const char *command)
 
{
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_RCON);
 

	
 
	NetworkSend_uint16(p, color);
 
	NetworkSend_string(p, command);
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
// **********
 
// Receiving functions
 
//   DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkClientState *cs, Packet *p
 
// **********
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMPANY_INFO)
 
{
 
	SEND_COMMAND(PACKET_SERVER_COMPANY_INFO)(cs);
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_JOIN)
 
{
 
	char name[NETWORK_CLIENT_NAME_LENGTH];
 
	char unique_id[NETWORK_NAME_LENGTH];
 
	NetworkClientInfo *ci;
 
	byte playas;
 
	NetworkLanguage client_lang;
 
	char client_revision[NETWORK_REVISION_LENGTH];
 

	
 
	NetworkRecv_string(cs, p, client_revision, sizeof(client_revision));
 

	
 
#if defined(WITH_REV) || defined(WITH_REV_HACK)
 
	// Check if the client has revision control enabled
 
	if (strcmp(NOREV_STRING, client_revision) != 0 &&
 
			strcmp(_network_game_info.server_revision, client_revision) != 0) {
 
		// Different revisions!!
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_WRONG_REVISION);
 
		return;
 
	}
 
#endif
 

	
 
	NetworkRecv_string(cs, p, name, sizeof(name));
 
	playas = NetworkRecv_uint8(cs, p);
 
	client_lang = NetworkRecv_uint8(cs, p);
 
	NetworkRecv_string(cs, p, unique_id, sizeof(unique_id));
 

	
 
	if (cs->has_quit) return;
 

	
 
	// join another company does not affect these values
 
	switch (playas) {
 
		case PLAYER_NEW_COMPANY: /* New company */
 
			if (ActivePlayerCount() >= _network_game_info.companies_max) {
 
				SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_FULL);
 
				return;
 
			}
 
			break;
 
		case PLAYER_SPECTATOR: /* Spectator */
 
			if (NetworkSpectatorCount() >= _network_game_info.spectators_max) {
 
				SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_FULL);
 
				return;
 
			}
 
			break;
 
		default: /* Join another company (companies 1-8 (index 0-7)) */
 
			if (!IsValidPlayer(playas)) {
 
				SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_PLAYER_MISMATCH);
 
				return;
 
			}
 
			break;
 
	}
 

	
 
	// We need a valid name.. make it Player
 
	if (*name == '\0') ttd_strlcpy(name, "Player", sizeof(name));
 

	
 
	if (!NetworkFindName(name)) { // Change name if duplicate
 
		// We could not create a name for this player
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NAME_IN_USE);
 
		return;
 
	}
 

	
 
	ci = DEREF_CLIENT_INFO(cs);
 

	
 
	ttd_strlcpy(ci->client_name, name, sizeof(ci->client_name));
 
	ttd_strlcpy(ci->unique_id, unique_id, sizeof(ci->unique_id));
 
	ci->client_playas = playas;
 
	ci->client_lang = client_lang;
 

	
 
	// We now want a password from the client
 
	//  else we do not allow him in!
 
	if (_network_game_info.use_password) {
 
		SEND_COMMAND(PACKET_SERVER_NEED_PASSWORD)(cs, NETWORK_GAME_PASSWORD);
 
	} else {
 
		if (IsValidPlayer(ci->client_playas) && _network_player_info[ci->client_playas].password[0] != '\0') {
 
			SEND_COMMAND(PACKET_SERVER_NEED_PASSWORD)(cs, NETWORK_COMPANY_PASSWORD);
 
		} else {
 
			SEND_COMMAND(PACKET_SERVER_WELCOME)(cs);
 
		}
 
	}
 

	
 
	/* Make sure companies to which people try to join are not autocleaned */
 
	if (IsValidPlayer(playas)) _network_player_info[playas].months_empty = 0;
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_PASSWORD)
 
{
 
	NetworkPasswordType type;
 
	char password[NETWORK_PASSWORD_LENGTH];
 
	const NetworkClientInfo *ci;
 

	
 
	type = NetworkRecv_uint8(cs, p);
 
	NetworkRecv_string(cs, p, password, sizeof(password));
 

	
 
	if (cs->status == STATUS_INACTIVE && type == NETWORK_GAME_PASSWORD) {
 
		// Check game-password
 
		if (strcmp(password, _network_game_info.server_password) != 0) {
 
			// Password is invalid
 
			SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_WRONG_PASSWORD);
 
			return;
 
		}
 

	
 
		ci = DEREF_CLIENT_INFO(cs);
 

	
 
		if (IsValidPlayer(ci->client_playas) && _network_player_info[ci->client_playas].password[0] != '\0') {
 
			SEND_COMMAND(PACKET_SERVER_NEED_PASSWORD)(cs, NETWORK_COMPANY_PASSWORD);
 
			return;
 
		}
 

	
 
		// Valid password, allow user
 
		SEND_COMMAND(PACKET_SERVER_WELCOME)(cs);
 
		return;
 
	} else if (cs->status == STATUS_INACTIVE && type == NETWORK_COMPANY_PASSWORD) {
 
		ci = DEREF_CLIENT_INFO(cs);
 

	
 
		if (strcmp(password, _network_player_info[ci->client_playas].password) != 0) {
 
			// Password is invalid
 
			SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_WRONG_PASSWORD);
 
			return;
 
		}
 

	
 
		SEND_COMMAND(PACKET_SERVER_WELCOME)(cs);
 
		return;
 
	}
 

	
 

	
 
	SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
	return;
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_GETMAP)
 
{
 
	const NetworkClientState *new_cs;
 

	
 
	// The client was never joined.. so this is impossible, right?
 
	//  Ignore the packet, give the client a warning, and close his connection
 
	if (cs->status < STATUS_AUTH || cs->has_quit) {
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_AUTHORIZED);
 
		return;
 
	}
 

	
 
	// Check if someone else is receiving the map
 
	FOR_ALL_CLIENTS(new_cs) {
 
		if (new_cs->status == STATUS_MAP) {
 
			// Tell the new client to wait
 
			cs->status = STATUS_MAP_WAIT;
 
			SEND_COMMAND(PACKET_SERVER_WAIT)(cs);
 
			return;
 
		}
 
	}
 

	
 
	// We receive a request to upload the map.. give it to the client!
 
	SEND_COMMAND(PACKET_SERVER_MAP)(cs);
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_MAP_OK)
 
{
 
	// Client has the map, now start syncing
 
	if (cs->status == STATUS_DONE_MAP && !cs->has_quit) {
 
		char client_name[NETWORK_CLIENT_NAME_LENGTH];
 
		NetworkClientState *new_cs;
 

	
 
		NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
		NetworkTextMessage(NETWORK_ACTION_JOIN, 1, false, client_name, "");
 

	
 
		// Mark the client as pre-active, and wait for an ACK
 
		//  so we know he is done loading and in sync with us
 
		cs->status = STATUS_PRE_ACTIVE;
 
		NetworkHandleCommandQueue(cs);
 
		SEND_COMMAND(PACKET_SERVER_FRAME)(cs);
 
		SEND_COMMAND(PACKET_SERVER_SYNC)(cs);
 

	
 
		// This is the frame the client receives
 
		//  we need it later on to make sure the client is not too slow
 
		cs->last_frame = _frame_counter;
 
		cs->last_frame_server = _frame_counter;
 

	
 
		FOR_ALL_CLIENTS(new_cs) {
 
			if (new_cs->status > STATUS_AUTH) {
 
				SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(new_cs, DEREF_CLIENT_INFO(cs));
 
				SEND_COMMAND(PACKET_SERVER_JOIN)(new_cs, cs->index);
 
			}
 
		}
 

	
 
		if (_network_pause_on_join) {
 
			/* Now pause the game till the client is in sync */
 
			DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
 

	
 
			NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game paused (incoming client)", NETWORK_SERVER_INDEX);
 
		}
 
	} else {
 
		// Wrong status for this packet, give a warning to client, and close connection
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
	}
 
}
 

	
 
/** Enforce the command flags.
 
 * Eg a server-only command can only be executed by a server, etc.
 
 * @param *cp the commandpacket that is going to be checked
 
 * @param *ci client information for debugging output to console
 
 */
 
static bool CheckCommandFlags(const CommandPacket *cp, const NetworkClientInfo *ci)
 
{
 
	byte flags = GetCommandFlags(cp->cmd);
 

	
 
	if (flags & CMD_SERVER && ci->client_index != NETWORK_SERVER_INDEX) {
 
		IConsolePrintF(_icolour_err, "WARNING: server only command from client %d (IP: %s), kicking...", ci->client_index, GetPlayerIP(ci));
 
		return false;
 
	}
 

	
 
	if (flags & CMD_OFFLINE) {
 
		IConsolePrintF(_icolour_err, "WARNING: offline only command from client %d (IP: %s), kicking...", ci->client_index, GetPlayerIP(ci));
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/** The client has done a command and wants us to handle it
 
 * @param *cs the connected client that has sent the command
 
 * @param *p the packet in which the command was sent
 
 */
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND)
 
{
 
	NetworkClientState *new_cs;
 
	const NetworkClientInfo *ci;
 
	byte callback;
 

	
 
	CommandPacket *cp = malloc(sizeof(CommandPacket));
 

	
 
	// The client was never joined.. so this is impossible, right?
 
	//  Ignore the packet, give the client a warning, and close his connection
 
	if (cs->status < STATUS_DONE_MAP || cs->has_quit) {
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
		return;
 
	}
 

	
 
	cp->player = NetworkRecv_uint8(cs, p);
 
	cp->cmd    = NetworkRecv_uint32(cs, p);
 
	cp->p1     = NetworkRecv_uint32(cs, p);
 
	cp->p2     = NetworkRecv_uint32(cs, p);
 
	cp->tile   = NetworkRecv_uint32(cs, p);
 
	NetworkRecv_string(cs, p, cp->text, lengthof(cp->text));
 

	
 
	callback = NetworkRecv_uint8(cs, p);
 

	
 
	if (cs->has_quit) return;
 

	
 
	ci = DEREF_CLIENT_INFO(cs);
 

	
 
	/* Check if cp->cmd is valid */
 
	if (!IsValidCommand(cp->cmd)) {
 
		IConsolePrintF(_icolour_err, "WARNING: invalid command from client %d (IP: %s).", ci->client_index, GetPlayerIP(ci));
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
		return;
 
	}
 

	
 
	if (!CheckCommandFlags(cp, ci)) {
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_KICKED);
 
		return;
 
	}
 

	
 
	/** Only CMD_PLAYER_CTRL is always allowed, for the rest, playas needs
 
	 * to match the player in the packet. If it doesn't, the client has done
 
	 * something pretty naughty (or a bug), and will be kicked
 
	 */
 
	if (!(cp->cmd == CMD_PLAYER_CTRL && cp->p1 == 0) && ci->client_playas != cp->player) {
 
		IConsolePrintF(_icolour_err, "WARNING: player %d (IP: %s) tried to execute a command as player %d, kicking...",
 
		               ci->client_playas + 1, GetPlayerIP(ci), cp->player + 1);
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_PLAYER_MISMATCH);
 
		return;
 
	}
 

	
 
	/** @todo CMD_PLAYER_CTRL with p1 = 0 announces a new player to the server. To give the
 
	 * player the correct ID, the server injects p2 and executes the command. Any other p1
 
	 * is prohibited. Pretty ugly and should be redone together with its function.
 
	 * @see CmdPlayerCtrl() players.c:655
 
	 */
 
	if (cp->cmd == CMD_PLAYER_CTRL) {
 
		if (cp->p1 != 0) {
 
			SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_CHEATER);
 
			return;
 
		}
 

	
 
		/* XXX - Execute the command as a valid player. Normally this would be done by a
 
		 * spectator, but that is not allowed any commands. So do an impersonation. The drawback
 
		 * of this is that the first company's last_built_tile is also updated... */
 
		cp->player = 0;
 
		cp->p2 = cs - _clients; // XXX - UGLY! p2 is mis-used to get the client-id in CmdPlayerCtrl
 
	}
 

	
 
	// The frame can be executed in the same frame as the next frame-packet
 
	//  That frame just before that frame is saved in _frame_counter_max
 
	cp->frame = _frame_counter_max + 1;
 
	cp->next  = NULL;
 

	
 
	// Queue the command for the clients (are send at the end of the frame
 
	//   if they can handle it ;))
 
	FOR_ALL_CLIENTS(new_cs) {
 
		if (new_cs->status >= STATUS_MAP) {
 
			// Callbacks are only send back to the client who sent them in the
 
			//  first place. This filters that out.
 
			cp->callback = (new_cs != cs) ? 0 : callback;
 
			NetworkAddCommandQueue(new_cs, cp);
 
		}
 
	}
 

	
 
	cp->callback = 0;
 
	// Queue the command on the server
 
	if (_local_command_queue == NULL) {
 
		_local_command_queue = cp;
 
	} else {
 
		// Find last packet
 
		CommandPacket *c = _local_command_queue;
 
		while (c->next != NULL) c = c->next;
 
		c->next = cp;
 
	}
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_ERROR)
 
{
 
	// This packets means a client noticed an error and is reporting this
 
	//  to us. Display the error and report it to the other clients
 
	NetworkClientState *new_cs;
 
	char str[100];
 
	char client_name[NETWORK_CLIENT_NAME_LENGTH];
 
	NetworkErrorCode errorno = NetworkRecv_uint8(cs, p);
 

	
 
	// The client was never joined.. thank the client for the packet, but ignore it
 
	if (cs->status < STATUS_DONE_MAP || cs->has_quit) {
 
		cs->has_quit = true;
 
		return;
 
	}
 

	
 
	NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
	GetNetworkErrorMsg(str, errorno, lastof(str));
 

	
 
	DEBUG(net, 2, "'%s' reported an error and is closing its connection (%s)", client_name, str);
 

	
 
	NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str);
 

	
 
	FOR_ALL_CLIENTS(new_cs) {
 
		if (new_cs->status > STATUS_AUTH) {
 
			SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->index, errorno);
 
		}
 
	}
 

	
 
	cs->has_quit = true;
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_QUIT)
 
{
 
	// The client wants to leave. Display this and report it to the other
 
	//  clients.
 
	NetworkClientState *new_cs;
 
	char str[100];
 
	char client_name[NETWORK_CLIENT_NAME_LENGTH];
 

	
 
	// The client was never joined.. thank the client for the packet, but ignore it
 
	if (cs->status < STATUS_DONE_MAP || cs->has_quit) {
 
		cs->has_quit = true;
 
		return;
 
	}
 

	
 
	NetworkRecv_string(cs, p, str, lengthof(str));
 

	
 
	NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
	NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str);
 

	
 
	FOR_ALL_CLIENTS(new_cs) {
 
		if (new_cs->status > STATUS_AUTH) {
 
			SEND_COMMAND(PACKET_SERVER_QUIT)(new_cs, cs->index, str);
 
		}
 
	}
 

	
 
	cs->has_quit = true;
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_ACK)
 
{
 
	uint32 frame = NetworkRecv_uint32(cs, p);
 

	
 
	/* The client is trying to catch up with the server */
 
	if (cs->status == STATUS_PRE_ACTIVE) {
 
		/* The client is not yet catched up? */
 
		if (frame + DAY_TICKS < _frame_counter) return;
 

	
 
		/* Now he is! Unpause the game */
 
		cs->status = STATUS_ACTIVE;
 

	
 
		if (_network_pause_on_join) {
 
			DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
 
			NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused (client connected)", NETWORK_SERVER_INDEX);
 
		}
 

	
 
		CheckMinPlayers();
 

	
 
		/* Execute script for, e.g. MOTD */
 
		IConsoleCmdExec("exec scripts/on_server_connect.scr 0");
 
	}
 

	
 
	// The client received the frame, make note of it
 
	cs->last_frame = frame;
 
	// With those 2 values we can calculate the lag realtime
 
	cs->last_frame_server = _frame_counter;
 
}
 

	
 

	
 

	
 
void NetworkServer_HandleChat(NetworkAction action, DestType desttype, int dest, const char *msg, uint16 from_index)
 
{
 
	NetworkClientState *cs;
 
	const NetworkClientInfo *ci, *ci_own, *ci_to;
 

	
 
	switch (desttype) {
 
	case DESTTYPE_CLIENT:
 
		/* Are we sending to the server? */
 
		if (dest == NETWORK_SERVER_INDEX) {
 
			ci = NetworkFindClientInfoFromIndex(from_index);
 
			/* Display the text locally, and that is it */
 
			if (ci != NULL)
 
				NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas), false, ci->client_name, "%s", msg);
 
		} else {
 
			/* Else find the client to send the message to */
 
			FOR_ALL_CLIENTS(cs) {
 
				if (cs->index == dest) {
 
					SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, from_index, false, msg);
 
					break;
 
				}
 
			}
 
		}
 

	
 
		// Display the message locally (so you know you have sent it)
 
		if (from_index != dest) {
 
			if (from_index == NETWORK_SERVER_INDEX) {
 
				ci = NetworkFindClientInfoFromIndex(from_index);
 
				ci_to = NetworkFindClientInfoFromIndex(dest);
 
				if (ci != NULL && ci_to != NULL)
 
					NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas), true, ci_to->client_name, "%s", msg);
 
			} else {
 
				FOR_ALL_CLIENTS(cs) {
 
					if (cs->index == from_index) {
 
						SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, dest, true, msg);
 
						break;
 
					}
 
				}
 
			}
 
		}
 
		break;
 
	case DESTTYPE_TEAM: {
 
		bool show_local = true; // If this is false, the message is already displayed
 
														// on the client who did sent it.
 
		/* Find all clients that belong to this player */
 
		ci_to = NULL;
 
		FOR_ALL_CLIENTS(cs) {
 
			ci = DEREF_CLIENT_INFO(cs);
 
			if (ci->client_playas == dest) {
 
				SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, from_index, false, msg);
 
				if (cs->index == from_index) show_local = false;
 
				ci_to = ci; // Remember a client that is in the company for company-name
 
			}
 
		}
 

	
 
		ci = NetworkFindClientInfoFromIndex(from_index);
 
		ci_own = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 
		if (ci != NULL && ci_own != NULL && ci_own->client_playas == dest) {
 
			NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas), false, ci->client_name, "%s", msg);
 
			if (from_index == NETWORK_SERVER_INDEX) show_local = false;
 
			ci_to = ci_own;
 
		}
 

	
 
		/* There is no such player */
 
		if (ci_to == NULL) break;
 

	
 
		// Display the message locally (so you know you have sent it)
 
		if (ci != NULL && show_local) {
 
			if (from_index == NETWORK_SERVER_INDEX) {
 
				char name[NETWORK_NAME_LENGTH];
 
				StringID str = IsValidPlayer(ci_to->client_playas) ? GetPlayer(ci_to->client_playas)->name_1 : STR_NETWORK_SPECTATORS;
 
				GetString(name, str, lastof(name));
 
				NetworkTextMessage(action, GetDrawStringPlayerColor(ci_own->client_playas), true, name, "%s", msg);
 
			} else {
 
				FOR_ALL_CLIENTS(cs) {
 
					if (cs->index == from_index) {
 
						SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, ci_to->client_index, true, msg);
 
					}
 
				}
 
			}
 
		}
 
		}
 
		break;
 
	default:
 
		DEBUG(net, 0, "[server] received unknown chat destination type %d. Doing broadcast instead", desttype);
 
		/* fall-through to next case */
 
	case DESTTYPE_BROADCAST:
 
		FOR_ALL_CLIENTS(cs) {
 
			SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, from_index, false, msg);
 
		}
 
		ci = NetworkFindClientInfoFromIndex(from_index);
 
		if (ci != NULL)
 
			NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas), false, ci->client_name, "%s", msg);
 
		break;
 
	}
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_CHAT)
 
{
 
	NetworkAction action = NetworkRecv_uint8(cs, p);
 
	DestType desttype = NetworkRecv_uint8(cs, p);
 
	int dest = NetworkRecv_uint8(cs, p);
 
	char msg[MAX_TEXT_MSG_LEN];
 

	
 
	NetworkRecv_string(cs, p, msg, MAX_TEXT_MSG_LEN);
 

	
 
	NetworkServer_HandleChat(action, desttype, dest, msg, cs->index);
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_SET_PASSWORD)
 
{
 
	char password[NETWORK_PASSWORD_LENGTH];
 
	const NetworkClientInfo *ci;
 

	
 
	NetworkRecv_string(cs, p, password, sizeof(password));
 
	ci = DEREF_CLIENT_INFO(cs);
 

	
 
	if (IsValidPlayer(ci->client_playas)) {
 
		ttd_strlcpy(_network_player_info[ci->client_playas].password, password, sizeof(_network_player_info[0].password));
 
	}
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_SET_NAME)
 
{
 
	char client_name[NETWORK_CLIENT_NAME_LENGTH];
 
	NetworkClientInfo *ci;
 

	
 
	NetworkRecv_string(cs, p, client_name, sizeof(client_name));
 
	ci = DEREF_CLIENT_INFO(cs);
 

	
 
	if (cs->has_quit) return;
 

	
 
	if (ci != NULL) {
 
		// Display change
 
		if (NetworkFindName(client_name)) {
 
			NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, 1, false, ci->client_name, "%s", client_name);
 
			ttd_strlcpy(ci->client_name, client_name, sizeof(ci->client_name));
 
			NetworkUpdateClientInfo(ci->client_index);
 
		}
 
	}
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_RCON)
 
{
 
	char pass[NETWORK_PASSWORD_LENGTH];
 
	char command[NETWORK_RCONCOMMAND_LENGTH];
 

	
 
	if (_network_game_info.rcon_password[0] == '\0') return;
 

	
 
	NetworkRecv_string(cs, p, pass, sizeof(pass));
 
	NetworkRecv_string(cs, p, command, sizeof(command));
 

	
 
	if (strcmp(pass, _network_game_info.rcon_password) != 0) {
 
		DEBUG(net, 0, "[rcon] wrong password from client-id %d", cs->index);
 
		return;
 
	}
 

	
 
	DEBUG(net, 0, "[rcon] client-id %d executed: '%s'", cs->index, command);
 

	
 
	_redirect_console_to_client = cs->index;
 
	IConsoleCmdExec(command);
 
	_redirect_console_to_client = 0;
 
	return;
 
}
 

	
 
// The layout for the receive-functions by the server
 
typedef void NetworkServerPacket(NetworkClientState *cs, Packet *p);
 

	
 

	
 
// This array matches PacketType. At an incoming
 
//  packet it is matches against this array
 
//  and that way the right function to handle that
 
//  packet is found.
 
static NetworkServerPacket* const _network_server_packet[] = {
 
	NULL, /*PACKET_SERVER_FULL,*/
 
	NULL, /*PACKET_SERVER_BANNED,*/
 
	RECEIVE_COMMAND(PACKET_CLIENT_JOIN),
 
	NULL, /*PACKET_SERVER_ERROR,*/
 
	RECEIVE_COMMAND(PACKET_CLIENT_COMPANY_INFO),
 
	NULL, /*PACKET_SERVER_COMPANY_INFO,*/
 
	NULL, /*PACKET_SERVER_CLIENT_INFO,*/
 
	NULL, /*PACKET_SERVER_NEED_PASSWORD,*/
 
	RECEIVE_COMMAND(PACKET_CLIENT_PASSWORD),
 
	NULL, /*PACKET_SERVER_WELCOME,*/
 
	RECEIVE_COMMAND(PACKET_CLIENT_GETMAP),
 
	NULL, /*PACKET_SERVER_WAIT,*/
 
	NULL, /*PACKET_SERVER_MAP,*/
 
	RECEIVE_COMMAND(PACKET_CLIENT_MAP_OK),
 
	NULL, /*PACKET_SERVER_JOIN,*/
 
	NULL, /*PACKET_SERVER_FRAME,*/
 
	NULL, /*PACKET_SERVER_SYNC,*/
 
	RECEIVE_COMMAND(PACKET_CLIENT_ACK),
 
	RECEIVE_COMMAND(PACKET_CLIENT_COMMAND),
 
	NULL, /*PACKET_SERVER_COMMAND,*/
 
	RECEIVE_COMMAND(PACKET_CLIENT_CHAT),
 
	NULL, /*PACKET_SERVER_CHAT,*/
 
	RECEIVE_COMMAND(PACKET_CLIENT_SET_PASSWORD),
 
	RECEIVE_COMMAND(PACKET_CLIENT_SET_NAME),
 
	RECEIVE_COMMAND(PACKET_CLIENT_QUIT),
 
	RECEIVE_COMMAND(PACKET_CLIENT_ERROR),
 
	NULL, /*PACKET_SERVER_QUIT,*/
 
	NULL, /*PACKET_SERVER_ERROR_QUIT,*/
 
	NULL, /*PACKET_SERVER_SHUTDOWN,*/
 
	NULL, /*PACKET_SERVER_NEWGAME,*/
 
	NULL, /*PACKET_SERVER_RCON,*/
 
	RECEIVE_COMMAND(PACKET_CLIENT_RCON),
 
};
 

	
 
// If this fails, check the array above with network_data.h
 
assert_compile(lengthof(_network_server_packet) == PACKET_END);
 

	
 
// This update the company_info-stuff
 
void NetworkPopulateCompanyInfo(void)
 
{
 
	char password[NETWORK_PASSWORD_LENGTH];
 
	const Player *p;
 
	const Vehicle *v;
 
	const Station *s;
 
	const NetworkClientState *cs;
 
	const NetworkClientInfo *ci;
 
	uint i;
 
	uint16 months_empty;
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		if (!p->is_active) {
 
			memset(&_network_player_info[p->index], 0, sizeof(NetworkPlayerInfo));
 
			continue;
 
		}
 

	
 
		// Clean the info but not the password
 
		ttd_strlcpy(password, _network_player_info[p->index].password, sizeof(password));
 
		months_empty = _network_player_info[p->index].months_empty;
 
		memset(&_network_player_info[p->index], 0, sizeof(NetworkPlayerInfo));
 
		_network_player_info[p->index].months_empty = months_empty;
 
		ttd_strlcpy(_network_player_info[p->index].password, password, sizeof(_network_player_info[p->index].password));
 

	
 
		// Grap the company name
 
		SetDParam(0, p->name_1);
 
		SetDParam(1, p->name_2);
 
		GetString(_network_player_info[p->index].company_name, STR_JUST_STRING, lastof(_network_player_info[p->index].company_name));
 

	
 
		// Check the income
 
		if (_cur_year - 1 == p->inaugurated_year) {
 
			// The player is here just 1 year, so display [2], else display[1]
 
			for (i = 0; i < lengthof(p->yearly_expenses[2]); i++) {
 
				_network_player_info[p->index].income -= p->yearly_expenses[2][i];
 
			}
 
		} else {
 
			for (i = 0; i < lengthof(p->yearly_expenses[1]); i++) {
 
				_network_player_info[p->index].income -= p->yearly_expenses[1][i];
 
			}
 
		}
 

	
 
		// Set some general stuff
 
		_network_player_info[p->index].inaugurated_year = p->inaugurated_year;
 
		_network_player_info[p->index].company_value = p->old_economy[0].company_value;
 
		_network_player_info[p->index].money = p->money64;
 
		_network_player_info[p->index].performance = p->old_economy[0].performance_history;
 
	}
 

	
 
	// Go through all vehicles and count the type of vehicles
 
	FOR_ALL_VEHICLES(v) {
 
		if (!IsValidPlayer(v->owner)) continue;
 

	
 
		switch (v->type) {
 
			case VEH_Train:
 
				if (IsFrontEngine(v)) _network_player_info[v->owner].num_vehicle[0]++;
 
				break;
 

	
 
			case VEH_Road:
 
				if (v->cargo_type != CT_PASSENGERS) {
 
					_network_player_info[v->owner].num_vehicle[1]++;
 
				} else {
 
					_network_player_info[v->owner].num_vehicle[2]++;
 
				}
 
				break;
 

	
 
			case VEH_Aircraft:
 
				if (v->subtype <= 2) _network_player_info[v->owner].num_vehicle[3]++;
 
				break;
 

	
 
			case VEH_Ship:
 
				_network_player_info[v->owner].num_vehicle[4]++;
 
				break;
 

	
 
			case VEH_Special:
 
			case VEH_Disaster:
 
				break;
 
		}
 
	}
 

	
 
	// Go through all stations and count the types of stations
 
	FOR_ALL_STATIONS(s) {
 
		if (IsValidPlayer(s->owner)) {
 
			NetworkPlayerInfo *npi = &_network_player_info[s->owner];
 

	
 
			if (s->facilities & FACIL_TRAIN)      npi->num_station[0]++;
 
			if (s->facilities & FACIL_TRUCK_STOP) npi->num_station[1]++;
 
			if (s->facilities & FACIL_BUS_STOP)   npi->num_station[2]++;
 
			if (s->facilities & FACIL_AIRPORT)    npi->num_station[3]++;
 
			if (s->facilities & FACIL_DOCK)       npi->num_station[4]++;
 
		}
 
	}
 

	
 
	ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 
	// Register local player (if not dedicated)
 
	if (ci != NULL && IsValidPlayer(ci->client_playas))
 
		ttd_strlcpy(_network_player_info[ci->client_playas].players, ci->client_name, sizeof(_network_player_info[0].players));
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		char client_name[NETWORK_CLIENT_NAME_LENGTH];
 

	
 
		NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
		ci = DEREF_CLIENT_INFO(cs);
 
		if (ci != NULL && IsValidPlayer(ci->client_playas)) {
 
			if (strlen(_network_player_info[ci->client_playas].players) != 0)
 
				ttd_strlcat(_network_player_info[ci->client_playas].players, ", ", lengthof(_network_player_info[0].players));
 

	
 
			ttd_strlcat(_network_player_info[ci->client_playas].players, client_name, lengthof(_network_player_info[0].players));
 
		}
 
	}
 
}
 

	
 
// Send a packet to all clients with updated info about this client_index
 
void NetworkUpdateClientInfo(uint16 client_index)
 
{
 
	NetworkClientState *cs;
 
	NetworkClientInfo *ci = NetworkFindClientInfoFromIndex(client_index);
 

	
 
	if (ci == NULL) return;
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, ci);
 
	}
 
}
 

	
 
/* Check if we want to restart the map */
 
static void NetworkCheckRestartMap(void)
 
{
 
	if (_network_restart_game_year != 0 && _cur_year >= _network_restart_game_year) {
 
		DEBUG(net, 0, "Auto-restarting map. Year %d reached", _cur_year);
 

	
 
		StartNewGameWithoutGUI(GENERATE_NEW_SEED);
 
	}
 
}
 

	
 
/* Check if the server has autoclean_companies activated
 
    Two things happen:
 
      1) If a company is not protected, it is closed after 1 year (for example)
 
      2) If a company is protected, protection is disabled after 3 years (for example)
 
           (and item 1. happens a year later) */
 
static void NetworkAutoCleanCompanies(void)
 
{
 
	const NetworkClientState *cs;
 
	const NetworkClientInfo *ci;
 
	const Player *p;
 
	bool clients_in_company[MAX_PLAYERS];
 

	
 
	if (!_network_autoclean_companies) return;
 

	
 
	memset(clients_in_company, 0, sizeof(clients_in_company));
 

	
 
	/* Detect the active companies */
 
	FOR_ALL_CLIENTS(cs) {
 
		ci = DEREF_CLIENT_INFO(cs);
 
		if (IsValidPlayer(ci->client_playas)) clients_in_company[ci->client_playas] = true;
 
	}
 

	
 
	if (!_network_dedicated) {
 
		ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 
		if (IsValidPlayer(ci->client_playas)) clients_in_company[ci->client_playas] = true;
 
	}
 

	
 
	/* Go through all the comapnies */
 
	FOR_ALL_PLAYERS(p) {
 
		/* Skip the non-active once */
 
		if (!p->is_active || p->is_ai) continue;
 

	
 
		if (!clients_in_company[p->index]) {
 
			/* The company is empty for one month more */
 
			_network_player_info[p->index].months_empty++;
 

	
 
			/* Is the company empty for autoclean_unprotected-months, and is there no protection? */
 
			if (_network_player_info[p->index].months_empty > _network_autoclean_unprotected && _network_player_info[p->index].password[0] == '\0') {
 
				/* Shut the company down */
 
				DoCommandP(0, 2, p->index, NULL, CMD_PLAYER_CTRL);
 
				IConsolePrintF(_icolour_def, "Auto-cleaned company #%d", p->index + 1);
 
			}
 
			/* Is the compnay empty for autoclean_protected-months, and there is a protection? */
 
			if (_network_player_info[p->index].months_empty > _network_autoclean_protected && _network_player_info[p->index].password[0] != '\0') {
 
				/* Unprotect the company */
 
				_network_player_info[p->index].password[0] = '\0';
 
				IConsolePrintF(_icolour_def, "Auto-removed protection from company #%d", p->index+1);
 
				_network_player_info[p->index].months_empty = 0;
 
			}
 
		} else {
 
			/* It is not empty, reset the date */
 
			_network_player_info[p->index].months_empty = 0;
 
		}
 
	}
 
}
 

	
 
// This function changes new_name to a name that is unique (by adding #1 ...)
 
//  and it returns true if that succeeded.
 
bool NetworkFindName(char new_name[NETWORK_CLIENT_NAME_LENGTH])
 
{
 
	NetworkClientState *new_cs;
 
	bool found_name = false;
 
	byte number = 0;
 
	char original_name[NETWORK_CLIENT_NAME_LENGTH];
 

	
 
	// We use NETWORK_CLIENT_NAME_LENGTH in here, because new_name is really a pointer
 
	ttd_strlcpy(original_name, new_name, NETWORK_CLIENT_NAME_LENGTH);
 

	
 
	while (!found_name) {
 
		const NetworkClientInfo *ci;
 

	
 
		found_name = true;
 
		FOR_ALL_CLIENTS(new_cs) {
 
			ci = DEREF_CLIENT_INFO(new_cs);
 
			if (strcmp(ci->client_name, new_name) == 0) {
 
				// Name already in use
 
				found_name = false;
 
				break;
 
			}
 
		}
 
		// Check if it is the same as the server-name
 
		ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 
		if (ci != NULL) {
 
			if (strcmp(ci->client_name, new_name) == 0) found_name = false; // name already in use
 
		}
 

	
 
		if (!found_name) {
 
			// Try a new name (<name> #1, <name> #2, and so on)
 

	
 
			// Stop if we tried for more than 50 times..
 
			if (number++ > 50) break;
 
			snprintf(new_name, NETWORK_CLIENT_NAME_LENGTH, "%s #%d", original_name, number);
 
		}
 
	}
 

	
 
	return found_name;
 
}
 

	
 
// Reads a packet from the stream
 
bool NetworkServer_ReadPackets(NetworkClientState *cs)
 
{
 
	Packet *p;
 
	NetworkRecvStatus res;
 
	while ((p = NetworkRecv_Packet(cs, &res)) != NULL) {
 
		byte type = NetworkRecv_uint8(cs, p);
 
		if (type < PACKET_END && _network_server_packet[type] != NULL && !cs->has_quit) {
 
			_network_server_packet[type](cs, p);
 
		} else {
 
			DEBUG(net, 0, "[server] received invalid packet type %d", type);
 
		}
 
		free(p);
 
	}
 

	
 
	return true;
 
}
 

	
 
// Handle the local command-queue
 
static void NetworkHandleCommandQueue(NetworkClientState* cs)
 
{
 
	CommandPacket *cp;
 

	
 
	while ( (cp = cs->command_queue) != NULL) {
 
		SEND_COMMAND(PACKET_SERVER_COMMAND)(cs, cp);
 

	
 
		cs->command_queue = cp->next;
 
		free(cp);
 
	}
 
}
 

	
 
// This is called every tick if this is a _network_server
 
void NetworkServer_Tick(bool send_frame)
 
{
 
	NetworkClientState *cs;
 
#ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
	bool send_sync = false;
 
#endif
 

	
 
#ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
	if (_frame_counter >= _last_sync_frame + _network_sync_freq) {
 
		_last_sync_frame = _frame_counter;
 
		send_sync = true;
 
	}
 
#endif
 

	
 
	// Now we are done with the frame, inform the clients that they can
 
	//  do their frame!
 
	FOR_ALL_CLIENTS(cs) {
 
		// Check if the speed of the client is what we can expect from a client
 
		if (cs->status == STATUS_ACTIVE) {
 
			// 1 lag-point per day
 
			int lag = NetworkCalculateLag(cs) / DAY_TICKS;
 
			if (lag > 0) {
 
				if (lag > 3) {
 
					// Client did still not report in after 4 game-day, drop him
 
					//  (that is, the 3 of above, + 1 before any lag is counted)
 
					IConsolePrintF(_icolour_err,"Client #%d is dropped because the client did not respond for more than 4 game-days", cs->index);
 
					NetworkCloseClient(cs);
 
					continue;
 
				}
 

	
 
				// Report once per time we detect the lag
 
				if (cs->lag_test == 0) {
 
					IConsolePrintF(_icolour_warn,"[%d] Client #%d is slow, try increasing *net_frame_freq to a higher value!", _frame_counter, cs->index);
 
					cs->lag_test = 1;
 
				}
 
			} else {
 
				cs->lag_test = 0;
 
			}
 
		} else if (cs->status == STATUS_PRE_ACTIVE) {
 
			int lag = NetworkCalculateLag(cs);
 
			if (lag > _network_max_join_time) {
 
				IConsolePrintF(_icolour_err,"Client #%d is dropped because it took longer than %d ticks for him to join", cs->index, _network_max_join_time);
 
				NetworkCloseClient(cs);
 
			}
 
		}
 

	
 
		if (cs->status >= STATUS_PRE_ACTIVE) {
 
			// Check if we can send command, and if we have anything in the queue
 
			NetworkHandleCommandQueue(cs);
 

	
 
			// Send an updated _frame_counter_max to the client
 
			if (send_frame) SEND_COMMAND(PACKET_SERVER_FRAME)(cs);
 

	
 
#ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
			// Send a sync-check packet
 
			if (send_sync) SEND_COMMAND(PACKET_SERVER_SYNC)(cs);
 
#endif
 
		}
 
	}
 

	
 
	/* See if we need to advertise */
 
	NetworkUDPAdvertise();
 
}
 

	
 
void NetworkServerYearlyLoop(void)
 
{
 
	NetworkCheckRestartMap();
 
}
 

	
 
void NetworkServerMonthlyLoop(void)
 
{
 
	NetworkAutoCleanCompanies();
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/network_udp.c
Show inline comments
 
deleted file
src/network/network_udp.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "../string.h"
 
#include "network_data.h"
 
#include "../date.h"
 
#include "../map.h"
 
#include "network_gamelist.h"
 
#include "network_udp.h"
 
#include "../variables.h"
 
#include "../newgrf_config.h"
 

	
 
#include "core/udp.h"
 

	
 
/**
 
 * @file network_udp.c This file handles the UDP related communication.
 
 *
 
 * This is the GameServer <-> MasterServer and GameServer <-> GameClient
 
 * communication before the game is being joined.
 
 */
 

	
 
enum {
 
	ADVERTISE_NORMAL_INTERVAL = 30000, // interval between advertising in ticks (15 minutes)
 
	ADVERTISE_RETRY_INTERVAL  =   300, // readvertise when no response after this many ticks (9 seconds)
 
	ADVERTISE_RETRY_TIMES     =     3  // give up readvertising after this much failed retries
 
};
 

	
 
#define DEF_UDP_RECEIVE_COMMAND(type) void NetworkPacketReceive_ ## type ## _command(Packet *p, const struct sockaddr_in *client_addr)
 

	
 
static NetworkClientState _udp_cs;
 

	
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER)
 
{
 
	Packet *packet;
 
	// Just a fail-safe.. should never happen
 
	if (!_network_udp_server)
 
		return;
 

	
 
	packet = NetworkSend_Init(PACKET_UDP_SERVER_RESPONSE);
 

	
 
	// Update some game_info
 
	_network_game_info.game_date     = _date;
 
	_network_game_info.map_width     = MapSizeX();
 
	_network_game_info.map_height    = MapSizeY();
 
	_network_game_info.map_set       = _opt.landscape;
 
	_network_game_info.companies_on  = ActivePlayerCount();
 
	_network_game_info.spectators_on = NetworkSpectatorCount();
 
	_network_game_info.grfconfig     = _grfconfig;
 

	
 
	NetworkSend_NetworkGameInfo(packet, &_network_game_info);
 

	
 
	// Let the client know that we are here
 
	NetworkSendUDP_Packet(_udp_server_socket, packet, client_addr);
 

	
 
	free(packet);
 

	
 
	DEBUG(net, 2, "[udp] queried from '%s'", inet_ntoa(client_addr->sin_addr));
 
}
 

	
 
void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config)
 
{
 
	/* Find the matching GRF file */
 
	const GRFConfig *f = FindGRFConfig(config->grfid, config->md5sum);
 
	if (f == NULL) {
 
		/* Don't know the GRF, so mark game incompatible and the (possibly)
 
		 * already resolved name for this GRF (another server has sent the
 
		 * name of the GRF already */
 
		config->name     = FindUnknownGRFName(config->grfid, config->md5sum, true);
 
		SETBIT(config->flags, GCF_NOT_FOUND);
 
	} else {
 
		config->filename = f->filename;
 
		config->name     = f->name;
 
		config->info     = f->info;
 
	}
 
	SETBIT(config->flags, GCF_COPY);
 
}
 

	
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE)
 
{
 
	extern const char _openttd_revision[];
 
	NetworkGameList *item;
 

	
 
	// Just a fail-safe.. should never happen
 
	if (_network_udp_server || _udp_cs.has_quit) return;
 

	
 
	DEBUG(net, 4, "[udp] server response from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));
 

	
 
	// Find next item
 
	item = NetworkGameListAddItem(inet_addr(inet_ntoa(client_addr->sin_addr)), ntohs(client_addr->sin_port));
 

	
 
	NetworkRecv_NetworkGameInfo(&_udp_cs, p, &item->info);
 

	
 
	item->info.compatible = true;
 
	{
 
		/* Checks whether there needs to be a request for names of GRFs and makes
 
		 * the request if necessary. GRFs that need to be requested are the GRFs
 
		 * that do not exist on the clients system and we do not have the name
 
		 * resolved of, i.e. the name is still UNKNOWN_GRF_NAME_PLACEHOLDER.
 
		 * The in_request array and in_request_count are used so there is no need
 
		 * to do a second loop over the GRF list, which can be relatively expensive
 
		 * due to the string comparisons. */
 
		const GRFConfig *in_request[NETWORK_MAX_GRF_COUNT];
 
		const GRFConfig *c;
 
		uint in_request_count = 0;
 
		struct sockaddr_in out_addr;
 

	
 
		for (c = item->info.grfconfig; c != NULL; c = c->next) {
 
			if (HASBIT(c->flags, GCF_NOT_FOUND)) item->info.compatible = false;
 
			if (!HASBIT(c->flags, GCF_NOT_FOUND) || strcmp(c->name, UNKNOWN_GRF_NAME_PLACEHOLDER) != 0) continue;
 
			in_request[in_request_count] = c;
 
			in_request_count++;
 
		}
 

	
 
		if (in_request_count > 0) {
 
			/* There are 'unknown' GRFs, now send a request for them */
 
			uint i;
 
			Packet *packet = NetworkSend_Init(PACKET_UDP_CLIENT_GET_NEWGRFS);
 

	
 
			NetworkSend_uint8 (packet, in_request_count);
 
			for (i = 0; i < in_request_count; i++) {
 
				NetworkSend_GRFIdentifier(packet, in_request[i]);
 
			}
 

	
 
			out_addr.sin_family      = AF_INET;
 
			out_addr.sin_port        = htons(item->port);
 
			out_addr.sin_addr.s_addr = item->ip;
 
			NetworkSendUDP_Packet(_udp_client_socket, packet, &out_addr);
 
			free(packet);
 
		}
 
	}
 

	
 
	if (item->info.server_lang >= NETWORK_NUM_LANGUAGES) item->info.server_lang = 0;
 
	if (item->info.map_set >= NUM_LANDSCAPE ) item->info.map_set = 0;
 

	
 
	if (item->info.hostname[0] == '\0')
 
		snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", inet_ntoa(client_addr->sin_addr));
 

	
 
	/* Check if we are allowed on this server based on the revision-match */
 
	item->info.version_compatible =
 
		strcmp(item->info.server_revision, _openttd_revision) == 0 ||
 
		strcmp(item->info.server_revision, NOREV_STRING) == 0;
 
	item->info.compatible &= item->info.version_compatible; // Already contains match for GRFs
 

	
 
	item->online = true;
 

	
 
	UpdateNetworkGameWindow(false);
 
}
 

	
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO)
 
{
 
	NetworkClientState *cs;
 
	NetworkClientInfo *ci;
 
	Packet *packet;
 
	Player *player;
 
	byte current = 0;
 
	int i;
 

	
 
	// Just a fail-safe.. should never happen
 
	if (!_network_udp_server) return;
 

	
 
	packet = NetworkSend_Init(PACKET_UDP_SERVER_DETAIL_INFO);
 

	
 
	/* Send the amount of active companies */
 
	NetworkSend_uint8 (packet, NETWORK_COMPANY_INFO_VERSION);
 
	NetworkSend_uint8 (packet, ActivePlayerCount());
 

	
 
	/* Fetch the latest version of everything */
 
	NetworkPopulateCompanyInfo();
 

	
 
	/* Go through all the players */
 
	FOR_ALL_PLAYERS(player) {
 
		/* Skip non-active players */
 
		if (!player->is_active) continue;
 

	
 
		current++;
 

	
 
		/* Send the information */
 
		NetworkSend_uint8(packet, current);
 

	
 
		NetworkSend_string(packet, _network_player_info[player->index].company_name);
 
		NetworkSend_uint32(packet, _network_player_info[player->index].inaugurated_year);
 
		NetworkSend_uint64(packet, _network_player_info[player->index].company_value);
 
		NetworkSend_uint64(packet, _network_player_info[player->index].money);
 
		NetworkSend_uint64(packet, _network_player_info[player->index].income);
 
		NetworkSend_uint16(packet, _network_player_info[player->index].performance);
 

	
 
		/* Send 1 if there is a passord for the company else send 0 */
 
		if (_network_player_info[player->index].password[0] != '\0') {
 
			NetworkSend_uint8(packet, 1);
 
		} else {
 
			NetworkSend_uint8(packet, 0);
 
		}
 

	
 
		for (i = 0; i < NETWORK_VEHICLE_TYPES; i++)
 
			NetworkSend_uint16(packet, _network_player_info[player->index].num_vehicle[i]);
 

	
 
		for (i = 0; i < NETWORK_STATION_TYPES; i++)
 
			NetworkSend_uint16(packet, _network_player_info[player->index].num_station[i]);
 

	
 
		/* Find the clients that are connected to this player */
 
		FOR_ALL_CLIENTS(cs) {
 
			ci = DEREF_CLIENT_INFO(cs);
 
			if (ci->client_playas == player->index) {
 
				/* The uint8 == 1 indicates that a client is following */
 
				NetworkSend_uint8(packet, 1);
 
				NetworkSend_string(packet, ci->client_name);
 
				NetworkSend_string(packet, ci->unique_id);
 
				NetworkSend_uint32(packet, ci->join_date);
 
			}
 
		}
 
		/* Also check for the server itself */
 
		ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 
		if (ci->client_playas == player->index) {
 
			/* The uint8 == 1 indicates that a client is following */
 
			NetworkSend_uint8(packet, 1);
 
			NetworkSend_string(packet, ci->client_name);
 
			NetworkSend_string(packet, ci->unique_id);
 
			NetworkSend_uint32(packet, ci->join_date);
 
		}
 

	
 
		/* Indicates end of client list */
 
		NetworkSend_uint8(packet, 0);
 
	}
 

	
 
	/* And check if we have any spectators */
 
	FOR_ALL_CLIENTS(cs) {
 
		ci = DEREF_CLIENT_INFO(cs);
 
		if (!IsValidPlayer(ci->client_playas)) {
 
			/* The uint8 == 1 indicates that a client is following */
 
			NetworkSend_uint8(packet, 1);
 
			NetworkSend_string(packet, ci->client_name);
 
			NetworkSend_string(packet, ci->unique_id);
 
			NetworkSend_uint32(packet, ci->join_date);
 
		}
 
	}
 

	
 
	/* Also check for the server itself */
 
	ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 
	if (!IsValidPlayer(ci->client_playas)) {
 
		/* The uint8 == 1 indicates that a client is following */
 
		NetworkSend_uint8(packet, 1);
 
		NetworkSend_string(packet, ci->client_name);
 
		NetworkSend_string(packet, ci->unique_id);
 
		NetworkSend_uint32(packet, ci->join_date);
 
	}
 

	
 
	/* Indicates end of client list */
 
	NetworkSend_uint8(packet, 0);
 

	
 
	NetworkSendUDP_Packet(_udp_server_socket, packet, client_addr);
 

	
 
	free(packet);
 
}
 

	
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST)
 
{
 
	int i;
 
	struct in_addr ip;
 
	uint16 port;
 
	uint8 ver;
 

	
 
	/* packet begins with the protocol version (uint8)
 
	 * then an uint16 which indicates how many
 
	 * ip:port pairs are in this packet, after that
 
	 * an uint32 (ip) and an uint16 (port) for each pair
 
	 */
 

	
 
	ver = NetworkRecv_uint8(&_udp_cs, p);
 

	
 
	if (_udp_cs.has_quit) return;
 

	
 
	if (ver == 1) {
 
		for (i = NetworkRecv_uint16(&_udp_cs, p); i != 0 ; i--) {
 
			ip.s_addr = TO_LE32(NetworkRecv_uint32(&_udp_cs, p));
 
			port = NetworkRecv_uint16(&_udp_cs, p);
 
			NetworkUDPQueryServer(inet_ntoa(ip), port);
 
		}
 
	}
 
}
 

	
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER)
 
{
 
	_network_advertise_retries = 0;
 
	DEBUG(net, 2, "[udp] advertising on master server successfull");
 

	
 
	/* We are advertised, but we don't want to! */
 
	if (!_network_advertise) NetworkUDPRemoveAdvertise();
 
}
 

	
 
/**
 
 * A client has requested the names of some NewGRFs.
 
 *
 
 * Replying this can be tricky as we have a limit of SEND_MTU bytes
 
 * in the reply packet and we can send up to 100 bytes per NewGRF
 
 * (GRF ID, MD5sum and NETWORK_GRF_NAME_LENGTH bytes for the name).
 
 * As SEND_MTU is _much_ less than 100 * NETWORK_MAX_GRF_COUNT, it
 
 * could be that a packet overflows. To stop this we only reply
 
 * with the first N NewGRFs so that if the first N + 1 NewGRFs
 
 * would be sent, the packet overflows.
 
 * in_reply and in_reply_count are used to keep a list of GRFs to
 
 * send in the reply.
 
 */
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS)
 
{
 
	uint8 num_grfs;
 
	uint i;
 

	
 
	const GRFConfig *in_reply[NETWORK_MAX_GRF_COUNT];
 
	Packet *packet;
 
	uint8 in_reply_count = 0;
 
	uint packet_len = 0;
 

	
 
	/* Just a fail-safe.. should never happen */
 
	if (_udp_cs.has_quit) return;
 

	
 
	DEBUG(net, 6, "[udp] newgrf data request from %s:%d", inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port));
 

	
 
	num_grfs = NetworkRecv_uint8 (&_udp_cs, p);
 
	if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
 

	
 
	for (i = 0; i < num_grfs; i++) {
 
		GRFConfig c;
 
		const GRFConfig *f;
 

	
 
		NetworkRecv_GRFIdentifier(&_udp_cs, p, &c);
 

	
 
		/* Find the matching GRF file */
 
		f = FindGRFConfig(c.grfid, c.md5sum);
 
		if (f == NULL) continue; // The GRF is unknown to this server
 

	
 
		/* If the reply might exceed the size of the packet, only reply
 
		 * the current list and do not send the other data.
 
		 * The name could be an empty string, if so take the filename. */
 
		packet_len += sizeof(c.grfid) + sizeof(c.md5sum) +
 
				min(strlen((f->name != NULL && strlen(f->name) > 0) ? f->name : f->filename) + 1, NETWORK_GRF_NAME_LENGTH);
 
		if (packet_len > SEND_MTU - 4) { // 4 is 3 byte header + grf count in reply
 
			break;
 
		}
 
		in_reply[in_reply_count] = f;
 
		in_reply_count++;
 
	}
 

	
 
	if (in_reply_count == 0) return;
 

	
 
	packet = NetworkSend_Init(PACKET_UDP_SERVER_NEWGRFS);
 
	NetworkSend_uint8 (packet, in_reply_count);
 
	for (i = 0; i < in_reply_count; i++) {
 
		char name[NETWORK_GRF_NAME_LENGTH];
 

	
 
		/* The name could be an empty string, if so take the filename */
 
		ttd_strlcpy(name, (in_reply[i]->name != NULL && strlen(in_reply[i]->name) > 0) ?
 
				in_reply[i]->name : in_reply[i]->filename, sizeof(name));
 
	 	NetworkSend_GRFIdentifier(packet, in_reply[i]);
 
		NetworkSend_string(packet, name);
 
	}
 

	
 
	NetworkSendUDP_Packet(_udp_server_socket, packet, client_addr);
 
	free(packet);
 
}
 

	
 
/** The return of the client's request of the names of some NewGRFs */
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_NEWGRFS)
 
{
 
	uint8 num_grfs;
 
	uint i;
 

	
 
	/* Just a fail-safe.. should never happen */
 
	if (_udp_cs.has_quit) return;
 

	
 
	DEBUG(net, 6, "[udp] newgrf data reply from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));
 

	
 
	num_grfs = NetworkRecv_uint8 (&_udp_cs, p);
 
	if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
 

	
 
	for (i = 0; i < num_grfs; i++) {
 
		char *unknown_name;
 
		char name[NETWORK_GRF_NAME_LENGTH];
 
		GRFConfig c;
 

	
 
		NetworkRecv_GRFIdentifier(&_udp_cs, p, &c);
 
		NetworkRecv_string(&_udp_cs, p, name, sizeof(name));
 

	
 
		/* An empty name is not possible under normal circumstances
 
		 * and causes problems when showing the NewGRF list. */
 
		if (strlen(name) == 0) continue;
 

	
 
		/* Finds the fake GRFConfig for the just read GRF ID and MD5sum tuple.
 
		 * If it exists and not resolved yet, then name of the fake GRF is
 
		 * overwritten with the name from the reply. */
 
		unknown_name = FindUnknownGRFName(c.grfid, c.md5sum, false);
 
		if (unknown_name != NULL && strcmp(unknown_name, UNKNOWN_GRF_NAME_PLACEHOLDER) == 0) {
 
			ttd_strlcpy(unknown_name, name, NETWORK_GRF_NAME_LENGTH);
 
		}
 
	}
 
}
 

	
 
/**
 
 * Every type of UDP packet should only be received by a single socket;
 
 * The socket communicating with the masterserver should receive the
 
 * game information of some 'random' host.
 
 */
 
typedef struct NetworkUDPPacketAndSocket {
 
	void (*callback)(Packet *p, const struct sockaddr_in *client_addr);
 
	SOCKET *incoming_socket;
 
} NetworkUPDPacketAndSocket;
 

	
 
static const NetworkUPDPacketAndSocket _network_udp_packet[PACKET_UDP_END] = {
 
	{ RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER),   &_udp_server_socket },
 
	{ RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE),      &_udp_client_socket },
 
	{ RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO),   &_udp_server_socket },
 
	{ NULL,                                             NULL                },
 
	{ NULL,                                             NULL                },
 
	{ RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER),  &_udp_master_socket },
 
	{ NULL,                                             NULL                },
 
	{ RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST), &_udp_client_socket },
 
	{ NULL,                                             NULL                },
 
	{ RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS),   &_udp_server_socket },
 
	{ RECEIVE_COMMAND(PACKET_UDP_SERVER_NEWGRFS),       &_udp_client_socket },
 
};
 

	
 
void NetworkHandleUDPPacket(const SOCKET udp, Packet *p, const struct sockaddr_in *client_addr)
 
{
 
	byte type;
 

	
 
	/* Fake a client, so we can see when there is an illegal packet */
 
	_udp_cs.socket = INVALID_SOCKET;
 
	_udp_cs.has_quit = false;
 

	
 
	type = NetworkRecv_uint8(&_udp_cs, p);
 

	
 
	if (type < PACKET_UDP_END && *_network_udp_packet[type].incoming_socket == udp && !_udp_cs.has_quit) {
 
		_network_udp_packet[type].callback(p, client_addr);
 
	} else {
 
		if (*_network_udp_packet[type].incoming_socket != udp) {
 
			DEBUG(net, 0, "[udp] received packet on wrong port from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));
 
		} else if (!_udp_cs.has_quit) {
 
			DEBUG(net, 0, "[udp] received invalid packet type %d from %s:%d", type,  inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));
 
		} else {
 
			DEBUG(net, 0, "[udp] received illegal packet from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));
 
		}
 
	}
 
}
 

	
 

	
 
// Close UDP connection
 
void NetworkUDPStop(void)
 
{
 
	DEBUG(net, 1, "[udp] closed listeners");
 

	
 
	if (_network_udp_server) {
 
		NetworkUDPClose(&_udp_server_socket);
 
		NetworkUDPClose(&_udp_master_socket);
 
	} else {
 
		NetworkUDPClose(&_udp_client_socket);
 
	}
 

	
 
	_network_udp_server = false;
 
	_network_udp_broadcast = 0;
 
}
 

	
 
// Broadcast to all ips
 
static void NetworkUDPBroadCast(SOCKET udp)
 
{
 
	Packet* p = NetworkSend_Init(PACKET_UDP_CLIENT_FIND_SERVER);
 
	uint i;
 

	
 
	for (i = 0; _broadcast_list[i] != 0; i++) {
 
		struct sockaddr_in out_addr;
 

	
 
		out_addr.sin_family = AF_INET;
 
		out_addr.sin_port = htons(_network_server_port);
 
		out_addr.sin_addr.s_addr = _broadcast_list[i];
 

	
 
		DEBUG(net, 4, "[udp] broadcasting to %s", inet_ntoa(out_addr.sin_addr));
 

	
 
		NetworkSendUDP_Packet(udp, p, &out_addr);
 
	}
 

	
 
	free(p);
 
}
 

	
 

	
 
// Request the the server-list from the master server
 
void NetworkUDPQueryMasterServer(void)
 
{
 
	struct sockaddr_in out_addr;
 
	Packet *p;
 

	
 
	if (_udp_client_socket == INVALID_SOCKET)
 
		if (!NetworkUDPListen(&_udp_client_socket, 0, 0, true))
 
			return;
 

	
 
	p = NetworkSend_Init(PACKET_UDP_CLIENT_GET_LIST);
 

	
 
	out_addr.sin_family = AF_INET;
 
	out_addr.sin_port = htons(NETWORK_MASTER_SERVER_PORT);
 
	out_addr.sin_addr.s_addr = NetworkResolveHost(NETWORK_MASTER_SERVER_HOST);
 

	
 
	// packet only contains protocol version
 
	NetworkSend_uint8(p, NETWORK_MASTER_SERVER_VERSION);
 

	
 
	NetworkSendUDP_Packet(_udp_client_socket, p, &out_addr);
 

	
 
	DEBUG(net, 2, "[udp] master server queried at %s:%d", inet_ntoa(out_addr.sin_addr),ntohs(out_addr.sin_port));
 

	
 
	free(p);
 
}
 

	
 
// Find all servers
 
void NetworkUDPSearchGame(void)
 
{
 
	// We are still searching..
 
	if (_network_udp_broadcast > 0) return;
 

	
 
	// No UDP-socket yet..
 
	if (_udp_client_socket == INVALID_SOCKET)
 
		if (!NetworkUDPListen(&_udp_client_socket, 0, 0, true))
 
			return;
 

	
 
	DEBUG(net, 0, "[udp] searching server");
 

	
 
	NetworkUDPBroadCast(_udp_client_socket);
 
	_network_udp_broadcast = 300; // Stay searching for 300 ticks
 
}
 

	
 
NetworkGameList *NetworkUDPQueryServer(const char* host, unsigned short port)
 
{
 
	struct sockaddr_in out_addr;
 
	Packet *p;
 
	NetworkGameList *item;
 

	
 
	// No UDP-socket yet..
 
	if (_udp_client_socket == INVALID_SOCKET)
 
		if (!NetworkUDPListen(&_udp_client_socket, 0, 0, true))
 
			return NULL;
 

	
 
	out_addr.sin_family = AF_INET;
 
	out_addr.sin_port = htons(port);
 
	out_addr.sin_addr.s_addr = NetworkResolveHost(host);
 

	
 
	// Clear item in gamelist
 
	item = NetworkGameListAddItem(inet_addr(inet_ntoa(out_addr.sin_addr)), ntohs(out_addr.sin_port));
 
	memset(&item->info, 0, sizeof(item->info));
 
	ttd_strlcpy(item->info.server_name, host, lengthof(item->info.server_name));
 
	ttd_strlcpy(item->info.hostname, host, lengthof(item->info.hostname));
 
	item->online = false;
 

	
 
	// Init the packet
 
	p = NetworkSend_Init(PACKET_UDP_CLIENT_FIND_SERVER);
 

	
 
	NetworkSendUDP_Packet(_udp_client_socket, p, &out_addr);
 

	
 
	free(p);
 

	
 
	UpdateNetworkGameWindow(false);
 
	return item;
 
}
 

	
 
/* Remove our advertise from the master-server */
 
void NetworkUDPRemoveAdvertise(void)
 
{
 
	struct sockaddr_in out_addr;
 
	Packet *p;
 

	
 
	/* Check if we are advertising */
 
	if (!_networking || !_network_server || !_network_udp_server) return;
 

	
 
	/* check for socket */
 
	if (_udp_master_socket == INVALID_SOCKET)
 
		if (!NetworkUDPListen(&_udp_master_socket, _network_server_bind_ip, 0, false))
 
			return;
 

	
 
	DEBUG(net, 1, "[udp] removing advertise from master server");
 

	
 
	/* Find somewhere to send */
 
	out_addr.sin_family = AF_INET;
 
	out_addr.sin_port = htons(NETWORK_MASTER_SERVER_PORT);
 
	out_addr.sin_addr.s_addr = NetworkResolveHost(NETWORK_MASTER_SERVER_HOST);
 

	
 
	/* Send the packet */
 
	p = NetworkSend_Init(PACKET_UDP_SERVER_UNREGISTER);
 
	/* Packet is: Version, server_port */
 
	NetworkSend_uint8(p, NETWORK_MASTER_SERVER_VERSION);
 
	NetworkSend_uint16(p, _network_server_port);
 
	NetworkSendUDP_Packet(_udp_master_socket, p, &out_addr);
 

	
 
	free(p);
 
}
 

	
 
/* Register us to the master server
 
     This function checks if it needs to send an advertise */
 
void NetworkUDPAdvertise(void)
 
{
 
	struct sockaddr_in out_addr;
 
	Packet *p;
 

	
 
	/* Check if we should send an advertise */
 
	if (!_networking || !_network_server || !_network_udp_server || !_network_advertise)
 
		return;
 

	
 
	/* check for socket */
 
	if (_udp_master_socket == INVALID_SOCKET)
 
		if (!NetworkUDPListen(&_udp_master_socket, _network_server_bind_ip, 0, false))
 
			return;
 

	
 
	if (_network_need_advertise) {
 
		_network_need_advertise = false;
 
		_network_advertise_retries = ADVERTISE_RETRY_TIMES;
 
	} else {
 
		/* Only send once every ADVERTISE_NORMAL_INTERVAL ticks */
 
		if (_network_advertise_retries == 0) {
 
			if ((_network_last_advertise_frame + ADVERTISE_NORMAL_INTERVAL) > _frame_counter)
 
				return;
 
			_network_advertise_retries = ADVERTISE_RETRY_TIMES;
 
		}
 

	
 
		if ((_network_last_advertise_frame + ADVERTISE_RETRY_INTERVAL) > _frame_counter)
 
			return;
 
	}
 

	
 
	_network_advertise_retries--;
 
	_network_last_advertise_frame = _frame_counter;
 

	
 
	/* Find somewhere to send */
 
	out_addr.sin_family = AF_INET;
 
	out_addr.sin_port = htons(NETWORK_MASTER_SERVER_PORT);
 
	out_addr.sin_addr.s_addr = NetworkResolveHost(NETWORK_MASTER_SERVER_HOST);
 

	
 
	DEBUG(net, 1, "[udp] advertising to master server");
 

	
 
	/* Send the packet */
 
	p = NetworkSend_Init(PACKET_UDP_SERVER_REGISTER);
 
	/* Packet is: WELCOME_MESSAGE, Version, server_port */
 
	NetworkSend_string(p, NETWORK_MASTER_SERVER_WELCOME_MESSAGE);
 
	NetworkSend_uint8(p, NETWORK_MASTER_SERVER_VERSION);
 
	NetworkSend_uint16(p, _network_server_port);
 
	NetworkSendUDP_Packet(_udp_master_socket, p, &out_addr);
 

	
 
	free(p);
 
}
 

	
 
void NetworkUDPInitialize(void)
 
{
 
	_udp_client_socket = INVALID_SOCKET;
 
	_udp_server_socket = INVALID_SOCKET;
 
	_udp_master_socket = INVALID_SOCKET;
 

	
 
	_network_udp_server = false;
 
	_network_udp_broadcast = 0;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/newgrf.c
Show inline comments
 
deleted file
src/newgrf.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 

	
 
#include <stdarg.h>
 

	
 
#include "openttd.h"
 
#include "debug.h"
 
#include "gfx.h"
 
#include "fileio.h"
 
#include "functions.h"
 
#include "engine.h"
 
#include "spritecache.h"
 
#include "station.h"
 
#include "sprite.h"
 
#include "newgrf.h"
 
#include "variables.h"
 
#include "string.h"
 
#include "table/strings.h"
 
#include "bridge.h"
 
#include "economy.h"
 
#include "newgrf_engine.h"
 
#include "vehicle.h"
 
#include "newgrf_text.h"
 
#include "table/sprites.h"
 
#include "fontcache.h"
 
#include "date.h"
 
#include "currency.h"
 
#include "sound.h"
 
#include "newgrf_config.h"
 
#include "newgrf_sound.h"
 
#include "newgrf_spritegroup.h"
 

	
 
/* TTDPatch extended GRF format codec
 
 * (c) Petr Baudis 2004 (GPL'd)
 
 * Changes by Florian octo Forster are (c) by the OpenTTD development team.
 
 *
 
 * Contains portions of documentation by TTDPatch team.
 
 * Thanks especially to Josef Drexler for the documentation as well as a lot
 
 * of help at #tycoon. Also thanks to Michael Blunck for is GRF files which
 
 * served as subject to the initial testing of this codec. */
 

	
 

	
 
static int _skip_sprites; // XXX
 
static uint _file_index; // XXX
 
SpriteID _signal_base;
 
SpriteID _coast_base;
 

	
 
static GRFFile *_cur_grffile;
 
GRFFile *_first_grffile;
 
static SpriteID _cur_spriteid;
 
static GrfLoadingStage _cur_stage;
 
static uint32 _nfo_line;
 

	
 
static GRFConfig *_cur_grfconfig;
 

	
 
/* Miscellaneous GRF features, set by Action 0x0D, parameter 0x9E */
 
static byte _misc_grf_features = 0;
 

	
 
/* 32 * 8 = 256 flags. Apparently TTDPatch uses this many.. */
 
static uint32 _ttdpatch_flags[8];
 

	
 
/* Used by Action 0x06 to preload a pseudo sprite and modify its content */
 
static byte *_preload_sprite = NULL;
 

	
 
/* Set if any vehicle is loaded which uses 2cc (two company colours) */
 
bool _have_2cc = false;
 

	
 

	
 
typedef enum GrfDataType {
 
	GDT_SOUND,
 
} GrfDataType;
 

	
 
static byte _grf_data_blocks;
 
static GrfDataType _grf_data_type;
 

	
 

	
 
typedef enum grfspec_feature {
 
	GSF_TRAIN,
 
	GSF_ROAD,
 
	GSF_SHIP,
 
	GSF_AIRCRAFT,
 
	GSF_STATION,
 
	GSF_CANAL,
 
	GSF_BRIDGE,
 
	GSF_TOWNHOUSE,
 
	GSF_GLOBALVAR,
 
	GSF_INDUSTRYTILES,
 
	GSF_INDUSTRIES,
 
	GSF_CARGOS,
 
	GSF_SOUNDFX,
 
} grfspec_feature;
 

	
 

	
 
typedef void (*SpecialSpriteHandler)(byte *buf, int len);
 

	
 
static const int _vehcounts[4] = {
 
	/* GSF_TRAIN */    NUM_TRAIN_ENGINES,
 
	/* GSF_ROAD */     NUM_ROAD_ENGINES,
 
	/* GSF_SHIP */     NUM_SHIP_ENGINES,
 
	/* GSF_AIRCRAFT */ NUM_AIRCRAFT_ENGINES
 
};
 

	
 
static const int _vehshifts[4] = {
 
	/* GSF_TRAIN */    0,
 
	/* GSF_ROAD */     ROAD_ENGINES_INDEX,
 
	/* GSF_SHIP */     SHIP_ENGINES_INDEX,
 
	/* GSF_AIRCRAFT */ AIRCRAFT_ENGINES_INDEX,
 
};
 

	
 
enum {
 
	MAX_STATIONS = 256,
 
};
 

	
 
static uint16 cargo_allowed[TOTAL_NUM_ENGINES];
 
static uint16 cargo_disallowed[TOTAL_NUM_ENGINES];
 

	
 
/* Contains the GRF ID of the owner of a vehicle if it has been reserved */
 
static uint32 _grm_engines[TOTAL_NUM_ENGINES];
 

	
 
/** DEBUG() function dedicated to newGRF debugging messages
 
 * Function is essentialy the same as DEBUG(grf, severity, ...) with the
 
 * addition of file:line information when parsing grf files.
 
 * NOTE: for the above reason(s) grfmsg() should ONLY be used for
 
 * loading/parsing grf files, not for runtime debug messages as there
 
 * is no file information available during that time.
 
 * @param severity debugging severity level, see debug.h
 
 * @param debugging message in printf() format */
 
void CDECL grfmsg(int severity, const char *str, ...)
 
{
 
	char buf[1024];
 
	va_list va;
 

	
 
	va_start(va, str);
 
	vsnprintf(buf, sizeof(buf), str, va);
 
	va_end(va);
 

	
 
	DEBUG(grf, severity, "[%s:%d] %s", _cur_grfconfig->filename, _nfo_line, buf);
 
}
 

	
 
static inline void check_length(int real, int wanted, const char *str)
 
{
 
	if (real >= wanted) return;
 
	grfmsg(0, "%s: Invalid pseudo sprite length %d (expected %d)!", str, real, wanted);
 
}
 

	
 
static inline byte grf_load_byte(byte **buf)
 
{
 
	return *(*buf)++;
 
}
 

	
 
static uint16 grf_load_word(byte **buf)
 
{
 
	uint16 val = grf_load_byte(buf);
 
	return val | (grf_load_byte(buf) << 8);
 
}
 

	
 
static uint16 grf_load_extended(byte** buf)
 
{
 
	uint16 val;
 
	val = grf_load_byte(buf);
 
	if (val == 0xFF) val = grf_load_word(buf);
 
	return val;
 
}
 

	
 
static uint32 grf_load_dword(byte **buf)
 
{
 
	uint32 val = grf_load_word(buf);
 
	return val | (grf_load_word(buf) << 16);
 
}
 

	
 
static uint32 grf_load_var(byte size, byte **buf)
 
{
 
	switch (size) {
 
		case 1: return grf_load_byte(buf);
 
		case 2: return grf_load_word(buf);
 
		case 4: return grf_load_dword(buf);
 
		default:
 
			NOT_REACHED();
 
			return 0;
 
	}
 
}
 

	
 
static GRFFile *GetFileByGRFID(uint32 grfid)
 
{
 
	GRFFile *file;
 

	
 
	for (file = _first_grffile; file != NULL; file = file->next) {
 
		if (file->grfid == grfid) break;
 
	}
 
	return file;
 
}
 

	
 
static GRFFile *GetFileByFilename(const char *filename)
 
{
 
	GRFFile *file;
 

	
 
	for (file = _first_grffile; file != NULL; file = file->next) {
 
		if (strcmp(file->filename, filename) == 0) break;
 
	}
 
	return file;
 
}
 

	
 

	
 
typedef bool (*VCI_Handler)(uint engine, int numinfo, int prop, byte **buf, int len);
 

	
 
#define FOR_EACH_OBJECT for (i = 0; i < numinfo; i++)
 

	
 
static void dewagonize(int condition, int engine)
 
{
 
	EngineInfo *ei = &_engine_info[engine];
 
	RailVehicleInfo *rvi = &_rail_vehicle_info[engine];
 

	
 
	if (condition != 0) {
 
		ei->unk2 &= ~0x80;
 
		rvi->flags &= ~2;
 
	} else {
 
		ei->unk2 |= 0x80;
 
		rvi->flags |= 2;
 
	}
 
}
 

	
 
static bool RailVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len)
 
{
 
	EngineInfo *ei = &_engine_info[engine];
 
	RailVehicleInfo *rvi = &_rail_vehicle_info[engine];
 
	byte *buf = *bufp;
 
	int i;
 
	bool ret = false;
 

	
 
	switch (prop) {
 
		case 0x05: /* Track type */
 
			FOR_EACH_OBJECT {
 
				uint8 tracktype = grf_load_byte(&buf);
 

	
 
				switch (tracktype) {
 
					case 0: ei[i].railtype = rvi[i].engclass == 2 ? RAILTYPE_ELECTRIC : RAILTYPE_RAIL; break;
 
					case 1: ei[i].railtype = RAILTYPE_MONO; break;
 
					case 2: ei[i].railtype = RAILTYPE_MAGLEV; break;
 
					default:
 
						grfmsg(1, "RailVehicleChangeInfo: Invalid track type %d specified, ignoring", tracktype);
 
						break;
 
				}
 
			}
 
			break;
 

	
 
		case 0x08: /* AI passenger service */
 
			/* TODO */
 
			FOR_EACH_OBJECT grf_load_byte(&buf);
 
			ret = true;
 
			break;
 

	
 
		case 0x09: /* Speed (1 unit is 1 kmh) */
 
			FOR_EACH_OBJECT {
 
				uint16 speed = grf_load_word(&buf);
 
				if (speed == 0xFFFF) speed = 0;
 

	
 
				rvi[i].max_speed = speed;
 
			}
 
			break;
 

	
 
		case 0x0B: /* Power */
 
			FOR_EACH_OBJECT {
 
				uint16 power = grf_load_word(&buf);
 

	
 
				if (rvi[i].flags & RVI_MULTIHEAD) power /= 2;
 

	
 
				rvi[i].power = power;
 
				dewagonize(power, engine + i);
 
			}
 
			break;
 

	
 
		case 0x0D: /* Running cost factor */
 
			FOR_EACH_OBJECT {
 
				uint8 runcostfact = grf_load_byte(&buf);
 

	
 
				if (rvi[i].flags & RVI_MULTIHEAD) runcostfact /= 2;
 

	
 
				rvi[i].running_cost_base = runcostfact;
 
			}
 
			break;
 

	
 
		case 0x0E: /* Running cost base */
 
			FOR_EACH_OBJECT {
 
				uint32 base = grf_load_dword(&buf);
 

	
 
				switch (base) {
 
					case 0x4C30: rvi[i].running_cost_class = 0; break;
 
					case 0x4C36: rvi[i].running_cost_class = 1; break;
 
					case 0x4C3C: rvi[i].running_cost_class = 2; break;
 
					case 0: break; /* Used by wagons */
 
					default:
 
						grfmsg(1, "RailVehicleChangeInfo: Unsupported running cost base 0x%04X, ignoring", base);
 
						break;
 
				}
 
			}
 
			break;
 

	
 
		case 0x12: /* Sprite ID */
 
			FOR_EACH_OBJECT {
 
				uint8 spriteid = grf_load_byte(&buf);
 

	
 
				/* TTD sprite IDs point to a location in a 16bit array, but we use it
 
				 * as an array index, so we need it to be half the original value. */
 
				if (spriteid < 0xFD) spriteid >>= 1;
 

	
 
				rvi[i].image_index = spriteid;
 
			}
 
			break;
 

	
 
		case 0x13: /* Dual-headed */
 
			FOR_EACH_OBJECT {
 
				uint8 dual = grf_load_byte(&buf);
 

	
 
				if (dual != 0) {
 
					if (!(rvi[i].flags & RVI_MULTIHEAD)) {
 
						// adjust power and running cost if needed
 
						rvi[i].power /= 2;
 
						rvi[i].running_cost_base /= 2;
 
					}
 
					rvi[i].flags |= RVI_MULTIHEAD;
 
				} else {
 
					if (rvi[i].flags & RVI_MULTIHEAD) {
 
						// adjust power and running cost if needed
 
						rvi[i].power *= 2;
 
						rvi[i].running_cost_base *= 2;
 
					}
 
					rvi[i].flags &= ~RVI_MULTIHEAD;
 
				}
 
			}
 
			break;
 

	
 
		case 0x14: /* Cargo capacity */
 
			FOR_EACH_OBJECT rvi[i].capacity = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x15: /* Cargo type */
 
			FOR_EACH_OBJECT {
 
				uint8 ctype = grf_load_byte(&buf);
 

	
 
				if (ctype < NUM_CARGO) {
 
					rvi[i].cargo_type = ctype;
 
				} else {
 
					grfmsg(2, "RailVehicleChangeInfo: Invalid cargo type %d, ignoring", ctype);
 
				}
 
			}
 
			break;
 

	
 
		case 0x16: /* Weight */
 
			FOR_EACH_OBJECT SB(rvi[i].weight, 0, 8, grf_load_byte(&buf));
 
			break;
 

	
 
		case 0x17: /* Cost factor */
 
			FOR_EACH_OBJECT rvi[i].base_cost = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x18: /* AI rank */
 
			FOR_EACH_OBJECT rvi[i].ai_rank = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x19: /* Engine traction type */
 
			/* What do the individual numbers mean?
 
			 * 0x00 .. 0x07: Steam
 
			 * 0x08 .. 0x27: Diesel
 
			 * 0x28 .. 0x31: Electric
 
			 * 0x32 .. 0x37: Monorail
 
			 * 0x38 .. 0x41: Maglev
 
			 */
 
			FOR_EACH_OBJECT {
 
				uint8 traction = grf_load_byte(&buf);
 
				int engclass;
 

	
 
				if (traction <= 0x07) {
 
					engclass = 0;
 
				} else if (traction <= 0x27) {
 
					engclass = 1;
 
				} else if (traction <= 0x31) {
 
					engclass = 2;
 
				} else if (traction <= 0x41) {
 
					engclass = 2;
 
				} else {
 
					break;
 
				}
 
				if (ei[i].railtype == RAILTYPE_RAIL     && engclass == 2) ei[i].railtype = RAILTYPE_ELECTRIC;
 
				if (ei[i].railtype == RAILTYPE_ELECTRIC && engclass != 2) ei[i].railtype = RAILTYPE_RAIL;
 

	
 
				rvi[i].engclass = engclass;
 
			}
 
			break;
 

	
 
		case 0x1A: /* Alter purchase list sort order */
 
			FOR_EACH_OBJECT {
 
				EngineID pos = grf_load_byte(&buf);
 

	
 
				if (pos < NUM_TRAIN_ENGINES) {
 
					AlterRailVehListOrder(engine + i, pos);
 
				} else {
 
					grfmsg(2, "RailVehicleChangeInfo: Invalid train engine ID %d, ignoring", pos);
 
				}
 
			}
 
			break;
 

	
 
		case 0x1B: /* Powered wagons power bonus */
 
			FOR_EACH_OBJECT rvi[i].pow_wag_power = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x1C: /* Refit cost */
 
			FOR_EACH_OBJECT ei[i].refit_cost = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x1D: /* Refit cargo */
 
			FOR_EACH_OBJECT ei[i].refit_mask = grf_load_dword(&buf);
 
			break;
 

	
 
		case 0x1E: /* Callback */
 
			FOR_EACH_OBJECT ei[i].callbackmask = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x1F: /* Tractive effort coefficient */
 
			FOR_EACH_OBJECT rvi[i].tractive_effort = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x21: /* Shorter vehicle */
 
			FOR_EACH_OBJECT rvi[i].shorten_factor = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x22: /* Visual effect */
 
			// see note in engine.h about rvi->visual_effect
 
			FOR_EACH_OBJECT rvi[i].visual_effect = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x23: /* Powered wagons weight bonus */
 
			FOR_EACH_OBJECT rvi[i].pow_wag_weight = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x24: /* High byte of vehicle weight */
 
			FOR_EACH_OBJECT {
 
				byte weight = grf_load_byte(&buf);
 

	
 
				if (weight > 4) {
 
					grfmsg(2, "RailVehicleChangeInfo: Nonsensical weight of %d tons, ignoring", weight << 8);
 
				} else {
 
					SB(rvi[i].weight, 8, 8, weight);
 
				}
 
			}
 
			break;
 

	
 
		case 0x25: /* User-defined bit mask to set when checking veh. var. 42 */
 
			FOR_EACH_OBJECT rvi[i].user_def_data = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x27: /* Miscellaneous flags */
 
			FOR_EACH_OBJECT {
 
				ei[i].misc_flags = grf_load_byte(&buf);
 
				if (HASBIT(ei[i].misc_flags, EF_USES_2CC)) _have_2cc = true;
 
			}
 
			break;
 

	
 
		case 0x28: /* Cargo classes allowed */
 
			FOR_EACH_OBJECT cargo_allowed[engine + i] = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x29: /* Cargo classes disallowed */
 
			FOR_EACH_OBJECT cargo_disallowed[engine + i] = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x2A: /* Long format introduction date (days since year 0) */
 
			FOR_EACH_OBJECT ei[i].base_intro = grf_load_dword(&buf);
 
			break;
 

	
 
		/* TODO */
 
		/* Fall-through for unimplemented one byte long properties. */
 
		case 0x20: /* Air drag */
 
		case 0x26: /* Retire vehicle early */
 
			/* TODO */
 
			FOR_EACH_OBJECT grf_load_byte(&buf);
 
			ret = true;
 
			break;
 

	
 
		default:
 
			ret = true;
 
			break;
 
	}
 
	*bufp = buf;
 
	return ret;
 
}
 

	
 
static bool RoadVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len)
 
{
 
	EngineInfo *ei = &_engine_info[ROAD_ENGINES_INDEX + engine];
 
	RoadVehicleInfo *rvi = &_road_vehicle_info[engine];
 
	byte *buf = *bufp;
 
	int i;
 
	bool ret = false;
 

	
 
	switch (prop) {
 
		case 0x08: /* Speed (1 unit is 0.5 kmh) */
 
			FOR_EACH_OBJECT rvi[i].max_speed = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x09: /* Running cost factor */
 
			FOR_EACH_OBJECT rvi[i].running_cost = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0A: /* Running cost base */
 
			/* TODO: I have no idea. --pasky */
 
			FOR_EACH_OBJECT grf_load_dword(&buf);
 
			ret = true;
 
			break;
 

	
 
		case 0x0E: /* Sprite ID */
 
			FOR_EACH_OBJECT {
 
				uint8 spriteid = grf_load_byte(&buf);
 

	
 
				// cars have different custom id in the GRF file
 
				if (spriteid == 0xFF) spriteid = 0xFD;
 

	
 
				if (spriteid < 0xFD) spriteid >>= 1;
 

	
 
				rvi[i].image_index = spriteid;
 
			}
 
			break;
 

	
 
		case 0x0F: /* Cargo capacity */
 
			FOR_EACH_OBJECT rvi[i].capacity = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x10: /* Cargo type */
 
			FOR_EACH_OBJECT {
 
				uint8 cargo = grf_load_byte(&buf);
 

	
 
				if (cargo < NUM_CARGO) {
 
					rvi[i].cargo_type = cargo;
 
				} else {
 
					grfmsg(2, "RoadVehicleChangeInfo: Invalid cargo type %d, ignoring", cargo);
 
				}
 
			}
 
			break;
 

	
 
		case 0x11: /* Cost factor */
 
			FOR_EACH_OBJECT rvi[i].base_cost = grf_load_byte(&buf); // ?? is it base_cost?
 
			break;
 

	
 
		case 0x12: /* SFX */
 
			FOR_EACH_OBJECT rvi[i].sfx = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x13: /* Power in 10hp */
 
		case 0x14: /* Weight in 1/4 tons */
 
		case 0x15: /* Speed in mph*0.8 */
 
			/* TODO: Support for road vehicles realistic power
 
			 * computations (called rvpower in TTDPatch) is just
 
			 * missing in OTTD yet. --pasky */
 
			FOR_EACH_OBJECT grf_load_byte(&buf);
 
			ret = true;
 
			break;
 

	
 
		case 0x16: /* Cargos available for refitting */
 
			FOR_EACH_OBJECT ei[i].refit_mask = grf_load_dword(&buf);
 
			break;
 

	
 
		case 0x17: /* Callback mask */
 
			FOR_EACH_OBJECT ei[i].callbackmask = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x1A: /* Refit cost */
 
			FOR_EACH_OBJECT ei[i].refit_cost = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x1C: /* Miscellaneous flags */
 
			FOR_EACH_OBJECT {
 
				ei[i].misc_flags = grf_load_byte(&buf);
 
				if (HASBIT(ei[i].misc_flags, EF_USES_2CC)) _have_2cc = true;
 
			}
 
			break;
 

	
 
		case 0x1D: /* Cargo classes allowed */
 
			FOR_EACH_OBJECT cargo_allowed[ROAD_ENGINES_INDEX + engine + i] = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x1E: /* Cargo classes disallowed */
 
			FOR_EACH_OBJECT cargo_disallowed[ROAD_ENGINES_INDEX + engine + i] = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x1F: /* Long format introduction date (days since year 0) */
 
			FOR_EACH_OBJECT ei[i].base_intro = grf_load_dword(&buf);
 
			break;
 

	
 
		case 0x18: /* Tractive effort */
 
		case 0x19: /* Air drag */
 
		case 0x1B: /* Retire vehicle early */
 
			/* TODO */
 
			FOR_EACH_OBJECT grf_load_byte(&buf);
 
			ret = true;
 
			break;
 

	
 
		default:
 
			ret = true;
 
			break;
 
	}
 

	
 
	*bufp = buf;
 
	return ret;
 
}
 

	
 
static bool ShipVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len)
 
{
 
	EngineInfo *ei = &_engine_info[SHIP_ENGINES_INDEX + engine];
 
	ShipVehicleInfo *svi = &_ship_vehicle_info[engine];
 
	byte *buf = *bufp;
 
	int i;
 
	bool ret = false;
 

	
 
	//printf("e %x prop %x?\n", engine, prop);
 
	switch (prop) {
 
		case 0x08: /* Sprite ID */
 
			FOR_EACH_OBJECT {
 
				uint8 spriteid = grf_load_byte(&buf);
 

	
 
				// ships have different custom id in the GRF file
 
				if (spriteid == 0xFF) spriteid = 0xFD;
 

	
 
				if (spriteid < 0xFD) spriteid >>= 1;
 

	
 
				svi[i].image_index = spriteid;
 
			}
 
			break;
 

	
 
		case 0x09: /* Refittable */
 
			FOR_EACH_OBJECT svi[i].refittable = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0A: /* Cost factor */
 
			FOR_EACH_OBJECT svi[i].base_cost = grf_load_byte(&buf); // ?? is it base_cost?
 
			break;
 

	
 
		case 0x0B: /* Speed (1 unit is 0.5 kmh) */
 
			FOR_EACH_OBJECT svi[i].max_speed = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0C: /* Cargo type */
 
			FOR_EACH_OBJECT {
 
				uint8 cargo = grf_load_byte(&buf);
 

	
 
				// XXX: Need to consult this with patchman yet.
 
#if 0
 
				// Documentation claims this is already the
 
				// per-landscape cargo type id, but newships.grf
 
				// assume otherwise.
 
				cargo = local_cargo_id_ctype[cargo];
 
#endif
 
				if (cargo < NUM_CARGO) {
 
					svi[i].cargo_type = cargo;
 
				} else {
 
					grfmsg(2, "ShipVehicleChangeInfo: Invalid cargo type %d, ignoring", cargo);
 
				}
 
			}
 
			break;
 

	
 
		case 0x0D: /* Cargo capacity */
 
			FOR_EACH_OBJECT svi[i].capacity = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x0F: /* Running cost factor */
 
			FOR_EACH_OBJECT svi[i].running_cost = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x10: /* SFX */
 
			FOR_EACH_OBJECT svi[i].sfx = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x11: /* Cargos available for refitting */
 
			FOR_EACH_OBJECT ei[i].refit_mask = grf_load_dword(&buf);
 
			break;
 

	
 
		case 0x12: /* Callback mask */
 
			FOR_EACH_OBJECT ei[i].callbackmask = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x13: /* Refit cost */
 
			FOR_EACH_OBJECT ei[i].refit_cost = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x17: /* Miscellaneous flags */
 
			FOR_EACH_OBJECT {
 
				ei[i].misc_flags = grf_load_byte(&buf);
 
				if (HASBIT(ei[i].misc_flags, EF_USES_2CC)) _have_2cc = true;
 
			}
 
			break;
 

	
 
		case 0x18: /* Cargo classes allowed */
 
			FOR_EACH_OBJECT cargo_allowed[SHIP_ENGINES_INDEX + engine + i] = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x19: /* Cargo classes disallowed */
 
			FOR_EACH_OBJECT cargo_disallowed[SHIP_ENGINES_INDEX + engine + i] = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x1A: /* Long format introduction date (days since year 0) */
 
			FOR_EACH_OBJECT ei[i].base_intro = grf_load_dword(&buf);
 
			break;
 

	
 
		case 0x14: /* Ocean speed fraction */
 
		case 0x15: /* Canal speed fraction */
 
		case 0x16: /* Retire vehicle early */
 
			/* TODO */
 
			FOR_EACH_OBJECT grf_load_byte(&buf);
 
			ret = true;
 
			break;
 

	
 
		default:
 
			ret = true;
 
			break;
 
	}
 

	
 
	*bufp = buf;
 
	return ret;
 
}
 

	
 
static bool AircraftVehicleChangeInfo(uint engine, int numinfo, int prop, byte **bufp, int len)
 
{
 
	EngineInfo *ei = &_engine_info[AIRCRAFT_ENGINES_INDEX + engine];
 
	AircraftVehicleInfo *avi = &_aircraft_vehicle_info[engine];
 
	byte *buf = *bufp;
 
	int i;
 
	bool ret = false;
 

	
 
	//printf("e %x prop %x?\n", engine, prop);
 
	switch (prop) {
 
		case 0x08: /* Sprite ID */
 
			FOR_EACH_OBJECT {
 
				uint8 spriteid = grf_load_byte(&buf);
 

	
 
				// aircraft have different custom id in the GRF file
 
				if (spriteid == 0xFF) spriteid = 0xFD;
 

	
 
				if (spriteid < 0xFD) spriteid >>= 1;
 

	
 
				avi[i].image_index = spriteid;
 
			}
 
			break;
 

	
 
		case 0x09: /* Helicopter */
 
			FOR_EACH_OBJECT {
 
				if (grf_load_byte(&buf) == 0) {
 
					avi[i].subtype = 0;
 
				} else {
 
					SB(avi[i].subtype, 0, 1, 1);
 
				}
 
			}
 
			break;
 

	
 
		case 0x0A: /* Large */
 
			FOR_EACH_OBJECT SB(avi[i].subtype, 1, 1, (grf_load_byte(&buf) != 0 ? 1 : 0));
 
			break;
 

	
 
		case 0x0B: /* Cost factor */
 
			FOR_EACH_OBJECT avi[i].base_cost = grf_load_byte(&buf); // ?? is it base_cost?
 
			break;
 

	
 
		case 0x0C: /* Speed (1 unit is 8 mph) */
 
			FOR_EACH_OBJECT avi[i].max_speed = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0D: /* Acceleration */
 
			FOR_EACH_OBJECT avi[i].acceleration = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0E: /* Running cost factor */
 
			FOR_EACH_OBJECT avi[i].running_cost = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0F: /* Passenger capacity */
 
			FOR_EACH_OBJECT avi[i].passenger_capacity = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x11: /* Mail capacity */
 
			FOR_EACH_OBJECT avi[i].mail_capacity = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x12: /* SFX */
 
			FOR_EACH_OBJECT avi[i].sfx = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x13: /* Cargos available for refitting */
 
			FOR_EACH_OBJECT ei[i].refit_mask = grf_load_dword(&buf);
 
			break;
 

	
 
		case 0x14: /* Callback mask */
 
			FOR_EACH_OBJECT ei[i].callbackmask = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x15: /* Refit cost */
 
			FOR_EACH_OBJECT ei[i].refit_cost = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x17: /* Miscellaneous flags */
 
			FOR_EACH_OBJECT {
 
				ei[i].misc_flags = grf_load_byte(&buf);
 
				if (HASBIT(ei[i].misc_flags, EF_USES_2CC)) _have_2cc = true;
 
			}
 
			break;
 

	
 
		case 0x18: /* Cargo classes allowed */
 
			FOR_EACH_OBJECT cargo_allowed[AIRCRAFT_ENGINES_INDEX + engine + i] = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x19: /* Cargo classes disallowed */
 
			FOR_EACH_OBJECT cargo_disallowed[AIRCRAFT_ENGINES_INDEX + engine + i] = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x1A: /* Long format introduction date (days since year 0) */
 
			FOR_EACH_OBJECT ei[i].base_intro = grf_load_dword(&buf);
 
			break;
 

	
 
		case 0x16: /* Retire vehicle early */
 
			/* TODO */
 
			FOR_EACH_OBJECT grf_load_byte(&buf);
 
			ret = true;
 
			break;
 

	
 
		default:
 
			ret = true;
 
			break;
 
	}
 

	
 
	*bufp = buf;
 
	return ret;
 
}
 

	
 
static bool StationChangeInfo(uint stid, int numinfo, int prop, byte **bufp, int len)
 
{
 
	StationSpec **statspec;
 
	byte *buf = *bufp;
 
	int i;
 
	bool ret = false;
 

	
 
	if (stid + numinfo > MAX_STATIONS) {
 
		grfmsg(1, "StationChangeInfo: Station %u is invalid, max %u, ignoring", stid + numinfo, MAX_STATIONS);
 
		return false;
 
	}
 

	
 
	/* Allocate station specs if necessary */
 
	if (_cur_grffile->stations == NULL) _cur_grffile->stations = calloc(MAX_STATIONS, sizeof(*_cur_grffile->stations));
 

	
 
	statspec = &_cur_grffile->stations[stid];
 

	
 
	if (prop != 0x08) {
 
		/* Check that all stations we are modifying are defined. */
 
		FOR_EACH_OBJECT {
 
			if (statspec[i] == NULL) {
 
				grfmsg(2, "StationChangeInfo: Attempt to modify undefined station %u, ignoring", stid + i);
 
				return false;
 
			}
 
		}
 
	}
 

	
 
	switch (prop) {
 
		case 0x08: /* Class ID */
 
			FOR_EACH_OBJECT {
 
				uint32 classid;
 

	
 
				/* Property 0x08 is special; it is where the station is allocated */
 
				if (statspec[i] == NULL) statspec[i] = calloc(1, sizeof(*statspec[i]));
 

	
 
				/* Swap classid because we read it in BE meaning WAYP or DFLT */
 
				classid = grf_load_dword(&buf);
 
				statspec[i]->sclass = AllocateStationClass(BSWAP32(classid));
 
			}
 
			break;
 

	
 
		case 0x09: /* Define sprite layout */
 
			FOR_EACH_OBJECT {
 
				StationSpec *statspec = _cur_grffile->stations[stid + i];
 
				uint t;
 

	
 
				statspec->tiles = grf_load_extended(&buf);
 
				statspec->renderdata = calloc(statspec->tiles, sizeof(*statspec->renderdata));
 
				statspec->copied_renderdata = false;
 

	
 
				for (t = 0; t < statspec->tiles; t++) {
 
					DrawTileSprites *dts = &statspec->renderdata[t];
 
					uint seq_count = 0;
 

	
 
					dts->seq = NULL;
 
					dts->ground_sprite = grf_load_dword(&buf);
 
					if (dts->ground_sprite == 0) continue;
 

	
 
					while (buf < *bufp + len) {
 
						DrawTileSeqStruct *dtss;
 

	
 
						// no relative bounding box support
 
						dts->seq = realloc((void*)dts->seq, ++seq_count * sizeof(DrawTileSeqStruct));
 
						dtss = (DrawTileSeqStruct*) &dts->seq[seq_count - 1];
 

	
 
						dtss->delta_x = grf_load_byte(&buf);
 
						if ((byte) dtss->delta_x == 0x80) break;
 
						dtss->delta_y = grf_load_byte(&buf);
 
						dtss->delta_z = grf_load_byte(&buf);
 
						dtss->size_x = grf_load_byte(&buf);
 
						dtss->size_y = grf_load_byte(&buf);
 
						dtss->size_z = grf_load_byte(&buf);
 
						dtss->image = grf_load_dword(&buf);
 

	
 
						/* Remap flags as ours collide */
 
						if (HASBIT(dtss->image, 31)) {
 
							CLRBIT(dtss->image, 31);
 
							SETBIT(dtss->image, 30);
 
						}
 
						if (HASBIT(dtss->image, 14)) {
 
							CLRBIT(dtss->image, 14);
 
							SETBIT(dtss->image, 31);
 
						}
 
					}
 
				}
 
			}
 
			break;
 

	
 
		case 0x0A: /* Copy sprite layout */
 
			FOR_EACH_OBJECT {
 
				StationSpec *statspec = _cur_grffile->stations[stid + i];
 
				byte srcid = grf_load_byte(&buf);
 
				const StationSpec *srcstatspec = _cur_grffile->stations[srcid];
 

	
 
				statspec->tiles = srcstatspec->tiles;
 
				statspec->renderdata = srcstatspec->renderdata;
 
				statspec->copied_renderdata = true;
 
			}
 
			break;
 

	
 
		case 0x0B: /* Callback mask */
 
			FOR_EACH_OBJECT statspec[i]->callbackmask = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0C: /* Disallowed number of platforms */
 
			FOR_EACH_OBJECT statspec[i]->disallowed_platforms = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0D: /* Disallowed platform lengths */
 
			FOR_EACH_OBJECT statspec[i]->disallowed_lengths = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0E: /* Define custom layout */
 
			FOR_EACH_OBJECT {
 
				StationSpec *statspec = _cur_grffile->stations[stid + i];
 

	
 
				statspec->copied_layouts = false;
 

	
 
				while (buf < *bufp + len) {
 
					byte length = grf_load_byte(&buf);
 
					byte number = grf_load_byte(&buf);
 
					StationLayout layout;
 
					uint l, p;
 

	
 
					if (length == 0 || number == 0) break;
 

	
 
					//debug("l %d > %d ?", length, stat->lengths);
 
					if (length > statspec->lengths) {
 
						statspec->platforms = realloc(statspec->platforms, length);
 
						memset(statspec->platforms + statspec->lengths, 0, length - statspec->lengths);
 

	
 
						statspec->layouts = realloc(statspec->layouts, length * sizeof(*statspec->layouts));
 
						memset(statspec->layouts + statspec->lengths, 0,
 
						       (length - statspec->lengths) * sizeof(*statspec->layouts));
 

	
 
						statspec->lengths = length;
 
					}
 
					l = length - 1; // index is zero-based
 

	
 
					//debug("p %d > %d ?", number, stat->platforms[l]);
 
					if (number > statspec->platforms[l]) {
 
						statspec->layouts[l] = realloc(statspec->layouts[l],
 
						                               number * sizeof(**statspec->layouts));
 
						// We expect NULL being 0 here, but C99 guarantees that.
 
						memset(statspec->layouts[l] + statspec->platforms[l], 0,
 
						       (number - statspec->platforms[l]) * sizeof(**statspec->layouts));
 

	
 
						statspec->platforms[l] = number;
 
					}
 

	
 
					p = 0;
 
					layout = malloc(length * number);
 
					for (l = 0; l < length; l++) {
 
						for (p = 0; p < number; p++) {
 
							layout[l * number + p] = grf_load_byte(&buf);
 
						}
 
					}
 

	
 
					l--;
 
					p--;
 
					free(statspec->layouts[l][p]);
 
					statspec->layouts[l][p] = layout;
 
				}
 
			}
 
			break;
 

	
 
		case 0x0F: /* Copy custom layout */
 
			FOR_EACH_OBJECT {
 
				StationSpec *statspec = _cur_grffile->stations[stid + i];
 
				byte srcid = grf_load_byte(&buf);
 
				const StationSpec *srcstatspec = _cur_grffile->stations[srcid];
 

	
 
				statspec->lengths   = srcstatspec->lengths;
 
				statspec->platforms = srcstatspec->platforms;
 
				statspec->layouts   = srcstatspec->layouts;
 
				statspec->copied_layouts = true;
 
			}
 
			break;
 

	
 
		case 0x10: /* Little/lots cargo threshold */
 
			FOR_EACH_OBJECT statspec[i]->cargo_threshold = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x11: /* Pylon placement */
 
			FOR_EACH_OBJECT statspec[i]->pylons = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x12: /* Cargo types for random triggers */
 
			FOR_EACH_OBJECT statspec[i]->cargo_triggers = grf_load_dword(&buf);
 
			break;
 

	
 
		case 0x13: /* General flags */
 
			FOR_EACH_OBJECT statspec[i]->flags = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x14: /* Overhead wire placement */
 
			FOR_EACH_OBJECT statspec[i]->wires = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x15: /* Blocked tiles */
 
			FOR_EACH_OBJECT statspec[i]->blocked = grf_load_byte(&buf);
 
			break;
 

	
 
		default:
 
			ret = true;
 
			break;
 
	}
 

	
 
	*bufp = buf;
 
	return ret;
 
}
 

	
 
static bool BridgeChangeInfo(uint brid, int numinfo, int prop, byte **bufp, int len)
 
{
 
	byte *buf = *bufp;
 
	int i;
 
	bool ret = false;
 

	
 
	switch (prop) {
 
		case 0x08: /* Year of availability */
 
			FOR_EACH_OBJECT _bridge[brid + i].avail_year = ORIGINAL_BASE_YEAR + grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x09: /* Minimum length */
 
			FOR_EACH_OBJECT _bridge[brid + i].min_length = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0A: /* Maximum length */
 
			FOR_EACH_OBJECT _bridge[brid + i].max_length = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0B: /* Cost factor */
 
			FOR_EACH_OBJECT _bridge[brid + i].price = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0C: /* Maximum speed */
 
			FOR_EACH_OBJECT _bridge[brid + i].speed = grf_load_word(&buf);
 
			break;
 

	
 
		case 0x0D: /* Bridge sprite tables */
 
			FOR_EACH_OBJECT {
 
				Bridge *bridge = &_bridge[brid + i];
 
				byte tableid = grf_load_byte(&buf);
 
				byte numtables = grf_load_byte(&buf);
 

	
 
				if (bridge->sprite_table == NULL) {
 
					/* Allocate memory for sprite table pointers and zero out */
 
					bridge->sprite_table = calloc(7, sizeof(*bridge->sprite_table));
 
				}
 

	
 
				for (; numtables-- != 0; tableid++) {
 
					byte sprite;
 

	
 
					if (tableid >= 7) { // skip invalid data
 
						grfmsg(1, "BridgeChangeInfo: Table %d >= 7, skipping", tableid);
 
						for (sprite = 0; sprite < 32; sprite++) grf_load_dword(&buf);
 
						continue;
 
					}
 

	
 
					if (bridge->sprite_table[tableid] == NULL) {
 
						bridge->sprite_table[tableid] = malloc(32 * sizeof(**bridge->sprite_table));
 
					}
 

	
 
					for (sprite = 0; sprite < 32; sprite++)
 
						bridge->sprite_table[tableid][sprite] = grf_load_dword(&buf);
 
				}
 
			}
 
			break;
 

	
 
		case 0x0E: /* Flags; bit 0 - disable far pillars */
 
			FOR_EACH_OBJECT _bridge[brid + i].flags = grf_load_byte(&buf);
 
			break;
 

	
 
		case 0x0F: /* Long format year of availability (year since year 0) */
 
			FOR_EACH_OBJECT _bridge[brid + i].avail_year = clamp(grf_load_dword(&buf), MIN_YEAR, MAX_YEAR);
 
			break;
 

	
 
		default:
 
			ret = true;
 
	}
 

	
 
	*bufp = buf;
 
	return ret;
 
}
 

	
 
static bool GlobalVarChangeInfo(uint gvid, int numinfo, int prop, byte **bufp, int len)
 
{
 
	byte *buf = *bufp;
 
	int i;
 
	bool ret = false;
 

	
 
	switch (prop) {
 
		case 0x08: /* Cost base factor */
 
			FOR_EACH_OBJECT {
 
				byte factor = grf_load_byte(&buf);
 
				uint price = gvid + i;
 

	
 
				if (price < NUM_PRICES) {
 
					SetPriceBaseMultiplier(price, factor);
 
				} else {
 
					grfmsg(1, "GlobalVarChangeInfo: Price %d out of range, ignoring", price);
 
				}
 
			}
 
			break;
 

	
 
		case 0x0A: // Currency display names
 
			FOR_EACH_OBJECT {
 
				uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
 
				StringID newone = GetGRFStringID(_cur_grffile->grfid, grf_load_word(&buf));
 

	
 
				if ((newone != STR_UNDEFINED) && (curidx < NUM_CURRENCY)) {
 
					_currency_specs[curidx].name = newone;
 
				}
 
			}
 
			break;
 

	
 
		case 0x0B: // Currency multipliers
 
			FOR_EACH_OBJECT {
 
				uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
 
				uint32 rate = grf_load_dword(&buf);
 

	
 
				if (curidx < NUM_CURRENCY) {
 
					/* TTDPatch uses a multiple of 1000 for its conversion calculations,
 
					 * which OTTD does not. For this reason, divide grf value by 1000,
 
					 * to be compatible */
 
					_currency_specs[curidx].rate = rate / 1000;
 
				} else {
 
					grfmsg(1, "GlobalVarChangeInfo: Currency multipliers %d out of range, ignoring", curidx);
 
				}
 
			}
 
			break;
 

	
 
		case 0x0C: // Currency options
 
			FOR_EACH_OBJECT {
 
				uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
 
				uint16 options = grf_load_word(&buf);
 

	
 
				if (curidx < NUM_CURRENCY) {
 
					_currency_specs[curidx].separator = GB(options, 0, 8);
 
					/* By specifying only one bit, we prevent errors,
 
					 * since newgrf specs said that only 0 and 1 can be set for symbol_pos */
 
					_currency_specs[curidx].symbol_pos = GB(options, 8, 1);
 
				} else {
 
					grfmsg(1, "GlobalVarChangeInfo: Currency option %d out of range, ignoring", curidx);
 
				}
 
			}
 
			break;
 

	
 
		case 0x0D: // Currency prefix symbol
 
			FOR_EACH_OBJECT {
 
				uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
 
				uint32 tempfix = grf_load_dword(&buf);
 

	
 
				if (curidx < NUM_CURRENCY) {
 
					memcpy(_currency_specs[curidx].prefix,&tempfix,4);
 
					_currency_specs[curidx].prefix[4] = 0;
 
				} else {
 
					grfmsg(1, "GlobalVarChangeInfo: Currency symbol %d out of range, ignoring", curidx);
 
				}
 
			}
 
			break;
 

	
 
		case 0x0E: // Currency suffix symbol
 
			FOR_EACH_OBJECT {
 
				uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
 
				uint32 tempfix = grf_load_dword(&buf);
 

	
 
				if (curidx < NUM_CURRENCY) {
 
					memcpy(&_currency_specs[curidx].suffix,&tempfix,4);
 
					_currency_specs[curidx].suffix[4] = 0;
 
				} else {
 
					grfmsg(1, "GlobalVarChangeInfo: Currency symbol %d out of range, ignoring", curidx);
 
				}
 
			}
 
			break;
 

	
 
		case 0x0F: //  Euro introduction dates
 
			FOR_EACH_OBJECT {
 
				uint curidx = GetNewgrfCurrencyIdConverted(gvid + i);
 
				Year year_euro = grf_load_word(&buf);
 

	
 
				if (curidx < NUM_CURRENCY) {
 
					_currency_specs[curidx].to_euro = year_euro;
 
				} else {
 
					grfmsg(1, "GlobalVarChangeInfo: Euro intro date %d out of range, ignoring", curidx);
 
				}
 
			}
 
			break;
 

	
 
		case 0x09: // Cargo translation table
 
		case 0x10: // 12 * 32 * B Snow line height table
 
		default:
 
			ret = true;
 
	}
 

	
 
	*bufp = buf;
 
	return ret;
 
}
 

	
 
static bool SoundEffectChangeInfo(uint sid, int numinfo, int prop, byte **bufp, int len)
 
{
 
	byte *buf = *bufp;
 
	int i;
 
	bool ret = false;
 

	
 
	if (_cur_grffile->sound_offset == 0) {
 
		grfmsg(1, "SoundEffectChangeInfo: No effects defined, skipping");
 
		return false;
 
	}
 

	
 
	switch (prop) {
 
		case 0x08: /* Relative volume */
 
			FOR_EACH_OBJECT {
 
				uint sound = sid + i + _cur_grffile->sound_offset - GetNumOriginalSounds();
 

	
 
				if (sound >= GetNumSounds()) {
 
					grfmsg(1, "SoundEffectChangeInfo: Sound %d not defined (max %d)", sound, GetNumSounds());
 
				} else {
 
					GetSound(sound)->volume = grf_load_byte(&buf);
 
				}
 
			}
 
			break;
 

	
 
		case 0x09: /* Priority */
 
			FOR_EACH_OBJECT {
 
				uint sound = sid + i + _cur_grffile->sound_offset - GetNumOriginalSounds();
 

	
 
				if (sound >= GetNumSounds()) {
 
					grfmsg(1, "SoundEffectChangeInfo: Sound %d not defined (max %d)", sound, GetNumSounds());
 
				} else {
 
					GetSound(sound)->priority = grf_load_byte(&buf);
 
				}
 
			}
 
			break;
 

	
 
		case 0x0A: /* Override old sound */
 
			FOR_EACH_OBJECT {
 
				uint sound = sid + i + _cur_grffile->sound_offset - GetNumOriginalSounds();
 
				uint orig_sound = grf_load_byte(&buf);
 

	
 
				if (sound >= GetNumSounds() || orig_sound >= GetNumSounds()) {
 
					grfmsg(1, "SoundEffectChangeInfo: Sound %d or %d not defined (max %d)", sound, orig_sound, GetNumSounds());
 
				} else {
 
					FileEntry *newfe = GetSound(sound);
 
					FileEntry *oldfe = GetSound(orig_sound);
 

	
 
					/* Literally copy the data of the new sound over the original */
 
					*oldfe = *newfe;
 
				}
 
			}
 
			break;
 

	
 
		default:
 
			ret = true;
 
	}
 

	
 
	*bufp = buf;
 
	return ret;
 
}
 

	
 
/* Action 0x00 */
 
static void FeatureChangeInfo(byte *buf, int len)
 
{
 
	byte *bufend = buf + len;
 
	int i;
 

	
 
	/* <00> <feature> <num-props> <num-info> <id> (<property <new-info>)...
 
	 *
 
	 * B feature       0, 1, 2 or 3 for trains, road vehicles, ships or planes
 
	 *                 4 for defining new train station sets
 
	 * B num-props     how many properties to change per vehicle/station
 
	 * B num-info      how many vehicles/stations to change
 
	 * B id            ID of first vehicle/station to change, if num-info is
 
	 *                 greater than one, this one and the following
 
	 *                 vehicles/stations will be changed
 
	 * B property      what property to change, depends on the feature
 
	 * V new-info      new bytes of info (variable size; depends on properties) */
 
	/* TODO: Bridges, town houses. */
 

	
 
	static const VCI_Handler handler[] = {
 
		/* GSF_TRAIN */        RailVehicleChangeInfo,
 
		/* GSF_ROAD */         RoadVehicleChangeInfo,
 
		/* GSF_SHIP */         ShipVehicleChangeInfo,
 
		/* GSF_AIRCRAFT */     AircraftVehicleChangeInfo,
 
		/* GSF_STATION */      StationChangeInfo,
 
		/* GSF_CANAL */        NULL,
 
		/* GSF_BRIDGE */       BridgeChangeInfo,
 
		/* GSF_TOWNHOUSE */    NULL,
 
		/* GSF_GLOBALVAR */    GlobalVarChangeInfo,
 
		/* GSF_INDUSTRYTILES */NULL,
 
		/* GSF_INDUSTRIES */   NULL,
 
		/* GSF_CARGOS */       NULL,
 
		/* GSF_SOUNDFX */      SoundEffectChangeInfo,
 
	};
 

	
 
	uint8 feature;
 
	uint8 numprops;
 
	uint8 numinfo;
 
	byte engine;
 
	EngineInfo *ei = NULL;
 

	
 
	if (len == 1) {
 
		grfmsg(8, "Silently ignoring one-byte special sprite 0x00");
 
		return;
 
	}
 

	
 
	check_length(len, 6, "FeatureChangeInfo");
 
	buf++;
 
	feature  = grf_load_byte(&buf);
 
	numprops = grf_load_byte(&buf);
 
	numinfo  = grf_load_byte(&buf);
 
	engine   = grf_load_byte(&buf);
 

	
 
	grfmsg(6, "FeatureChangeInfo: feature %d, %d properties, to apply to %d+%d",
 
	               feature, numprops, engine, numinfo);
 

	
 
	if (feature >= lengthof(handler) || handler[feature] == NULL) {
 
		grfmsg(1, "FeatureChangeInfo: Unsupported feature %d, skipping", feature);
 
		return;
 
	}
 

	
 
	if (feature <= GSF_AIRCRAFT) {
 
		if (engine + numinfo > _vehcounts[feature]) {
 
			grfmsg(0, "FeatureChangeInfo: Last engine ID %d out of bounds (max %d), skipping", engine + numinfo, _vehcounts[feature]);
 
			return;
 
		}
 
		ei = &_engine_info[engine + _vehshifts[feature]];
 
	}
 

	
 
	while (numprops-- && buf < bufend) {
 
		uint8 prop = grf_load_byte(&buf);
 
		bool ignoring = false;
 

	
 
		switch (feature) {
 
			case GSF_TRAIN:
 
			case GSF_ROAD:
 
			case GSF_SHIP:
 
			case GSF_AIRCRAFT:
 
				/* Common properties for vehicles */
 
				switch (prop) {
 
					case 0x00: /* Introduction date */
 
						FOR_EACH_OBJECT ei[i].base_intro = grf_load_word(&buf) + DAYS_TILL_ORIGINAL_BASE_YEAR;
 
						break;
 

	
 
					case 0x02: /* Decay speed */
 
						FOR_EACH_OBJECT SB(ei[i].unk2, 0, 7, grf_load_byte(&buf) & 0x7F);
 
						break;
 

	
 
					case 0x03: /* Vehicle life */
 
						FOR_EACH_OBJECT ei[i].lifelength = grf_load_byte(&buf);
 
						break;
 

	
 
					case 0x04: /* Model life */
 
						FOR_EACH_OBJECT ei[i].base_life = grf_load_byte(&buf);
 
						break;
 

	
 
					case 0x06: /* Climates available */
 
						FOR_EACH_OBJECT ei[i].climates = grf_load_byte(&buf);
 
						break;
 

	
 
					case 0x07: /* Loading speed */
 
						/* Hyronymus explained me what does
 
						 * this mean and insists on having a
 
						 * credit ;-). --pasky */
 
						FOR_EACH_OBJECT ei[i].load_amount = grf_load_byte(&buf);
 
						break;
 

	
 
					default:
 
						if (handler[feature](engine, numinfo, prop, &buf, bufend - buf))
 
							ignoring = true;
 
						break;
 
				}
 
				break;
 

	
 
			default:
 
				if (handler[feature](engine, numinfo, prop, &buf, bufend - buf))
 
					ignoring = true;
 
				break;
 
		}
 

	
 
		if (ignoring) grfmsg(2, "FeatureChangeInfo: Ignoring property 0x%02X (not implemented)", prop);
 
	}
 
}
 

	
 
/* Action 0x00 (GLS_SAFETYSCAN) */
 
static void SafeChangeInfo(byte *buf, int len)
 
{
 
	uint8 feature;
 
	uint8 numprops;
 
	uint8 numinfo;
 
	uint8 index;
 

	
 
	check_length(len, 6, "SafeChangeInfo");
 
	buf++;
 
	feature  = grf_load_byte(&buf);
 
	numprops = grf_load_byte(&buf);
 
	numinfo  = grf_load_byte(&buf);
 
	index    = grf_load_byte(&buf);
 

	
 
	if (feature == GSF_BRIDGE && numprops == 1) {
 
		uint8 prop = grf_load_byte(&buf);
 
		/* Bridge property 0x0D is redefinition of sprite layout tables, which
 
		 * is considered safe. */
 
		if (prop == 0x0D) return;
 
	}
 

	
 
	SETBIT(_cur_grfconfig->flags, GCF_UNSAFE);
 

	
 
	/* Skip remainder of GRF */
 
	_skip_sprites = -1;
 
}
 

	
 
#undef FOR_EACH_OBJECT
 

	
 
/**
 
 * Creates a spritegroup representing a callback result
 
 * @param value The value that was used to represent this callback result
 
 * @return A spritegroup representing that callback result
 
 */
 
static const SpriteGroup* NewCallBackResultSpriteGroup(uint16 value)
 
{
 
	SpriteGroup *group = AllocateSpriteGroup();
 

	
 
	group->type = SGT_CALLBACK;
 

	
 
	// Old style callback results have the highest byte 0xFF so signify it is a callback result
 
	// New style ones only have the highest bit set (allows 15-bit results, instead of just 8)
 
	if ((value >> 8) == 0xFF) {
 
		value &= ~0xFF00;
 
	} else {
 
		value &= ~0x8000;
 
	}
 

	
 
	group->g.callback.result = value;
 

	
 
	return group;
 
}
 

	
 
/**
 
 * Creates a spritegroup representing a sprite number result.
 
 * @param value The sprite number.
 
 * @param sprites The number of sprites per set.
 
 * @return A spritegroup representing the sprite number result.
 
 */
 
static const SpriteGroup* NewResultSpriteGroup(SpriteID sprite, byte num_sprites)
 
{
 
	SpriteGroup *group = AllocateSpriteGroup();
 
	group->type = SGT_RESULT;
 
	group->g.result.sprite = sprite;
 
	group->g.result.num_sprites = num_sprites;
 
	return group;
 
}
 

	
 
/* Action 0x01 */
 
static void NewSpriteSet(byte *buf, int len)
 
{
 
	/* <01> <feature> <num-sets> <num-ent>
 
	 *
 
	 * B feature       feature to define sprites for
 
	 *                 0, 1, 2, 3: veh-type, 4: train stations
 
	 * B num-sets      number of sprite sets
 
	 * E num-ent       how many entries per sprite set
 
	 *                 For vehicles, this is the number of different
 
	 *                         vehicle directions in each sprite set
 
	 *                         Set num-dirs=8, unless your sprites are symmetric.
 
	 *                         In that case, use num-dirs=4.
 
	 *                 For stations, must be 12 (hex) for the eighteen
 
	 *                         different sprites that make up a station */
 
	/* TODO: No stations support. */
 
	uint8 feature;
 
	uint num_sets;
 
	uint num_ents;
 
	uint i;
 

	
 
	check_length(len, 4, "NewSpriteSet");
 
	buf++;
 
	feature  = grf_load_byte(&buf);
 
	num_sets = grf_load_byte(&buf);
 
	num_ents = grf_load_extended(&buf);
 

	
 
	_cur_grffile->spriteset_start = _cur_spriteid;
 
	_cur_grffile->spriteset_feature = feature;
 
	_cur_grffile->spriteset_numsets = num_sets;
 
	_cur_grffile->spriteset_numents = num_ents;
 

	
 
	grfmsg(7, "New sprite set at %d of type %d, consisting of %d sets with %d views each (total %d)",
 
		_cur_spriteid, feature, num_sets, num_ents, num_sets * num_ents
 
	);
 

	
 
	for (i = 0; i < num_sets * num_ents; i++) {
 
		LoadNextSprite(_cur_spriteid++, _file_index);
 
		_nfo_line++;
 
	}
 
}
 

	
 
/* Helper function to either create a callback or link to a previously
 
 * defined spritegroup. */
 
static const SpriteGroup* GetGroupFromGroupID(byte setid, byte type, uint16 groupid)
 
{
 
	if (HASBIT(groupid, 15)) return NewCallBackResultSpriteGroup(groupid);
 

	
 
	if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) {
 
		grfmsg(1, "NewSpriteGroup(0x%02X:0x%02X): Groupid 0x%04X does not exist, leaving empty", setid, type, groupid);
 
		return NULL;
 
	}
 

	
 
	return _cur_grffile->spritegroups[groupid];
 
}
 

	
 
/* Helper function to either create a callback or a result sprite group. */
 
static const SpriteGroup* CreateGroupFromGroupID(byte feature, byte setid, byte type, uint16 spriteid, uint16 num_sprites)
 
{
 
	if (HASBIT(spriteid, 15)) return NewCallBackResultSpriteGroup(spriteid);
 

	
 
	if (spriteid >= _cur_grffile->spriteset_numsets) {
 
		grfmsg(1, "NewSpriteGroup(0x%02X:0x%02X): Sprite set %u invalid, max %u", setid, type, spriteid, _cur_grffile->spriteset_numsets);
 
		return NULL;
 
	}
 

	
 
	/* Check if the sprite is within range. This can fail if the Action 0x01
 
	 * is skipped, as TTDPatch mandates that Action 0x02s must be processed.
 
	 * We don't have that rule, but must live by the Patch... */
 
	if (_cur_grffile->spriteset_start + spriteid * num_sprites + num_sprites > _cur_spriteid) {
 
		grfmsg(1, "NewSpriteGroup(0x%02X:0x%02X): Real Sprite IDs 0x%04X - 0x%04X do not (all) exist (max 0x%04X), leaving empty",
 
				setid, type,
 
				_cur_grffile->spriteset_start + spriteid * num_sprites,
 
				_cur_grffile->spriteset_start + spriteid * num_sprites + num_sprites - 1, _cur_spriteid - 1);
 
		return NULL;
 
	}
 

	
 
	if (feature != _cur_grffile->spriteset_feature) {
 
		grfmsg(1, "NewSpriteGroup(0x%02X:0x%02X): Sprite set feature 0x%02X does not match action feature 0x%02X, skipping",
 
				_cur_grffile->spriteset_feature, feature);
 
		return NULL;
 
	}
 

	
 
	return NewResultSpriteGroup(_cur_grffile->spriteset_start + spriteid * num_sprites, num_sprites);
 
}
 

	
 
/* Action 0x02 */
 
static void NewSpriteGroup(byte *buf, int len)
 
{
 
	/* <02> <feature> <set-id> <type/num-entries> <feature-specific-data...>
 
	 *
 
	 * B feature       see action 1
 
	 * B set-id        ID of this particular definition
 
	 * B type/num-entries
 
	 *                 if 80 or greater, this is a randomized or variational
 
	 *                 list definition, see below
 
	 *                 otherwise it specifies a number of entries, the exact
 
	 *                 meaning depends on the feature
 
	 * V feature-specific-data (huge mess, don't even look it up --pasky) */
 
	uint8 feature;
 
	uint8 setid;
 
	uint8 type;
 
	SpriteGroup *group = NULL;
 
	byte *bufend = buf + len;
 

	
 
	check_length(len, 5, "NewSpriteGroup");
 
	buf++;
 

	
 
	feature = grf_load_byte(&buf);
 
	setid   = grf_load_byte(&buf);
 
	type    = grf_load_byte(&buf);
 

	
 
	if (setid >= _cur_grffile->spritegroups_count) {
 
		// Allocate memory for new sprite group references.
 
		_cur_grffile->spritegroups = realloc(_cur_grffile->spritegroups, (setid + 1) * sizeof(*_cur_grffile->spritegroups));
 
		// Initialise new space to NULL
 
		for (; _cur_grffile->spritegroups_count < (setid + 1); _cur_grffile->spritegroups_count++)
 
			_cur_grffile->spritegroups[_cur_grffile->spritegroups_count] = NULL;
 
	}
 

	
 
	switch (type) {
 
		/* Deterministic Sprite Group */
 
		case 0x81: // Self scope, byte
 
		case 0x82: // Parent scope, byte
 
		case 0x85: // Self scope, word
 
		case 0x86: // Parent scope, word
 
		case 0x89: // Self scope, dword
 
		case 0x8A: // Parent scope, dword
 
		{
 
			byte varadjust;
 
			byte varsize;
 
			uint i;
 

	
 
			/* Check we can load the var size parameter */
 
			check_length(bufend - buf, 1, "NewSpriteGroup (Deterministic) (1)");
 

	
 
			group = AllocateSpriteGroup();
 
			group->type = SGT_DETERMINISTIC;
 
			group->g.determ.var_scope = HASBIT(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
 

	
 
			switch (GB(type, 2, 2)) {
 
				default: NOT_REACHED();
 
				case 0: group->g.determ.size = DSG_SIZE_BYTE;  varsize = 1; break;
 
				case 1: group->g.determ.size = DSG_SIZE_WORD;  varsize = 2; break;
 
				case 2: group->g.determ.size = DSG_SIZE_DWORD; varsize = 4; break;
 
			}
 

	
 
			check_length(bufend - buf, 5 + varsize, "NewSpriteGroup (Deterministic) (2)");
 

	
 
			/* Loop through the var adjusts. Unfortunately we don't know how many we have
 
			 * from the outset, so we shall have to keep reallocing. */
 
			do {
 
				DeterministicSpriteGroupAdjust *adjust;
 

	
 
				if (group->g.determ.num_adjusts > 0) {
 
					check_length(bufend - buf, 2 + varsize + 3, "NewSpriteGroup (Deterministic) (3)");
 
				}
 

	
 
				group->g.determ.num_adjusts++;
 
				group->g.determ.adjusts = realloc(group->g.determ.adjusts, group->g.determ.num_adjusts * sizeof(*group->g.determ.adjusts));
 

	
 
				adjust = &group->g.determ.adjusts[group->g.determ.num_adjusts - 1];
 

	
 
				/* The first var adjust doesn't have an operation specified, so we set it to add. */
 
				adjust->operation = group->g.determ.num_adjusts == 1 ? DSGA_OP_ADD : grf_load_byte(&buf);
 
				adjust->variable  = grf_load_byte(&buf);
 
				adjust->parameter = IS_BYTE_INSIDE(adjust->variable, 0x60, 0x80) ? grf_load_byte(&buf) : 0;
 

	
 
				varadjust = grf_load_byte(&buf);
 
				adjust->shift_num = GB(varadjust, 0, 5);
 
				adjust->type      = GB(varadjust, 6, 2);
 
				adjust->and_mask  = grf_load_var(varsize, &buf);
 

	
 
				if (adjust->type != DSGA_TYPE_NONE) {
 
					adjust->add_val    = grf_load_var(varsize, &buf);
 
					adjust->divmod_val = grf_load_var(varsize, &buf);
 
				} else {
 
					adjust->add_val    = 0;
 
					adjust->divmod_val = 0;
 
				}
 

	
 
				/* Continue reading var adjusts while bit 5 is set. */
 
			} while (HASBIT(varadjust, 5));
 

	
 
			group->g.determ.num_ranges = grf_load_byte(&buf);
 
			group->g.determ.ranges = calloc(group->g.determ.num_ranges, sizeof(*group->g.determ.ranges));
 

	
 
			check_length(bufend - buf, 2 + (2 + 2 * varsize) * group->g.determ.num_ranges, "NewSpriteGroup (Deterministic)");
 

	
 
			for (i = 0; i < group->g.determ.num_ranges; i++) {
 
				group->g.determ.ranges[i].group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
 
				group->g.determ.ranges[i].low   = grf_load_var(varsize, &buf);
 
				group->g.determ.ranges[i].high  = grf_load_var(varsize, &buf);
 
			}
 

	
 
			group->g.determ.default_group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
 
			break;
 
		}
 

	
 
		/* Randomized Sprite Group */
 
		case 0x80: // Self scope
 
		case 0x83: // Parent scope
 
		{
 
			byte triggers;
 
			uint i;
 

	
 
			check_length(bufend - buf, 7, "NewSpriteGroup (Randomized) (1)");
 

	
 
			group = AllocateSpriteGroup();
 
			group->type = SGT_RANDOMIZED;
 
			group->g.random.var_scope = HASBIT(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
 

	
 
			triggers = grf_load_byte(&buf);
 
			group->g.random.triggers       = GB(triggers, 0, 7);
 
			group->g.random.cmp_mode       = HASBIT(triggers, 7) ? RSG_CMP_ALL : RSG_CMP_ANY;
 
			group->g.random.lowest_randbit = grf_load_byte(&buf);
 
			group->g.random.num_groups     = grf_load_byte(&buf);
 
			group->g.random.groups = calloc(group->g.random.num_groups, sizeof(*group->g.random.groups));
 

	
 
			check_length(bufend - buf, 2 * group->g.random.num_groups, "NewSpriteGroup (Randomized) (2)");
 

	
 
			for (i = 0; i < group->g.random.num_groups; i++) {
 
				group->g.random.groups[i] = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
 
			}
 

	
 
			break;
 
		}
 

	
 
		/* Neither a variable or randomized sprite group... must be a real group */
 
		default:
 
		{
 

	
 

	
 
			switch (feature) {
 
				case GSF_TRAIN:
 
				case GSF_ROAD:
 
				case GSF_SHIP:
 
				case GSF_AIRCRAFT:
 
				case GSF_STATION:
 
				{
 
					byte sprites     = _cur_grffile->spriteset_numents;
 
					byte num_loaded  = type;
 
					byte num_loading = grf_load_byte(&buf);
 
					uint i;
 

	
 
					if (_cur_grffile->spriteset_start == 0) {
 
						grfmsg(0, "NewSpriteGroup: No sprite set to work on! Skipping");
 
						return;
 
					}
 

	
 
					check_length(bufend - buf, 2 * num_loaded + 2 * num_loading, "NewSpriteGroup (Real) (1)");
 

	
 
					group = AllocateSpriteGroup();
 
					group->type = SGT_REAL;
 

	
 
					group->g.real.num_loaded  = num_loaded;
 
					group->g.real.num_loading = num_loading;
 
					if (num_loaded  > 0) group->g.real.loaded  = calloc(num_loaded,  sizeof(*group->g.real.loaded));
 
					if (num_loading > 0) group->g.real.loading = calloc(num_loading, sizeof(*group->g.real.loading));
 

	
 
					grfmsg(6, "NewSpriteGroup: New SpriteGroup 0x%02X, %u views, %u loaded, %u loading",
 
							setid, sprites, num_loaded, num_loading);
 

	
 
					for (i = 0; i < num_loaded; i++) {
 
						uint16 spriteid = grf_load_word(&buf);
 
						group->g.real.loaded[i] = CreateGroupFromGroupID(feature, setid, type, spriteid, sprites);
 
						grfmsg(8, "NewSpriteGroup: + rg->loaded[%i]  = subset %u", i, spriteid);
 
					}
 

	
 
					for (i = 0; i < num_loading; i++) {
 
						uint16 spriteid = grf_load_word(&buf);
 
						group->g.real.loading[i] = CreateGroupFromGroupID(feature, setid, type, spriteid, sprites);
 
						grfmsg(8, "NewSpriteGroup: + rg->loading[%i] = subset %u", i, spriteid);
 
					}
 

	
 
					break;
 
				}
 

	
 
				/* Loading of Tile Layout and Production Callback groups would happen here */
 
				default: grfmsg(1, "NewSpriteGroup: Unsupported feature %d, skipping", feature);
 
			}
 
		}
 
	}
 

	
 
	_cur_grffile->spritegroups[setid] = group;
 
}
 

	
 
/* Action 0x03 */
 
static void FeatureMapSpriteGroup(byte *buf, int len)
 
{
 
	/* <03> <feature> <n-id> <ids>... <num-cid> [<cargo-type> <cid>]... <def-cid>
 
	 * id-list    := [<id>] [id-list]
 
	 * cargo-list := <cargo-type> <cid> [cargo-list]
 
	 *
 
	 * B feature       see action 0
 
	 * B n-id          bits 0-6: how many IDs this definition applies to
 
	 *                 bit 7: if set, this is a wagon override definition (see below)
 
	 * B ids           the IDs for which this definition applies
 
	 * B num-cid       number of cargo IDs (sprite group IDs) in this definition
 
	 *                 can be zero, in that case the def-cid is used always
 
	 * B cargo-type    type of this cargo type (e.g. mail=2, wood=7, see below)
 
	 * W cid           cargo ID (sprite group ID) for this type of cargo
 
	 * W def-cid       default cargo ID (sprite group ID) */
 
	/* TODO: Bridges, town houses. */
 
	/* TODO: Multiple cargo support could be useful even for trains/cars -
 
	 * cargo id 0xff is used for showing images in the build train list. */
 

	
 
	static byte *last_engines;
 
	static int last_engines_count;
 
	uint8 feature;
 
	uint8 idcount;
 
	bool wagover;
 
	uint8 cidcount;
 
	int c, i;
 

	
 
	check_length(len, 6, "FeatureMapSpriteGroup");
 
	feature = buf[1];
 
	idcount = buf[2] & 0x7F;
 
	wagover = (buf[2] & 0x80) == 0x80;
 
	check_length(len, 3 + idcount, "FeatureMapSpriteGroup");
 

	
 
	/* If ``n-id'' (or ``idcount'') is zero, this is a ``feature
 
	 * callback''. */
 
	if (idcount == 0) {
 
		grfmsg(2, "FeatureMapSpriteGroup: Feature callbacks not implemented yet");
 
		return;
 
	}
 

	
 
	cidcount = buf[3 + idcount];
 
	check_length(len, 4 + idcount + cidcount * 3, "FeatureMapSpriteGroup");
 

	
 
	grfmsg(6, "FeatureMapSpriteGroup: Feature %d, %d ids, %d cids, wagon override %d",
 
			feature, idcount, cidcount, wagover);
 

	
 
	if (feature > GSF_STATION) {
 
		grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature %d, skipping", feature);
 
		return;
 
	}
 

	
 

	
 
	if (feature == GSF_STATION) {
 
		// We do things differently for stations.
 

	
 
		for (i = 0; i < idcount; i++) {
 
			uint8 stid = buf[3 + i];
 
			StationSpec *statspec = _cur_grffile->stations[stid];
 
			byte *bp = &buf[4 + idcount];
 

	
 
			for (c = 0; c < cidcount; c++) {
 
				uint8 ctype = grf_load_byte(&bp);
 
				uint16 groupid = grf_load_word(&bp);
 

	
 
				if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) {
 
					grfmsg(1, "FeatureMapSpriteGroup: Spriteset 0x%04X out of range 0x%X or empty, skipping",
 
					       groupid, _cur_grffile->spritegroups_count);
 
					return;
 
				}
 

	
 
				if (ctype == 0xFE) ctype = GC_DEFAULT_NA;
 
				if (ctype == 0xFF) ctype = GC_PURCHASE;
 

	
 
				if (ctype >= NUM_GLOBAL_CID) {
 
					grfmsg(1, "FeatureMapSpriteGroup: Cargo type %d out of range, skipping.", ctype);
 
					continue;
 
				}
 

	
 
				statspec->spritegroup[ctype] = _cur_grffile->spritegroups[groupid];
 
			}
 
		}
 

	
 
		{
 
			byte *bp = buf + 4 + idcount + cidcount * 3;
 
			uint16 groupid = grf_load_word(&bp);
 

	
 
			if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) {
 
				grfmsg(1, "FeatureMapSpriteGroup: Spriteset 0x%04X out of range 0x%X or empty, skipping",
 
				       groupid, _cur_grffile->spritegroups_count);
 
				return;
 
			}
 

	
 
			for (i = 0; i < idcount; i++) {
 
				uint8 stid = buf[3 + i];
 
				StationSpec *statspec = _cur_grffile->stations[stid];
 

	
 
				statspec->spritegroup[GC_DEFAULT] = _cur_grffile->spritegroups[groupid];
 
				statspec->grfid = _cur_grffile->grfid;
 
				statspec->localidx = stid;
 
				SetCustomStationSpec(statspec);
 
			}
 
		}
 
		return;
 
	}
 

	
 
	// FIXME: Tropicset contains things like:
 
	// 03 00 01 19 01 00 00 00 00 - this is missing one 00 at the end,
 
	// what should we exactly do with that? --pasky
 

	
 
	if (_cur_grffile->spriteset_start == 0 || _cur_grffile->spritegroups == 0) {
 
		grfmsg(1, "FeatureMapSpriteGroup: No sprite set to work on! Skipping");
 
		return;
 
	}
 

	
 
	if (!wagover && last_engines_count != idcount) {
 
		last_engines = realloc(last_engines, idcount);
 
		last_engines_count = idcount;
 
	}
 

	
 
	if (wagover) {
 
		if (last_engines_count == 0) {
 
			grfmsg(0, "FeatureMapSpriteGroup: WagonOverride: No engine to do override with");
 
			return;
 
		}
 
		grfmsg(6, "FeatureMapSpriteGroup: WagonOverride: %u engines, %u wagons",
 
				last_engines_count, idcount);
 
	}
 

	
 

	
 
	for (i = 0; i < idcount; i++) {
 
		uint8 engine_id = buf[3 + i];
 
		uint8 engine = engine_id + _vehshifts[feature];
 
		byte *bp = &buf[4 + idcount];
 

	
 
		if (engine_id > _vehcounts[feature]) {
 
			grfmsg(0, "Id %u for feature 0x%02X is out of bounds", engine_id, feature);
 
			return;
 
		}
 

	
 
		grfmsg(7, "FeatureMapSpriteGroup: [%d] Engine %d...", i, engine);
 

	
 
		for (c = 0; c < cidcount; c++) {
 
			uint8 ctype = grf_load_byte(&bp);
 
			uint16 groupid = grf_load_word(&bp);
 

	
 
			grfmsg(8, "FeatureMapSpriteGroup: * [%d] Cargo type 0x%X, group id 0x%02X", c, ctype, groupid);
 

	
 
			if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) {
 
				grfmsg(1, "FeatureMapSpriteGroup: Spriteset 0x%04X out of range 0x%X or empty, skipping", groupid, _cur_grffile->spritegroups_count);
 
				return;
 
			}
 

	
 
			if (ctype == GC_INVALID) ctype = GC_PURCHASE;
 

	
 
			if (ctype >= NUM_GLOBAL_CID) {
 
				grfmsg(1, "FeatureMapSpriteGroup: Cargo type %d out of range, skipping.", ctype);
 
				continue;
 
			}
 

	
 
			if (wagover) {
 
				SetWagonOverrideSprites(engine, ctype, _cur_grffile->spritegroups[groupid], last_engines, last_engines_count);
 
			} else {
 
				SetCustomEngineSprites(engine, ctype, _cur_grffile->spritegroups[groupid]);
 
				last_engines[i] = engine;
 
			}
 
		}
 
	}
 

	
 
	{
 
		byte *bp = buf + 4 + idcount + cidcount * 3;
 
		uint16 groupid = grf_load_word(&bp);
 

	
 
		grfmsg(8, "-- Default group id 0x%04X", groupid);
 

	
 
		for (i = 0; i < idcount; i++) {
 
			uint8 engine = buf[3 + i] + _vehshifts[feature];
 

	
 
			// Don't tell me you don't love duplicated code!
 
			if (groupid >= _cur_grffile->spritegroups_count || _cur_grffile->spritegroups[groupid] == NULL) {
 
				grfmsg(1, "FeatureMapSpriteGroup: Spriteset 0x%04X out of range 0x%X or empty, skipping", groupid, _cur_grffile->spritegroups_count);
 
				return;
 
			}
 

	
 
			if (wagover) {
 
				/* If the ID for this action 3 is the same as the vehicle ID,
 
				 * this indicates we have a helicopter rotor override. */
 
				if (feature == GSF_AIRCRAFT && engine == last_engines[i]) {
 
					SetRotorOverrideSprites(engine, _cur_grffile->spritegroups[groupid]);
 
				} else {
 
					// TODO: No multiple cargo types per vehicle yet. --pasky
 
					SetWagonOverrideSprites(engine, GC_DEFAULT, _cur_grffile->spritegroups[groupid], last_engines, last_engines_count);
 
				}
 
			} else {
 
				SetCustomEngineSprites(engine, GC_DEFAULT, _cur_grffile->spritegroups[groupid]);
 
				SetEngineGRF(engine, _cur_grffile);
 
				last_engines[i] = engine;
 
			}
 
		}
 
	}
 
}
 

	
 
/* Action 0x04 */
 
static void FeatureNewName(byte *buf, int len)
 
{
 
	/* <04> <veh-type> <language-id> <num-veh> <offset> <data...>
 
	 *
 
	 * B veh-type      see action 0 (as 00..07, + 0A
 
	 *                 But IF veh-type = 48, then generic text
 
	 * B language-id   If bit 6 is set, This is the extended language scheme,
 
	                   with up to 64 language.
 
	                   Otherwise, it is a mapping where set bits have meaning
 
	                   0 = american, 1 = english, 2 = german, 3 = french, 4 = spanish
 
	                   Bit 7 set means this is a generic text, not a vehicle one (or else)
 
	 * B num-veh       number of vehicles which are getting a new name
 
	 * B/W offset      number of the first vehicle that gets a new name
 
	 *                 Byte : ID of vehicle to change
 
	 *                 Word : ID of string to change/add
 
	 * S data          new texts, each of them zero-terminated, after
 
	 *                 which the next name begins. */
 
	/* TODO: No support for changing non-vehicle text. Perhaps we shouldn't
 
	 * implement it at all, but it could be useful for some "modpacks"
 
	 * (completely new scenarios changing all graphics and logically also
 
	 * factory names etc). We should then also support all languages (by
 
	 * name), not only the original four ones. --pasky
 
	 * All of the above are coming.  In Time.  Some sooner than others :)*/
 

	
 
	uint8 feature;
 
	uint8 lang;
 
	uint8 num;
 
	uint16 id;
 
	uint16 endid;
 
	const char* name;
 
	bool new_scheme = _cur_grffile->grf_version >= 7;
 
	bool generic;
 

	
 
	check_length(len, 6, "FeatureNewName");
 
	buf++;
 
	feature  = grf_load_byte(&buf);
 
	lang     = grf_load_byte(&buf);
 
	num      = grf_load_byte(&buf);
 
	generic  = HASBIT(lang, 7);
 
	id       = generic ? grf_load_word(&buf) : grf_load_byte(&buf);
 

	
 
	CLRBIT(lang, 7);
 

	
 
	if (feature <= GSF_AIRCRAFT && id < _vehcounts[feature]) {
 
		id += _vehshifts[feature];
 
	}
 
	endid    = id + num;
 

	
 
	grfmsg(6, "FeatureNewName: About to rename engines %d..%d (feature %d) in language 0x%02X",
 
	               id, endid, feature, lang);
 

	
 
	name = (const char*)buf; /*transfer read value*/
 
	len -= generic ? 6 : 5;
 

	
 
	for (; id < endid && len > 0; id++) {
 
		size_t ofs = strlen(name) + 1;
 

	
 
		if (ofs < 128) {
 
			grfmsg(8, "FeatureNewName: %d <- %s", id, name);
 

	
 
			switch (feature) {
 
				case GSF_TRAIN:
 
				case GSF_ROAD:
 
				case GSF_SHIP:
 
				case GSF_AIRCRAFT: {
 
					if (id < TOTAL_NUM_ENGINES) {
 
						StringID string = AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_8000_KIRBY_PAUL_TANK_STEAM + id);
 
						SetCustomEngineName(id, string);
 
					} else {
 
						AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, id);
 
					}
 
					break;
 
				}
 

	
 
				default:
 
					switch (GB(id, 8, 8)) {
 
						case 0xC4: /* Station class name */
 
							if (_cur_grffile->stations == NULL || _cur_grffile->stations[GB(id, 0, 8)] == NULL) {
 
								grfmsg(1, "FeatureNewName: Attempt to name undefined station 0x%X, ignoring", GB(id, 0, 8));
 
							} else {
 
								StationClassID sclass = _cur_grffile->stations[GB(id, 0, 8)]->sclass;
 
								SetStationClassName(sclass, AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED));
 
							}
 
							break;
 

	
 
						case 0xC5: /* Station name */
 
							if (_cur_grffile->stations == NULL || _cur_grffile->stations[GB(id, 0, 8)] == NULL) {
 
								grfmsg(1, "FeatureNewName: Attempt to name undefined station 0x%X, ignoring", GB(id, 0, 8));
 
							} else {
 
								_cur_grffile->stations[GB(id, 0, 8)]->name = AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED);
 
							}
 
							break;
 

	
 
						case 0xC9:
 
						case 0xD0:
 
						case 0xDC:
 
							AddGRFString(_cur_grffile->grfid, id, lang, new_scheme, name, STR_UNDEFINED);
 
							break;
 

	
 
						default:
 
							grfmsg(7, "FeatureNewName: Unsupported ID (0x%04X)", id);
 
							break;
 
					}
 
					break;
 

	
 
#if 0
 
				case GSF_CANAL :
 
				case GSF_BRIDGE :
 
				case GSF_TOWNHOUSE :
 
					AddGRFString(_cur_spriteid, id, lang, name);
 
					switch (GB(id, 8,8)) {
 
						case 0xC9: /* House name */
 
						default:
 
							grfmsg(7, "FeatureNewName: Unsupported ID (0x%04X)", id);
 
					}
 
					break;
 

	
 
				case GSF_INDUSTRIES :
 
				case 0x48 :   /* for generic strings */
 
					AddGRFString(_cur_spriteid, id, lang, name);
 
					break;
 
				default :
 
					grfmsg(7, "FeatureNewName: Unsupported feature (0x%02X)", feature);
 
					break;
 
#endif
 
			}
 
		} else {
 
			/* ofs is the string length + 1, so if the string is empty, ofs
 
			 * is 1 */
 
			if (ofs == 1) {
 
				grfmsg(7, "FeatureNewName: Can't add empty name");
 
			} else {
 
				grfmsg(7, "FeatureNewName: Too long a name (%d)", ofs);
 
			}
 
		}
 
		name += ofs;
 
		len -= (int)ofs;
 
	}
 
}
 

	
 
/* Action 0x05 */
 
static void GraphicsNew(byte *buf, int len)
 
{
 
	/* <05> <graphics-type> <num-sprites> <other data...>
 
	 *
 
	 * B graphics-type What set of graphics the sprites define.
 
	 * E num-sprites   How many sprites are in this set?
 
	 * V other data    Graphics type specific data.  Currently unused. */
 
	/* TODO */
 

	
 
	uint8 type;
 
	uint16 num;
 
	SpriteID replace = 0;
 

	
 
	check_length(len, 2, "GraphicsNew");
 
	buf++;
 
	type = grf_load_byte(&buf);
 
	num  = grf_load_extended(&buf);
 

	
 
	switch (type) {
 
		case 0x04: /* Signal graphics */
 
			if (num != 112 && num != 240) {
 
				grfmsg(1, "GraphicsNews: Signal graphics sprite count must be 112 or 240, skipping");
 
				return;
 
			}
 
			_signal_base = _cur_spriteid;
 
			break;
 

	
 
		case 0x05: /* Catenary graphics */
 
			if (num != 48) {
 
				grfmsg(1, "GraphicsNews: Catenary graphics sprite count must be 48, skipping");
 
				return;
 
			}
 
			replace = SPR_ELRAIL_BASE + 3;
 
			break;
 

	
 
		case 0x06: /* Foundations */
 
			if (num != 74) {
 
				grfmsg(1, "GraphicsNews: Foundation graphics sprite count must be 74, skipping");
 
				return;
 
			}
 
			replace = SPR_SLOPES_BASE;
 
			break;
 

	
 
		case 0x08: /* Canal graphics */
 
			if (num != 65) {
 
				grfmsg(1, "GraphicsNews: Canal graphics sprite count must be 65, skipping");
 
				return;
 
			}
 
			replace = SPR_CANALS_BASE + 5;
 
			break;
 

	
 
		case 0x0D: /* Coast graphics */
 
			if (num != 16) {
 
				grfmsg(1, "GraphicsNews: Coast graphics sprite count must be 16, skipping");
 
				return;
 
			}
 
			_coast_base = _cur_spriteid;
 
			break;
 

	
 
		default:
 
			grfmsg(2, "GraphicsNew: Custom graphics (type 0x%02X) sprite block of length %u (unimplemented, ignoring)",
 
					type, num);
 
			return;
 
	}
 

	
 
	if (replace == 0) {
 
		grfmsg(2, "GraphicsNew: Loading %u sprites of type 0x%02X at SpriteID 0x%04X", num, type, _cur_spriteid);
 
	} else {
 
		grfmsg(2, "GraphicsNew: Replacing %u sprites of type 0x%02X at SpriteID 0x%04X", num, type, replace);
 
	}
 

	
 
	for (; num > 0; num--) {
 
		LoadNextSprite(replace == 0 ? _cur_spriteid++ : replace++, _file_index);
 
		_nfo_line++;
 
	}
 
}
 

	
 
static uint32 GetParamVal(byte param, uint32 *cond_val)
 
{
 
	switch (param) {
 
		case 0x81: /* current year */
 
			return clamp(_cur_year, ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR) - ORIGINAL_BASE_YEAR;
 

	
 
		case 0x83: /* current climate, 0=temp, 1=arctic, 2=trop, 3=toyland */
 
			return _opt.landscape;
 

	
 
		case 0x84: /* GRF loading stage */
 
			return (_cur_stage > GLS_INIT) | ((_cur_stage == GLS_ACTIVATION) << 9);
 

	
 
		case 0x85: /* TTDPatch flags, only for bit tests */
 
			if (cond_val == NULL) {
 
				/* Supported in Action 0x07 and 0x09, not 0x0D */
 
				return 0;
 
			} else {
 
				uint32 param_val = _ttdpatch_flags[*cond_val / 0x20];
 
				*cond_val %= 0x20;
 
				return param_val;
 
			}
 

	
 
		case 0x86: /* road traffic side, bit 4 clear=left, set=right */
 
			return _opt.road_side << 4;
 

	
 
		case 0x88: /* GRF ID check */
 
			return 0;
 

	
 
		case 0x8B: { /* TTDPatch version */
 
			uint major    = 2;
 
			uint minor    = 6;
 
			uint revision = 0; // special case: 2.0.1 is 2.0.10
 
			uint build    = 1168;
 
			return (major << 24) | (minor << 20) | (revision << 16) | build;
 
		}
 

	
 
		case 0x8D: /* TTD Version, 00=DOS, 01=Windows */
 
			return !_use_dos_palette;
 

	
 
		case 0x8E: /* Y-offset for train sprites */
 
			return _traininfo_vehicle_pitch;
 

	
 
		case 0x92: /* Game mode */
 
			return _game_mode;
 

	
 
		case 0x9A: /* Always -1 */
 
			return -1;
 

	
 
		case 0x9D: /* TTD Platform, 00=TTDPatch, 01=OpenTTD */
 
			return 1;
 

	
 
		case 0x9E: /* Miscellaneous GRF features */
 
			return _misc_grf_features;
 

	
 
		default:
 
			/* GRF Parameter */
 
			if (param < 0x80) return _cur_grffile->param[param];
 

	
 
			/* In-game variable. */
 
			grfmsg(1, "Unsupported in-game variable 0x%02X", param);
 
			return -1;
 
	}
 
}
 

	
 
/* Action 0x06 */
 
static void CfgApply(byte *buf, int len)
 
{
 
	/* <06> <param-num> <param-size> <offset> ... <FF>
 
	 *
 
	 * B param-num     Number of parameter to substitute (First = "zero")
 
	 *                 Ignored if that parameter was not specified in newgrf.cfg
 
	 * B param-size    How many bytes to replace.  If larger than 4, the
 
	 *                 bytes of the following parameter are used.  In that
 
	 *                 case, nothing is applied unless *all* parameters
 
	 *                 were specified.
 
	 * B offset        Offset into data from beginning of next sprite
 
	 *                 to place where parameter is to be stored. */
 

	
 
	/* Preload the next sprite */
 
	uint32 pos = FioGetPos();
 
	uint16 num = FioReadWord();
 
	uint8 type = FioReadByte();
 

	
 
	/* Check if the sprite is a pseudo sprite. We can't operate on real sprites. */
 
	if (type == 0xFF) {
 
		_preload_sprite = malloc(num);
 
		FioReadBlock(_preload_sprite, num);
 
	}
 

	
 
	/* Reset the file position to the start of the next sprite */
 
	FioSeekTo(pos, SEEK_SET);
 

	
 
	if (type != 0xFF) {
 
		grfmsg(2, "CfgApply: Ignoring (next sprite is real, unsupported)");
 
		return;
 
	}
 

	
 
	/* Now perform the Action 0x06 on our data. */
 
	buf++;
 

	
 
	for (;;) {
 
		uint i;
 
		uint param_num;
 
		uint param_size;
 
		uint offset;
 
		bool add_value;
 

	
 
		/* Read the parameter to apply. 0xFF indicates no more data to change. */
 
		param_num = grf_load_byte(&buf);
 
		if (param_num == 0xFF) break;
 

	
 
		/* Get the size of the parameter to use. If the size covers multiple
 
		 * double words, sequential parameter values are used. */
 
		param_size = grf_load_byte(&buf);
 

	
 
		/* Bit 7 of param_size indicates we should add to the original value
 
		 * instead of replacing it. */
 
		add_value  = HASBIT(param_size, 7);
 
		param_size = GB(param_size, 0, 7);
 

	
 
		/* Where to apply the data to within the pseudo sprite data. */
 
		offset     = grf_load_extended(&buf);
 

	
 
		/* If the parameter is a GRF parameter (not an internal variable) check
 
		 * if it (and all further sequential parameters) has been defined. */
 
		if (param_num < 0x80 && (param_num + (param_size - 1) / 4) >= _cur_grffile->param_end) {
 
			grfmsg(2, "CfgApply: Ignoring (param %d not set)", (param_num + (param_size - 1) / 4));
 
			break;
 
		}
 

	
 
		grfmsg(8, "CfgApply: Applying %u bytes from parameter 0x%02X at offset 0x%04X", param_size, param_num, offset);
 

	
 
		for (i = 0; i < param_size; i++) {
 
			uint32 value = GetParamVal(param_num + i / 4, NULL);
 

	
 
			if (add_value) {
 
				_preload_sprite[offset + i] += GB(value, (i % 4) * 8, 8);
 
			} else {
 
				_preload_sprite[offset + i] = GB(value, (i % 4) * 8, 8);
 
			}
 
		}
 
	}
 
}
 

	
 
/* Action 0x07 */
 
/* Action 0x09 */
 
static void SkipIf(byte *buf, int len)
 
{
 
	/* <07/09> <param-num> <param-size> <condition-type> <value> <num-sprites>
 
	 *
 
	 * B param-num
 
	 * B param-size
 
	 * B condition-type
 
	 * V value
 
	 * B num-sprites */
 
	/* TODO: More params. More condition types. */
 
	uint8 param;
 
	uint8 paramsize;
 
	uint8 condtype;
 
	uint8 numsprites;
 
	uint32 param_val = 0;
 
	uint32 cond_val = 0;
 
	uint32 mask = 0;
 
	bool result;
 
	GRFLabel *label;
 
	GRFLabel *choice = NULL;
 

	
 
	check_length(len, 6, "SkipIf");
 
	buf++;
 
	param     = grf_load_byte(&buf);
 
	paramsize = grf_load_byte(&buf);
 
	condtype  = grf_load_byte(&buf);
 

	
 
	if (condtype < 2) {
 
		/* Always 1 for bit tests, the given value should be ignored. */
 
		paramsize = 1;
 
	}
 

	
 
	switch (paramsize) {
 
		case 4: cond_val = grf_load_dword(&buf); mask = 0xFFFFFFFF; break;
 
		case 2: cond_val = grf_load_word(&buf);  mask = 0x0000FFFF; break;
 
		case 1: cond_val = grf_load_byte(&buf);  mask = 0x000000FF; break;
 
		default: break;
 
	}
 

	
 
	if (param < 0x80 && _cur_grffile->param_end <= param) {
 
		grfmsg(7, "Param %d undefined, skipping test", param);
 
		return;
 
	}
 

	
 
	param_val = GetParamVal(param, &cond_val);
 

	
 
	grfmsg(7, "Test condtype %d, param 0x%08X, condval 0x%08X", condtype, param_val, cond_val);
 

	
 
	if (param == 0x88) {
 
		/* GRF ID checks */
 

	
 
		const GRFConfig *c = GetGRFConfig(cond_val);
 

	
 
		if (condtype != 10 && c == NULL) {
 
			grfmsg(7, "GRFID 0x%08X unknown, skipping test", BSWAP32(cond_val));
 
			return;
 
		}
 

	
 
		switch (condtype) {
 
			/* Tests 6 to 10 are only for param 0x88, GRFID checks */
 
			case 6: /* Is GRFID active? */
 
				result = HASBIT(c->flags, GCF_ACTIVATED);
 
				break;
 

	
 
			case 7: /* Is GRFID non-active? */
 
				result = !HASBIT(c->flags, GCF_ACTIVATED);
 
				break;
 

	
 
			case 8: /* GRFID is not but will be active? */
 
				result = !HASBIT(c->flags, GCF_ACTIVATED) && !HASBIT(c->flags, GCF_DISABLED);
 
				break;
 

	
 
			case 9: /* GRFID is or will be active? */
 
				result = !HASBIT(c->flags, GCF_NOT_FOUND) && !HASBIT(c->flags, GCF_DISABLED);
 
				break;
 

	
 
			case 10: /* GRFID is not nor will be active */
 
				/* This is the only condtype that doesn't get ignored if the GRFID is not found */
 
				result = c == NULL || HASBIT(c->flags, GCF_DISABLED) || HASBIT(c->flags, GCF_NOT_FOUND);
 
				break;
 

	
 
			default: grfmsg(1, "Unsupported GRF test %d. Ignoring", condtype); return;
 
		}
 
	} else {
 
		/* Parameter or variable tests */
 
		switch (condtype) {
 
			case 0: result = !!(param_val & (1 << cond_val));
 
				break;
 
			case 1: result = !(param_val & (1 << cond_val));
 
				break;
 
			case 2: result = (param_val & mask) == cond_val;
 
				break;
 
			case 3: result = (param_val & mask) != cond_val;
 
				break;
 
			case 4: result = (param_val & mask) < cond_val;
 
				break;
 
			case 5: result = (param_val & mask) > cond_val;
 
				break;
 

	
 
			default: grfmsg(1, "Unsupported test %d. Ignoring", condtype); return;
 
		}
 
	}
 

	
 
	if (!result) {
 
		grfmsg(2, "Not skipping sprites, test was false");
 
		return;
 
	}
 

	
 
	numsprites = grf_load_byte(&buf);
 

	
 
	/* numsprites can be a GOTO label if it has been defined in the GRF
 
	 * file. The jump will always be the first matching label that follows
 
	 * the current nfo_line. If no matching label is found, the first matching
 
	 * label in the file is used. */
 
	for (label = _cur_grffile->label; label != NULL; label = label->next) {
 
		if (label->label != numsprites) continue;
 

	
 
		/* Remember a goto before the current line */
 
		if (choice == NULL) choice = label;
 
		/* If we find a label here, this is definitely good */
 
		if (label->nfo_line > _nfo_line) {
 
			choice = label;
 
			break;
 
		}
 
	}
 

	
 
	if (choice != NULL) {
 
		grfmsg(2, "Jumping to label 0x%0X at line %d, test was true", choice->label, choice->nfo_line);
 
		FioSeekTo(choice->pos, SEEK_SET);
 
		_nfo_line = choice->nfo_line;
 
		return;
 
	}
 

	
 
	grfmsg(2, "Skipping %d sprites, test was true", numsprites);
 
	_skip_sprites = numsprites;
 
	if (_skip_sprites == 0) {
 
		/* Zero means there are no sprites to skip, so
 
		 * we use -1 to indicate that all further
 
		 * sprites should be skipped. */
 
		_skip_sprites = -1;
 
	}
 
}
 

	
 

	
 
/* Action 0x08 (GLS_FILESCAN) */
 
static void ScanInfo(byte *buf, int len)
 
{
 
	uint8 version;
 
	uint32 grfid;
 
	const char *name;
 
	const char *info;
 
	int name_len;
 
	int info_len;
 

	
 
	check_length(len, 8, "Info"); buf++;
 
	version = grf_load_byte(&buf);
 
	grfid = grf_load_dword(&buf);
 

	
 
	_cur_grfconfig->grfid = grfid;
 

	
 
	/* GRF IDs starting with 0xFF are reserved for internal TTDPatch use */
 
	if (GB(grfid, 24, 8) == 0xFF) SETBIT(_cur_grfconfig->flags, GCF_SYSTEM);
 

	
 
	len -= 6;
 
	name = (const char*)buf;
 
	name_len = ttd_strnlen(name, len);
 

	
 
	if (name_len < len) {
 
		_cur_grfconfig->name = TranslateTTDPatchCodes(name);
 

	
 
		len -= name_len + 1;
 
		info = name + name_len + 1;
 
		info_len = ttd_strnlen(info, len);
 

	
 
		if (info_len < len) _cur_grfconfig->info  = TranslateTTDPatchCodes(info);
 
	}
 

	
 
	/* GLS_INFOSCAN only looks for the action 8, so we can skip the rest of the file */
 
	_skip_sprites = -1;
 
}
 

	
 
/* Action 0x08 */
 
static void GRFInfo(byte *buf, int len)
 
{
 
	/* <08> <version> <grf-id> <name> <info>
 
	 *
 
	 * B version       newgrf version, currently 06
 
	 * 4*B grf-id      globally unique ID of this .grf file
 
	 * S name          name of this .grf set
 
	 * S info          string describing the set, and e.g. author and copyright */
 
	/* TODO: Check version. (We should have own versioning done somehow.) */
 
	uint8 version;
 
	uint32 grfid;
 
	const char *name;
 

	
 
	check_length(len, 8, "GRFInfo"); buf++;
 
	version = grf_load_byte(&buf);
 
	grfid = grf_load_dword(&buf);
 
	name = (const char*)buf;
 

	
 
	_cur_grffile->grfid = grfid;
 
	_cur_grffile->grf_version = version;
 
	SETBIT(_cur_grfconfig->flags, GCF_ACTIVATED);
 

	
 
	/* Do swap the GRFID for displaying purposes since people expect that */
 
	DEBUG(grf, 1, "Loaded GRFv%d set %08lX - %s", version, BSWAP32(grfid), name);
 
}
 

	
 
/* Action 0x0A */
 
static void SpriteReplace(byte *buf, int len)
 
{
 
	/* <0A> <num-sets> <set1> [<set2> ...]
 
	 * <set>: <num-sprites> <first-sprite>
 
	 *
 
	 * B num-sets      How many sets of sprites to replace.
 
	 * Each set:
 
	 * B num-sprites   How many sprites are in this set
 
	 * W first-sprite  First sprite number to replace */
 
	uint8 num_sets;
 
	uint i;
 

	
 
	buf++; /* skip action byte */
 
	num_sets = grf_load_byte(&buf);
 

	
 
	for (i = 0; i < num_sets; i++) {
 
		uint8 num_sprites = grf_load_byte(&buf);
 
		uint16 first_sprite = grf_load_word(&buf);
 
		uint j;
 

	
 
		grfmsg(2, "SpriteReplace: [Set %d] Changing %d sprites, beginning with %d",
 
			i, num_sprites, first_sprite
 
		);
 

	
 
		for (j = 0; j < num_sprites; j++) {
 
			LoadNextSprite(first_sprite + j, _file_index); // XXX
 
			_nfo_line++;
 
		}
 
	}
 
}
 

	
 
/* Action 0x0B */
 
static void GRFError(byte *buf, int len)
 
{
 
	/* <0B> <severity> <language-id> <message-id> [<message...> 00] [<data...>] 00 [<parnum>]
 
	 *
 
	 * B severity      00: notice, contine loading grf file
 
	 *                 01: warning, continue loading grf file
 
	 *                 02: error, but continue loading grf file, and attempt
 
	 *                     loading grf again when loading or starting next game
 
	 *                 03: error, abort loading and prevent loading again in
 
	 *                     the future (only when restarting the patch)
 
	 * B language-id   see action 4, use 1F for built-in error messages
 
	 * B message-id    message to show, see below
 
	 * S message       for custom messages (message-id FF), text of the message
 
	 *                 not present for built-in messages.
 
	 * V data          additional data for built-in (or custom) messages
 
	 * B parnum        see action 6, only used with built-in message 03 */
 
	/* TODO: For now we just show the message, sometimes incomplete and never translated. */
 

	
 
	static const char *const msgstr[] = {
 
		"%sRequires at least pseudo-TTDPatch version %s",
 
		"%sThis file is for %s version of TTD",
 
		"%sDesigned to be used with %s",
 
		"%sInvalid parameter %s",
 
		"%sMust be loaded before %s",
 
		"%sMust be loaded after %s",
 
		"%s%s"
 
	};
 

	
 
	static const char *const sevstr[] = {
 
		"",
 
		"Warning: ",
 
		"Error: ",
 
		"Fatal: ",
 
	};
 
	uint8 sevid;
 
	uint8 msgid;
 

	
 
	check_length(len, 6, "GRFError");
 
	sevid = buf[1];
 
	msgid = buf[3];
 

	
 
	// Undocumented TTDPatch feature.
 
	if (!HASBIT(sevid, 7) && _cur_stage < GLS_ACTIVATION) {
 
		grfmsg(7, "Skipping non-fatal GRFError in stage 1");
 
		return;
 
	}
 

	
 
	sevid = GB(sevid, 0, 2);
 
	grfmsg(0,  msgstr[(msgid == 0xFF) ? lengthof(msgstr) - 1 : msgid], sevstr[sevid], &buf[4]);
 
}
 

	
 
/* Action 0x0C */
 
static void GRFComment(byte *buf, int len)
 
{
 
	/* <0C> [<ignored...>]
 
	 *
 
	 * V ignored       Anything following the 0C is ignored */
 

	
 
	static char comment[256];
 
	if (len == 1) return;
 

	
 
	ttd_strlcpy(comment, (char*)(buf + 1), minu(sizeof(comment), len));
 
	grfmsg(2, "GRFComment: %s", comment);
 
}
 

	
 
/* Action 0x0D (GLS_SAFETYSCAN) */
 
static void SafeParamSet(byte *buf, int len)
 
{
 
	uint8 target;
 

	
 
	check_length(len, 5, "SafeParamSet");
 
	buf++;
 
	target = grf_load_byte(&buf);
 

	
 
	/* Only writing GRF parameters is considered safe */
 
	if (target < 0x80) return;
 

	
 
	/* GRM could be unsafe, but as here it can only happen after other GRFs
 
	 * are loaded, it should be okay. If the GRF tried to use the slots it
 
	 * reserved, it would be marked unsafe anyway. GRM for (e.g. bridge)
 
	 * sprites  is considered safe. */
 

	
 
	SETBIT(_cur_grfconfig->flags, GCF_UNSAFE);
 

	
 
	/* Skip remainder of GRF */
 
	_skip_sprites = -1;
 
}
 

	
 
/* Action 0x0D */
 
static void ParamSet(byte *buf, int len)
 
{
 
	/* <0D> <target> <operation> <source1> <source2> [<data>]
 
	 *
 
	 * B target        parameter number where result is stored
 
	 * B operation     operation to perform, see below
 
	 * B source1       first source operand
 
	 * B source2       second source operand
 
	 * D data          data to use in the calculation, not necessary
 
	 *                 if both source1 and source2 refer to actual parameters
 
	 *
 
	 * Operations
 
	 * 00      Set parameter equal to source1
 
	 * 01      Addition, source1 + source2
 
	 * 02      Subtraction, source1 - source2
 
	 * 03      Unsigned multiplication, source1 * source2 (both unsigned)
 
	 * 04      Signed multiplication, source1 * source2 (both signed)
 
	 * 05      Unsigned bit shift, source1 by source2 (source2 taken to be a
 
	 *         signed quantity; left shift if positive and right shift if
 
	 *         negative, source1 is unsigned)
 
	 * 06      Signed bit shift, source1 by source2
 
	 *         (source2 like in 05, and source1 as well)
 
	 */
 

	
 
	byte target;
 
	byte oper;
 
	uint32 src1;
 
	uint32 src2;
 
	uint32 data = 0;
 
	uint32 res;
 

	
 
	check_length(len, 5, "ParamSet");
 
	buf++;
 
	target = grf_load_byte(&buf);
 
	oper = grf_load_byte(&buf);
 
	src1 = grf_load_byte(&buf);
 
	src2 = grf_load_byte(&buf);
 

	
 
	if (len >= 8) data = grf_load_dword(&buf);
 

	
 
	/* You can add 80 to the operation to make it apply only if the target
 
	 * is not defined yet.  In this respect, a parameter is taken to be
 
	 * defined if any of the following applies:
 
	 * - it has been set to any value in the newgrf(w).cfg parameter list
 
	 * - it OR A PARAMETER WITH HIGHER NUMBER has been set to any value by
 
	 *   an earlier action D */
 
	if (oper & 0x80) {
 
		if (target < 0x80 && target < _cur_grffile->param_end) {
 
			grfmsg(7, "Param %u already defined, skipping", target);
 
			return;
 
		}
 

	
 
		oper &= 0x7F;
 
	}
 

	
 
	if (src2 == 0xFE) {
 
		if (GB(data, 0, 8) == 0xFF) {
 
			if (data == 0x0000FFFF) {
 
				/* Patch variables */
 
				grfmsg(2, "ParamSet: Reading Patch variables unsupported");
 
				return;
 
			} else {
 
				/* GRF Resource Management */
 
				if (_cur_stage != GLS_ACTIVATION) {
 
					/* Ignore GRM during initialization */
 
					src1 = 0;
 
				} else {
 
					uint8  op      = src1;
 
					uint8  feature = GB(data, 8, 8);
 
					uint16 count   = GB(data, 16, 16);
 

	
 
					switch (feature) {
 
						case 0x00: /* Trains */
 
						case 0x01: /* Road Vehicles */
 
						case 0x02: /* Ships */
 
						case 0x03: /* Aircraft */
 
						{
 
							uint start = 0;
 
							uint size  = 0;
 
							uint shift = _vehshifts[feature];
 
							int i;
 

	
 
							if (op == 6) {
 
								/* Return GRFID of set that reserved ID */
 
								src1 = _grm_engines[shift + _cur_grffile->param[target]];
 
								break;
 
							}
 

	
 
							/* With an operation of 2 or 3, we want to reserve a specific block of IDs */
 
							if (op == 2 || op == 3) start = _cur_grffile->param[target];
 

	
 
							for (i = start; i < _vehcounts[feature]; i++) {
 
								if (_grm_engines[shift + i] == 0) {
 
									size++;
 
								} else {
 
									if (op == 2 || op == 3) break;
 
									start = i + 1;
 
									size = 0;
 
								}
 

	
 
								if (size == count) break;
 
							}
 

	
 
							if (size == count) {
 
								/* Got the slot... */
 
								if (op == 0 || op == 3) {
 
									grfmsg(2, "GRM: Reserving %d vehicles at %d", count, start);
 
									for (i = 0; i < count; i++) _grm_engines[shift + start + i] = _cur_grffile->grfid;
 
								}
 
								src1 = start;
 
							} else {
 
								/* Unable to allocate */
 
								if (op != 4 && op != 5) {
 
									/* Deactivate GRF */
 
									grfmsg(0, "GRM: Unable to allocate %d vehicles, deactivating", count);
 
									SETBIT(_cur_grfconfig->flags, GCF_DISABLED);
 
									CLRBIT(_cur_grfconfig->flags, GCF_ACTIVATED);
 

	
 
									_skip_sprites = -1;
 
									return;
 
								}
 

	
 
								grfmsg(1, "GRM: Unable to allocate %d vehicles", count);
 
								src1 = -1;
 
							}
 
							break;
 
						}
 

	
 
						case 0x08: /* General sprites */
 
							switch (op) {
 
								case 0:
 
									/* 'Reserve' space at the current sprite ID */
 
									src1 = _cur_spriteid;
 
									_cur_spriteid += count;
 
									break;
 

	
 
								case 1:
 
									src1 = _cur_spriteid;
 
									break;
 

	
 
								default:
 
									grfmsg(1, "GRM: Unsupported operation %d for general sprites", op);
 
									return;
 
							}
 
							break;
 

	
 
						default: grfmsg(1, "GRM: Unsupported feature 0x%X", feature); return;
 
					}
 
				}
 
			}
 
		} else {
 
			/* Read another GRF File's parameter */
 
			const GRFFile *file = GetFileByGRFID(data);
 
			if (file == NULL || src1 >= file->param_end) {
 
				src1 = 0;
 
			} else {
 
				src1 = file->param[src1];
 
			}
 
		}
 
	} else {
 
		/* The source1 and source2 operands refer to the grf parameter number
 
		 * like in action 6 and 7.  In addition, they can refer to the special
 
		 * variables available in action 7, or they can be FF to use the value
 
		 * of <data>.  If referring to parameters that are undefined, a value
 
		 * of 0 is used instead.  */
 
		src1 = (src1 == 0xFF) ? data : GetParamVal(src1, NULL);
 
		src2 = (src2 == 0xFF) ? data : GetParamVal(src2, NULL);
 
	}
 

	
 
	/* TODO: You can access the parameters of another GRF file by using
 
	 * source2=FE, source1=the other GRF's parameter number and data=GRF
 
	 * ID.  This is only valid with operation 00 (set).  If the GRF ID
 
	 * cannot be found, a value of 0 is used for the parameter value
 
	 * instead. */
 

	
 
	switch (oper) {
 
		case 0x00:
 
			res = src1;
 
			break;
 

	
 
		case 0x01:
 
			res = src1 + src2;
 
			break;
 

	
 
		case 0x02:
 
			res = src1 - src2;
 
			break;
 

	
 
		case 0x03:
 
			res = src1 * src2;
 
			break;
 

	
 
		case 0x04:
 
			res = (int32)src1 * (int32)src2;
 
			break;
 

	
 
		case 0x05:
 
			if ((int32)src2 < 0) {
 
				res = src1 >> -(int32)src2;
 
			} else {
 
				res = src1 << src2;
 
			}
 
			break;
 

	
 
		case 0x06:
 
			if ((int32)src2 < 0) {
 
				res = (int32)src1 >> -(int32)src2;
 
			} else {
 
				res = (int32)src1 << src2;
 
			}
 
			break;
 

	
 
		case 0x07: /* Bitwise AND */
 
			res = src1 & src2;
 
			break;
 

	
 
		case 0x08: /* Bitwise OR */
 
			res = src1 | src2;
 
			break;
 

	
 
		case 0x09: /* Unsigned division */
 
			if (src2 == 0) {
 
				res = src1;
 
			} else {
 
				res = src1 / src2;
 
			}
 
			break;
 

	
 
		case 0x0A: /* Signed divison */
 
			if (src2 == 0) {
 
				res = src1;
 
			} else {
 
				res = (int32)src1 / (int32)src2;
 
			}
 
			break;
 

	
 
		case 0x0B: /* Unsigned modulo */
 
			if (src2 == 0) {
 
				res = src1;
 
			} else {
 
				res = src1 % src2;
 
			}
 
			break;
 

	
 
		case 0x0C: /* Signed modulo */
 
			if (src2 == 0) {
 
				res = src1;
 
			} else {
 
				res = (int32)src1 % (int32)src2;
 
			}
 
			break;
 

	
 
		default: grfmsg(0, "ParamSet: Unknown operation %d, skipping", oper); return;
 
	}
 

	
 
	switch (target) {
 
		case 0x8E: // Y-Offset for train sprites
 
			_traininfo_vehicle_pitch = res;
 
			break;
 

	
 
		// TODO implement
 
		case 0x8F: // Rail track type cost factors
 
		case 0x93: // Tile refresh offset to left
 
		case 0x94: // Tile refresh offset to right
 
		case 0x95: // Tile refresh offset upwards
 
		case 0x96: // Tile refresh offset downwards
 
		case 0x97: // Snow line height
 
		case 0x99: // Global ID offset
 
			grfmsg(7, "ParamSet: Skipping unimplemented target 0x%02X", target);
 
			break;
 

	
 
		case 0x9E: /* Miscellaneous GRF features */
 
			_misc_grf_features = res;
 
			/* Set train list engine width */
 
			_traininfo_vehicle_width = HASBIT(res, 3) ? 32 : 29;
 
			break;
 

	
 
		default:
 
			if (target < 0x80) {
 
				_cur_grffile->param[target] = res;
 
				if (target + 1U > _cur_grffile->param_end) _cur_grffile->param_end = target + 1;
 
			} else {
 
				grfmsg(7, "ParamSet: Skipping unknown target 0x%02X", target);
 
			}
 
			break;
 
	}
 
}
 

	
 
/* Action 0x0E (GLS_SAFETYSCAN) */
 
static void SafeGRFInhibit(byte *buf, int len)
 
{
 
	/* <0E> <num> <grfids...>
 
	 *
 
	 * B num           Number of GRFIDs that follow
 
	 * D grfids        GRFIDs of the files to deactivate */
 

	
 
	byte num;
 
	int i;
 

	
 
	check_length(len, 1, "GRFInhibit");
 
	buf++, len--;
 
	num = grf_load_byte(&buf); len--;
 
	check_length(len, 4 * num, "GRFInhibit");
 

	
 
	for (i = 0; i < num; i++) {
 
		uint32 grfid = grf_load_dword(&buf);
 

	
 
		/* GRF is unsafe it if tries to deactivate other GRFs */
 
		if (grfid != _cur_grfconfig->grfid) {
 
			SETBIT(_cur_grfconfig->flags, GCF_UNSAFE);
 

	
 
			/* Skip remainder of GRF */
 
			_skip_sprites = -1;
 

	
 
			return;
 
		}
 
	}
 
}
 

	
 
/* Action 0x0E */
 
static void GRFInhibit(byte *buf, int len)
 
{
 
	/* <0E> <num> <grfids...>
 
	 *
 
	 * B num           Number of GRFIDs that follow
 
	 * D grfids        GRFIDs of the files to deactivate */
 

	
 
	byte num;
 
	int i;
 

	
 
	check_length(len, 1, "GRFInhibit");
 
	buf++, len--;
 
	num = grf_load_byte(&buf); len--;
 
	check_length(len, 4 * num, "GRFInhibit");
 

	
 
	for (i = 0; i < num; i++) {
 
		uint32 grfid = grf_load_dword(&buf);
 
		GRFConfig *file = GetGRFConfig(grfid);
 

	
 
		/* Unset activation flag */
 
		if (file != NULL && file != _cur_grfconfig) {
 
			grfmsg(2, "GRFInhibit: Deactivating file '%s'", file->filename);
 
			SETBIT(file->flags, GCF_DISABLED);
 
			CLRBIT(file->flags, GCF_ACTIVATED);
 
		}
 
	}
 
}
 

	
 
/* Action 0x10 */
 
static void DefineGotoLabel(byte *buf, int len)
 
{
 
	/* <10> <label> [<comment>]
 
	 *
 
	 * B label      The label to define
 
	 * V comment    Optional comment - ignored */
 

	
 
	GRFLabel *label;
 

	
 
	check_length(len, 1, "DefineGotoLabel");
 
	buf++; len--;
 

	
 
	label = malloc(sizeof(*label));
 
	label->label    = grf_load_byte(&buf);
 
	label->nfo_line = _nfo_line;
 
	label->pos      = FioGetPos();
 
	label->next     = NULL;
 

	
 
	/* Set up a linked list of goto targets which we will search in an Action 0x7/0x9 */
 
	if (_cur_grffile->label == NULL) {
 
		_cur_grffile->label = label;
 
	} else {
 
		/* Attach the label to the end of the list */
 
		GRFLabel *l;
 
		for (l = _cur_grffile->label; l->next != NULL; l = l->next);
 
		l->next = label;
 
	}
 

	
 
	grfmsg(2, "DefineGotoLabel: GOTO target with label 0x%02X", label->label);
 
}
 

	
 
/* Action 0x11 */
 
static void GRFSound(byte *buf, int len)
 
{
 
	/* <11> <num>
 
	 *
 
	 * W num      Number of sound files that follow */
 

	
 
	uint16 num;
 

	
 
	check_length(len, 1, "GRFSound");
 
	buf++;
 
	num = grf_load_word(&buf);
 

	
 
	_grf_data_blocks = num;
 
	_grf_data_type   = GDT_SOUND;
 

	
 
	if (_cur_grffile->sound_offset == 0) _cur_grffile->sound_offset = GetNumSounds();
 
}
 

	
 
static void ImportGRFSound(byte *buf, int len)
 
{
 
	const GRFFile *file;
 
	FileEntry *se = AllocateFileEntry();
 
	uint32 grfid = grf_load_dword(&buf);
 
	uint16 sound = grf_load_word(&buf);
 

	
 
	file = GetFileByGRFID(grfid);
 
	if (file == NULL || file->sound_offset == 0) {
 
		grfmsg(1, "ImportGRFSound: Source file not available");
 
		return;
 
	}
 

	
 
	if (file->sound_offset + sound >= GetNumSounds()) {
 
		grfmsg(1, "ImportGRFSound: Sound effect %d is invalid", sound);
 
		return;
 
	}
 

	
 
	grfmsg(2, "ImportGRFSound: Copying sound %d (%d) from file %X", sound, file->sound_offset + sound, grfid);
 

	
 
	*se = *GetSound(file->sound_offset + sound);
 

	
 
	/* Reset volume and priority, which TTDPatch doesn't copy */
 
	se->volume   = 128;
 
	se->priority = 0;
 
}
 

	
 
/* 'Action 0xFE' */
 
static void GRFImportBlock(byte *buf, int len)
 
{
 
	if (_grf_data_blocks == 0) {
 
		grfmsg(2, "GRFImportBlock: Unexpected import block, skipping");
 
		return;
 
	}
 

	
 
	buf++;
 

	
 
	_grf_data_blocks--;
 

	
 
	/* XXX 'Action 0xFE' isn't really specified. It is only mentioned for
 
	 * importing sounds, so this is probably all wrong... */
 
	if (grf_load_byte(&buf) != _grf_data_type) {
 
		grfmsg(1, "GRFImportBlock: Import type mismatch");
 
	}
 

	
 
	switch (_grf_data_type) {
 
		case GDT_SOUND: ImportGRFSound(buf, len - 1); break;
 
		default: NOT_REACHED(); break;
 
	}
 
}
 

	
 
static void LoadGRFSound(byte *buf, int len)
 
{
 
	byte *buf_start = buf;
 
	FileEntry *se;
 

	
 
	/* Allocate a sound entry. This is done even if the data is not loaded
 
	 * so that the indices used elsewhere are still correct. */
 
	se = AllocateFileEntry();
 

	
 
	if (grf_load_dword(&buf) != BSWAP32('RIFF')) {
 
		grfmsg(1, "LoadGRFSound: Missing RIFF header");
 
		return;
 
	}
 

	
 
	/* Size of file -- we ignore this */
 
	grf_load_dword(&buf);
 

	
 
	if (grf_load_dword(&buf) != BSWAP32('WAVE')) {
 
		grfmsg(1, "LoadGRFSound: Invalid RIFF type");
 
		return;
 
	}
 

	
 
	for (;;) {
 
		uint32 tag  = grf_load_dword(&buf);
 
		uint32 size = grf_load_dword(&buf);
 

	
 
		switch (tag) {
 
			case ' tmf': /* 'fmt ' */
 
				/* Audio format, must be 1 (PCM) */
 
				if (grf_load_word(&buf) != 1) {
 
					grfmsg(1, "LoadGRFSound: Invalid audio format");
 
					return;
 
				}
 
				se->channels = grf_load_word(&buf);
 
				se->rate = grf_load_dword(&buf);
 
				grf_load_dword(&buf);
 
				grf_load_word(&buf);
 
				se->bits_per_sample = grf_load_word(&buf);
 

	
 
				/* Consume any extra bytes */
 
				for (; size > 16; size--) grf_load_byte(&buf);
 
				break;
 

	
 
			case 'atad': /* 'data' */
 
				se->file_size    = size;
 
				se->file_offset  = FioGetPos() - (len - (buf - buf_start)) + 1;
 
				se->file_offset |= _file_index << 24;
 

	
 
				/* Set default volume and priority */
 
				se->volume = 0x80;
 
				se->priority = 0;
 

	
 
				grfmsg(2, "LoadGRFSound: channels %u, sample rate %u, bits per sample %u, length %u", se->channels, se->rate, se->bits_per_sample, size);
 
				return;
 

	
 
			default:
 
				se->file_size = 0;
 
				return;
 
		}
 
	}
 
}
 

	
 
/* Action 0x12 */
 
static void LoadFontGlyph(byte *buf, int len)
 
{
 
	/* <12> <num_def> <font_size> <num_char> <base_char>
 
	 *
 
	 * B num_def      Number of definitions
 
	 * B font_size    Size of font (0 = normal, 1 = small, 2 = large)
 
	 * B num_char     Number of consecutive glyphs
 
	 * W base_char    First character index */
 

	
 
	uint8 num_def;
 
	uint i;
 

	
 
	buf++; len--;
 
	check_length(len, 1, "LoadFontGlyph");
 

	
 
	num_def = grf_load_byte(&buf);
 

	
 
	check_length(len, 1 + num_def * 4, "LoadFontGlyph");
 

	
 
	for (i = 0; i < num_def; i++) {
 
		FontSize size    = grf_load_byte(&buf);
 
		uint8  num_char  = grf_load_byte(&buf);
 
		uint16 base_char = grf_load_word(&buf);
 
		uint c;
 

	
 
		grfmsg(7, "LoadFontGlyph: Loading %u glyph(s) at 0x%04X for size %u", num_char, base_char, size);
 

	
 
		for (c = 0; c < num_char; c++) {
 
			SetUnicodeGlyph(size, base_char + c, _cur_spriteid);
 
			LoadNextSprite(_cur_spriteid++, _file_index);
 
			_nfo_line++;
 
		}
 
	}
 
}
 

	
 
/* 'Action 0xFF' */
 
static void GRFDataBlock(byte *buf, int len)
 
{
 
	byte name_len;
 
	const char *name;
 

	
 
	if (_grf_data_blocks == 0) {
 
		grfmsg(2, "GRFDataBlock: unexpected data block, skipping");
 
		return;
 
	}
 

	
 
	buf++;
 
	name_len = grf_load_byte(&buf);
 
	name = (const char *)buf;
 
	buf += name_len + 1;
 

	
 
	grfmsg(2, "GRFDataBlock: block name '%s'...", name);
 

	
 
	_grf_data_blocks--;
 

	
 
	switch (_grf_data_type) {
 
		case GDT_SOUND: LoadGRFSound(buf, len - name_len - 2); break;
 
		default: NOT_REACHED(); break;
 
	}
 
}
 

	
 

	
 
/* Used during safety scan on unsafe actions */
 
static void GRFUnsafe(byte *buf, int len)
 
{
 
	SETBIT(_cur_grfconfig->flags, GCF_UNSAFE);
 

	
 
	/* Skip remainder of GRF */
 
	_skip_sprites = -1;
 
}
 

	
 

	
 
static void InitializeGRFSpecial(void)
 
{
 
	_ttdpatch_flags[0] =  ((_patches.always_small_airport ? 1 : 0) << 0x0C)  // keepsmallairport
 
	                   |                                        (1 << 0x0D)  // newairports
 
	                   |                                        (1 << 0x0E)  // largestations
 
	                   |           ((_patches.longbridges ? 1 : 0) << 0x0F)  // longbridges
 
	                   |                                        (0 << 0x10)  // loadtime
 
	                   |                                        (1 << 0x12)  // presignals
 
	                   |                                        (1 << 0x13)  // extpresignals
 
	                   | ((_patches.never_expire_vehicles ? 1 : 0) << 0x16)  // enginespersist
 
	                   |                                        (1 << 0x1B)  // multihead
 
	                   |                                        (1 << 0x1D)  // lowmemory
 
	                   |                                        (1 << 0x1E); // generalfixes
 

	
 
	_ttdpatch_flags[1] =                                        (0 << 0x07)  // moreairports - based on units of noise
 
	                   |        ((_patches.mammoth_trains ? 1 : 0) << 0x08)  // mammothtrains
 
	                   |                                        (1 << 0x09)  // trainrefit
 
	                   |                                        (0 << 0x0B)  // subsidiaries
 
	                   |       ((_patches.gradual_loading ? 1 : 0) << 0x0C)  // gradualloading
 
	                   |                                        (1 << 0x12)  // unifiedmaglevmode - set bit 0 mode. Not revelant to OTTD
 
	                   |                                        (1 << 0x13)  // unifiedmaglevmode - set bit 1 mode
 
	                   |                                        (1 << 0x14)  // bridgespeedlimits
 
	                   |                                        (1 << 0x16)  // eternalgame
 
	                   |                                        (1 << 0x17)  // newtrains
 
	                   |                                        (1 << 0x18)  // newrvs
 
	                   |                                        (1 << 0x19)  // newships
 
	                   |                                        (1 << 0x1A)  // newplanes
 
	                   |           ((_patches.signal_side ? 1 : 0) << 0x1B)  // signalsontrafficside
 
	                   |                                        (1 << 0x1C); // electrifiedrailway
 

	
 
	_ttdpatch_flags[2] =                                        (1 << 0x01)  // loadallgraphics - obsolote
 
	                   |                                        (1 << 0x03)  // semaphores
 
	                   |                                        (0 << 0x0B)  // enhancedgui
 
	                   |                                        (0 << 0x0C)  // newagerating
 
	                   |       ((_patches.build_on_slopes ? 1 : 0) << 0x0D)  // buildonslopes
 
	                   |                                        (0 << 0x0F)  // planespeed
 
	                   |                                        (0 << 0x10)  // moreindustriesperclimate - obsolete
 
	                   |                                        (0 << 0x11)  // moretoylandfeatures
 
	                   |                                        (1 << 0x12)  // newstations
 
	                   |                                        (0 << 0x13)  // tracktypecostdiff
 
	                   |                                        (0 << 0x14)  // manualconvert
 
	                   |       ((_patches.build_on_slopes ? 1 : 0) << 0x15)  // buildoncoasts
 
	                   |                                        (1 << 0x16)  // canals
 
	                   |                                        (1 << 0x17)  // newstartyear
 
	                   |                                        (0 << 0x18)  // freighttrains
 
	                   |                                        (0 << 0x19)  // newhouses
 
	                   |                                        (1 << 0x1A)  // newbridges
 
	                   |                                        (0 << 0x1B)  // newtownnames
 
	                   |                                        (0 << 0x1C)  // moreanimations
 
	                   |    ((_patches.wagon_speed_limits ? 1 : 0) << 0x1D)  // wagonspeedlimits
 
	                   |                                        (1 << 0x1E)  // newshistory
 
	                   |                                        (0 << 0x1F); // custombridgeheads
 

	
 
	_ttdpatch_flags[3] =                                        (0 << 0x00)  // newcargodistribution
 
	                   |                                        (1 << 0x01)  // windowsnap
 
	                   |                                        (0 << 0x02)  // townbuildnoroad
 
	                   |                                        (0 << 0x03)  // pathbasedsignalling. To enable if ever pbs is back
 
	                   |                                        (0 << 0x04)  // aichoosechance
 
	                   |                                        (1 << 0x05)  // resolutionwidth
 
	                   |                                        (1 << 0x06)  // resolutionheight
 
	                   |                                        (0 << 0x07)  // newindustries
 
	                   |                                        (0 << 0x08)  // fifoloading
 
	                   |                                        (0 << 0x09)  // townroadbranchprob
 
	                   |                                        (0 << 0x0A)  // tempsnowline
 
	                   |                                        (0 << 0x0B)  // newcargo
 
	                   |                                        (1 << 0x0C)  // enhancemultiplayer
 
	                   |                                        (1 << 0x0D)  // onewayroads
 
	                   |   ((_patches.nonuniform_stations ? 1 : 0) << 0x0E)  // irregularstations
 
	                   |                                        (1 << 0x0F)  // statistics
 
	                   |                                        (1 << 0x10)  // newsounds
 
	                   |                                        (1 << 0x11)  // autoreplace
 
	                   |                                        (1 << 0x12)  // autoslope
 
	                   |                                        (0 << 0x13)  // followvehicle
 
	                   |                                        (0 << 0x14)  // trams
 
	                   |                                        (0 << 0x15)  // enhancetunnels
 
	                   |                                        (0 << 0x16)  // shortrvs
 
	                   |                                        (0 << 0x17); // articulatedrvs
 
}
 

	
 
static void ResetCustomStations(void)
 
{
 
	StationSpec *statspec;
 
	GRFFile *file;
 
	uint i;
 
	uint t;
 

	
 
	for (file = _first_grffile; file != NULL; file = file->next) {
 
		if (file->stations == NULL) continue;
 
		for (i = 0; i < MAX_STATIONS; i++) {
 
			if (file->stations[i] == NULL) continue;
 
			statspec = file->stations[i];
 

	
 
			/* Release renderdata, if it wasn't copied from another custom station spec  */
 
			if (!statspec->copied_renderdata) {
 
				for (t = 0; t < statspec->tiles; t++) {
 
					free((void*)statspec->renderdata[t].seq);
 
				}
 
				free(statspec->renderdata);
 
			}
 

	
 
			/* Release platforms and layouts */
 
			if (!statspec->copied_layouts) {
 
				uint l, p;
 
				for (l = 0; l < statspec->lengths; l++) {
 
					for (p = 0; p < statspec->platforms[l]; p++) {
 
						free(statspec->layouts[l][p]);
 
					}
 
					free(statspec->layouts[l]);
 
				}
 
				free(statspec->layouts);
 
				free(statspec->platforms);
 
			}
 

	
 
			/* Release this station */
 
			free(statspec);
 
		}
 

	
 
		/* Free and reset the station data */
 
		free(file->stations);
 
		file->stations = NULL;
 
	}
 
}
 

	
 
static void ResetNewGRF(void)
 
{
 
	GRFFile *f, *next;
 

	
 
	for (f = _first_grffile; f != NULL; f = next) {
 
		next = f->next;
 

	
 
		free(f->filename);
 
		free(f);
 
	}
 

	
 
	_first_grffile = NULL;
 
	_cur_grffile   = NULL;
 
}
 

	
 
/**
 
 * Reset all NewGRF loaded data
 
 * TODO
 
 */
 
static void ResetNewGRFData(void)
 
{
 
	uint i;
 

	
 
	CleanUpStrings();
 

	
 
	// Copy/reset original engine info data
 
	memcpy(&_engine_info, &orig_engine_info, sizeof(orig_engine_info));
 
	memcpy(&_rail_vehicle_info, &orig_rail_vehicle_info, sizeof(orig_rail_vehicle_info));
 
	memcpy(&_ship_vehicle_info, &orig_ship_vehicle_info, sizeof(orig_ship_vehicle_info));
 
	memcpy(&_aircraft_vehicle_info, &orig_aircraft_vehicle_info, sizeof(orig_aircraft_vehicle_info));
 
	memcpy(&_road_vehicle_info, &orig_road_vehicle_info, sizeof(orig_road_vehicle_info));
 

	
 
	// Copy/reset original bridge info data
 
	// First, free sprite table data
 
	for (i = 0; i < MAX_BRIDGES; i++) {
 
		if (_bridge[i].sprite_table != NULL) {
 
			uint j;
 

	
 
			for (j = 0; j < 7; j++) free(_bridge[i].sprite_table[j]);
 
			free(_bridge[i].sprite_table);
 
		}
 
	}
 
	memcpy(&_bridge, &orig_bridge, sizeof(_bridge));
 

	
 
	// Reset refit/cargo class data
 
	memset(&cargo_allowed, 0, sizeof(cargo_allowed));
 
	memset(&cargo_disallowed, 0, sizeof(cargo_disallowed));
 

	
 
	// Reset GRM reservations
 
	memset(&_grm_engines, 0, sizeof(_grm_engines));
 

	
 
	// Unload sprite group data
 
	UnloadWagonOverrides();
 
	UnloadRotorOverrideSprites();
 
	UnloadCustomEngineSprites();
 
	UnloadCustomEngineNames();
 
	ResetEngineListOrder();
 

	
 
	// Reset price base data
 
	ResetPriceBaseMultipliers();
 

	
 
	/* Reset the curencies array */
 
	ResetCurrencies();
 

	
 
	// Reset station classes
 
	ResetStationClasses();
 
	ResetCustomStations();
 

	
 
	/* Reset NewGRF files */
 
	ResetNewGRF();
 

	
 
	// Add engine type to engine data. This is needed for the refit precalculation.
 
	AddTypeToEngines();
 

	
 
	/* Reset misc GRF features and train list display variables */
 
	_misc_grf_features = 0;
 
	_traininfo_vehicle_pitch = 0;
 
	_traininfo_vehicle_width = 29;
 
	_have_2cc = false;
 
	_signal_base = 0;
 
	_coast_base = 0;
 

	
 
	InitializeSoundPool();
 
	InitializeSpriteGroupPool();
 
}
 

	
 
/** Reset all NewGRFData that was used only while processing data */
 
static void ClearTemporaryNewGRFData(void)
 
{
 
	/* Clear the GOTO labels used for GRF processing */
 
	GRFLabel *l;
 
	for (l = _cur_grffile->label; l != NULL;) {
 
		GRFLabel *l2 = l->next;
 
		free(l);
 
		l = l2;
 
	}
 
	_cur_grffile->label = NULL;
 

	
 
	/* Clear the list of spritegroups */
 
	free(_cur_grffile->spritegroups);
 
	_cur_grffile->spritegroups = NULL;
 
	_cur_grffile->spritegroups_count = 0;
 
}
 

	
 
static void InitNewGRFFile(const GRFConfig *config, int sprite_offset)
 
{
 
	GRFFile *newfile;
 

	
 
	newfile = GetFileByFilename(config->filename);
 
	if (newfile != NULL) {
 
		/* We already loaded it once. */
 
		newfile->sprite_offset = sprite_offset;
 
		_cur_grffile = newfile;
 
		return;
 
	}
 

	
 
	newfile = calloc(1, sizeof(*newfile));
 

	
 
	if (newfile == NULL) error ("Out of memory");
 

	
 
	newfile->filename = strdup(config->filename);
 
	newfile->sprite_offset = sprite_offset;
 

	
 
	/* Copy the initial parameter list */
 
	assert(lengthof(newfile->param) == lengthof(config->param) && lengthof(config->param) == 0x80);
 
	newfile->param_end = config->num_params;
 
	memcpy(newfile->param, config->param, sizeof(newfile->param));
 

	
 
	if (_first_grffile == NULL) {
 
		_cur_grffile = newfile;
 
		_first_grffile = newfile;
 
	} else {
 
		_cur_grffile->next = newfile;
 
		_cur_grffile = newfile;
 
	}
 
}
 

	
 

	
 
/** Bitmasked values of what type of cargo is refittable for the given vehicle-type.
 
 * This coupled with the landscape information (_landscape_global_cargo_mask) gives
 
 * us exactly what is refittable and what is not */
 
#define MC(cargo) (1 << cargo)
 
static const uint32 _default_refitmasks[NUM_VEHICLE_TYPES] = {
 
	/* Trains */
 
	MC(GC_PASSENGERS) | MC(GC_COAL)      | MC(GC_MAIL)   | MC(GC_LIVESTOCK) | MC(GC_GOODS)        | MC(GC_GRAIN)      | MC(GC_WOOD)    | MC(GC_IRON_ORE)    |
 
	MC(GC_STEEL)      | MC(GC_VALUABLES) | MC(GC_PAPER)  | MC(GC_FOOD)      | MC(GC_FRUIT)        | MC(GC_COPPER_ORE) | MC(GC_WATER)   | MC(GC_SUGAR)       |
 
	MC(GC_TOYS)       | MC(GC_CANDY)     | MC(GC_TOFFEE) | MC(GC_COLA)      | MC(GC_COTTON_CANDY) | MC(GC_BUBBLES)    | MC(GC_PLASTIC) | MC(GC_FIZZY_DRINKS),
 
	/* Road vehicles (not refittable by default) */
 
	0,
 
	/* Ships */
 
	MC(GC_COAL)  | MC(GC_MAIL)   | MC(GC_LIVESTOCK) | MC(GC_GOODS)        | MC(GC_GRAIN)   | MC(GC_WOOD)    | MC(GC_IRON_ORE) | MC(GC_STEEL) | MC(GC_VALUABLES) |
 
	MC(GC_PAPER) | MC(GC_FOOD)   | MC(GC_FRUIT)     | MC(GC_COPPER_ORE)   | MC(GC_WATER)   | MC(GC_RUBBER)  | MC(GC_SUGAR)    | MC(GC_TOYS)  | MC(GC_BATTERIES) |
 
	MC(GC_CANDY) | MC(GC_TOFFEE) | MC(GC_COLA)      | MC(GC_COTTON_CANDY) | MC(GC_BUBBLES) | MC(GC_PLASTIC) | MC(GC_FIZZY_DRINKS),
 
	/* Aircraft */
 
	MC(GC_PASSENGERS) | MC(GC_MAIL)  | MC(GC_GOODS)  | MC(GC_VALUABLES) | MC(GC_FOOD)         | MC(GC_FRUIT)   | MC(GC_SUGAR)   | MC(GC_TOYS) |
 
	MC(GC_BATTERIES)  | MC(GC_CANDY) | MC(GC_TOFFEE) | MC(GC_COLA)      | MC(GC_COTTON_CANDY) | MC(GC_BUBBLES) | MC(GC_PLASTIC) | MC(GC_FIZZY_DRINKS),
 
	/* Special/Disaster */
 
	0,0
 
};
 
#undef MC
 

	
 

	
 
/**
 
 * Precalculate refit masks from cargo classes for all vehicles.
 
 */
 
static void CalculateRefitMasks(void)
 
{
 
	EngineID engine;
 

	
 
	for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) {
 
		uint32 mask = 0;
 
		uint32 not_mask = 0;
 
		uint32 xor_mask = _engine_info[engine].refit_mask;
 
		byte i;
 

	
 
		if (cargo_allowed[engine] != 0) {
 
			// Build up the list of cargo types from the set cargo classes.
 
			for (i = 0; i < lengthof(cargo_classes); i++) {
 
				if (HASBIT(cargo_allowed[engine], i)) mask |= cargo_classes[i];
 
				if (HASBIT(cargo_disallowed[engine], i)) not_mask |= cargo_classes[i];
 
			}
 
		} else {
 
			// Don't apply default refit mask to wagons or engines with no capacity
 
			if (xor_mask == 0 && (
 
						GetEngine(engine)->type != VEH_Train || (
 
							RailVehInfo(engine)->capacity != 0 &&
 
							!(RailVehInfo(engine)->flags & RVI_WAGON)
 
						)
 
					)) {
 
				xor_mask = _default_refitmasks[GetEngine(engine)->type - VEH_Train];
 
			}
 
		}
 
		_engine_info[engine].refit_mask = ((mask & ~not_mask) ^ xor_mask) & _landscape_global_cargo_mask[_opt.landscape];
 
	}
 
}
 

	
 
/* Here we perform initial decoding of some special sprites (as are they
 
 * described at http://www.ttdpatch.net/src/newgrf.txt, but this is only a very
 
 * partial implementation yet). */
 
/* XXX: We consider GRF files trusted. It would be trivial to exploit OTTD by
 
 * a crafted invalid GRF file. We should tell that to the user somehow, or
 
 * better make this more robust in the future. */
 
static void DecodeSpecialSprite(uint num, GrfLoadingStage stage)
 
{
 
	/* XXX: There is a difference between staged loading in TTDPatch and
 
	 * here.  In TTDPatch, for some reason actions 1 and 2 are carried out
 
	 * during stage 1, whilst action 3 is carried out during stage 2 (to
 
	 * "resolve" cargo IDs... wtf). This is a little problem, because cargo
 
	 * IDs are valid only within a given set (action 1) block, and may be
 
	 * overwritten after action 3 associates them. But overwriting happens
 
	 * in an earlier stage than associating, so...  We just process actions
 
	 * 1 and 2 in stage 2 now, let's hope that won't get us into problems.
 
	 * --pasky */
 
	/* We need a pre-stage to set up GOTO labels of Action 0x10 because the grf
 
	 * is not in memory and scanning the file every time would be too expensive.
 
	 * In other stages we skip action 0x10 since it's already dealt with. */
 
	static const SpecialSpriteHandler handlers[][GLS_END] = {
 
		/* 0x00 */ { NULL,     SafeChangeInfo, NULL,       NULL,       FeatureChangeInfo, },
 
		/* 0x01 */ { NULL,     GRFUnsafe, NULL,            NULL,       NewSpriteSet, },
 
		/* 0x02 */ { NULL,     GRFUnsafe, NULL,            NULL,       NewSpriteGroup, },
 
		/* 0x03 */ { NULL,     GRFUnsafe, NULL,            NULL,       FeatureMapSpriteGroup, },
 
		/* 0x04 */ { NULL,     NULL,      NULL,            NULL,       FeatureNewName, },
 
		/* 0x05 */ { NULL,     NULL,      NULL,            NULL,       GraphicsNew, },
 
		/* 0x06 */ { NULL,     NULL,      NULL,            CfgApply,   CfgApply, },
 
		/* 0x07 */ { NULL,     NULL,      NULL,            NULL,       SkipIf, },
 
		/* 0x08 */ { ScanInfo, NULL,      NULL,            GRFInfo,    GRFInfo, },
 
		/* 0x09 */ { NULL,     NULL,      NULL,            SkipIf,     SkipIf, },
 
		/* 0x0A */ { NULL,     NULL,      NULL,            NULL,       SpriteReplace, },
 
		/* 0x0B */ { NULL,     NULL,      NULL,            GRFError,   GRFError, },
 
		/* 0x0C */ { NULL,     NULL,      NULL,            GRFComment, GRFComment, },
 
		/* 0x0D */ { NULL,     SafeParamSet, NULL,         ParamSet,   ParamSet, },
 
		/* 0x0E */ { NULL,     SafeGRFInhibit, NULL,       GRFInhibit, GRFInhibit, },
 
		/* 0x0F */ { NULL,     NULL,      NULL,            NULL,       NULL, },
 
		/* 0x10 */ { NULL,     NULL,      DefineGotoLabel, NULL,       NULL, },
 
		/* 0x11 */ { NULL,     GRFUnsafe, NULL,            NULL,       GRFSound, },
 
		/* 0x12 */ { NULL,     NULL,      NULL,            NULL,       LoadFontGlyph, },
 
	};
 

	
 
	byte* buf;
 
	byte action;
 

	
 
	if (_preload_sprite == NULL) {
 
		/* No preloaded sprite to work with; allocate and read the
 
		 * pseudo sprite content. */
 
		buf = malloc(num);
 
		if (buf == NULL) error("DecodeSpecialSprite: Could not allocate memory");
 
		FioReadBlock(buf, num);
 
	} else {
 
		/* Use the preloaded sprite data. */
 
		buf = _preload_sprite;
 
		_preload_sprite = NULL;
 
		grfmsg(7, "DecodeSpecialSprite: Using preloaded pseudo sprite data");
 

	
 
		/* Skip the real (original) content of this action. */
 
		FioSeekTo(num, SEEK_CUR);
 
	}
 

	
 
	action = buf[0];
 

	
 
	if (action == 0xFF) {
 
		grfmsg(7, "Handling data block in stage %d", stage);
 
		GRFDataBlock(buf, num);
 
	} else if (action == 0xFE) {
 
		grfmsg(7, "Handling import block in stage %d", stage);
 
		GRFImportBlock(buf, num);
 
	} else if (action >= lengthof(handlers)) {
 
		grfmsg(7, "Skipping unknown action 0x%02X", action);
 
	} else if (handlers[action][stage] == NULL) {
 
		grfmsg(7, "Skipping action 0x%02X in stage %d", action, stage);
 
	} else {
 
		grfmsg(7, "Handling action 0x%02X in stage %d", action, stage);
 
		handlers[action][stage](buf, num);
 
	}
 
	free(buf);
 
}
 

	
 

	
 
void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage)
 
{
 
	const char *filename = config->filename;
 
	uint16 num;
 

	
 
	/* A .grf file is activated only if it was active when the game was
 
	 * started.  If a game is loaded, only its active .grfs will be
 
	 * reactivated, unless "loadallgraphics on" is used.  A .grf file is
 
	 * considered active if its action 8 has been processed, i.e. its
 
	 * action 8 hasn't been skipped using an action 7.
 
	 *
 
	 * During activation, only actions 0, 1, 2, 3, 4, 5, 7, 8, 9, 0A and 0B are
 
	 * carried out.  All others are ignored, because they only need to be
 
	 * processed once at initialization.  */
 
	if (stage != GLS_FILESCAN && stage != GLS_SAFETYSCAN && stage != GLS_LABELSCAN) {
 
		_cur_grffile = GetFileByFilename(filename);
 
		if (_cur_grffile == NULL) error("File '%s' lost in cache.\n", filename);
 
		if (stage == GLS_ACTIVATION && !HASBIT(config->flags, GCF_ACTIVATED)) return;
 
	}
 

	
 
	FioOpenFile(file_index, filename);
 
	_file_index = file_index; // XXX
 

	
 
	_cur_grfconfig = config;
 

	
 
	DEBUG(grf, 2, "Reading NewGRF-file '%s'", filename);
 

	
 
	/* Skip the first sprite; we don't care about how many sprites this
 
	 * does contain; newest TTDPatches and George's longvehicles don't
 
	 * neither, apparently. */
 
	if (FioReadWord() == 4 && FioReadByte() == 0xFF) {
 
		FioReadDword();
 
	} else {
 
		DEBUG(grf, 7, "Custom .grf has invalid format");
 
		return;
 
	}
 

	
 
	_skip_sprites = 0; // XXX
 
	_nfo_line = 0;
 

	
 
	while ((num = FioReadWord()) != 0) {
 
		byte type = FioReadByte();
 
		_nfo_line++;
 

	
 
		if (type == 0xFF) {
 
			if (_skip_sprites == 0) {
 
				DecodeSpecialSprite(num, stage);
 

	
 
				/* Stop all processing if we are to skip the remaining sprites */
 
				if (_skip_sprites == -1) break;
 

	
 
				continue;
 
			} else {
 
				FioSkipBytes(num);
 
			}
 
		} else {
 
			if (_skip_sprites == 0) grfmsg(7, "Skipping unexpected sprite");
 

	
 
			FioSkipBytes(7);
 
			num -= 8;
 

	
 
			if (type & 2) {
 
				FioSkipBytes(num);
 
			} else {
 
				while (num > 0) {
 
					int8 i = FioReadByte();
 
					if (i >= 0) {
 
						num -= i;
 
						FioSkipBytes(i);
 
					} else {
 
						i = -(i >> 3);
 
						num -= i;
 
						FioReadByte();
 
					}
 
				}
 
			}
 
		}
 

	
 
		if (_skip_sprites > 0) _skip_sprites--;
 
	}
 
}
 

	
 

	
 
void LoadNewGRF(uint load_index, uint file_index)
 
{
 
	GrfLoadingStage stage;
 

	
 
	InitializeGRFSpecial();
 

	
 
	ResetNewGRFData();
 

	
 
	/* Load newgrf sprites
 
	 * in each loading stage, (try to) open each file specified in the config
 
	 * and load information from it. */
 
	for (stage = GLS_LABELSCAN; stage <= GLS_ACTIVATION; stage++) {
 
		uint slot = file_index;
 
		GRFConfig *c;
 

	
 
		_cur_stage = stage;
 
		_cur_spriteid = load_index;
 
		for (c = _grfconfig; c != NULL; c = c->next) {
 
			if (HASBIT(c->flags, GCF_DISABLED) || HASBIT(c->flags, GCF_NOT_FOUND)) continue;
 

	
 
			// TODO usererror()
 
			if (!FioCheckFileExists(c->filename)) error("NewGRF file is missing '%s'", c->filename);
 

	
 
			if (stage == GLS_LABELSCAN) InitNewGRFFile(c, _cur_spriteid);
 
			LoadNewGRFFile(c, slot++, stage);
 
			if (stage == GLS_ACTIVATION) {
 
				ClearTemporaryNewGRFData();
 
				DEBUG(sprite, 2, "Currently %i sprites are loaded", _cur_spriteid);
 
			}
 
		}
 
	}
 

	
 
	// Pre-calculate all refit masks after loading GRF files
 
	CalculateRefitMasks();
 
}
 

	
 

	
src/newgrf_cargo.c
Show inline comments
 
deleted file
src/newgrf_cargo.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "newgrf_cargo.h"
 

	
 
/** TRANSLATE FROM LOCAL CARGO TO GLOBAL CARGO ID'S.
 
 * This maps the per-landscape cargo ID's to globally unique cargo ID's usable ie. in
 
 * the custom GRF  files. It is basically just a transcribed table from TTDPatch's newgrf.txt.
 
 */
 
const CargoID _global_cargo_id[NUM_LANDSCAPE][NUM_CARGO] = {
 
	/* LT_NORMAL */ {GC_PASSENGERS, GC_COAL,   GC_MAIL, GC_OIL,  GC_LIVESTOCK, GC_GOODS, GC_GRAIN,  GC_WOOD, GC_IRON_ORE,     GC_STEEL,   GC_VALUABLES, GC_PAPER_TEMP},
 
	/* LT_HILLY */  {GC_PASSENGERS, GC_COAL,   GC_MAIL, GC_OIL,  GC_LIVESTOCK, GC_GOODS, GC_GRAIN,  GC_WOOD, GC_INVALID,      GC_PAPER,   GC_VALUABLES, GC_FOOD },
 
	/* LT_DESERT */ {GC_PASSENGERS, GC_RUBBER, GC_MAIL, GC_OIL,  GC_FRUIT,     GC_GOODS, GC_GRAIN,  GC_WOOD, GC_COPPER_ORE,   GC_WATER,   GC_VALUABLES, GC_FOOD },
 
	/* LT_CANDY */  {GC_PASSENGERS, GC_SUGAR,  GC_MAIL, GC_TOYS, GC_BATTERIES, GC_CANDY, GC_TOFFEE, GC_COLA, GC_COTTON_CANDY, GC_BUBBLES, GC_PLASTIC,   GC_FIZZY_DRINKS },
 
	/**
 
	 * - GC_INVALID (255) means that  cargo is not available for that climate
 
	 * - GC_PAPER_TEMP (27) is paper in  temperate climate in TTDPatch
 
	 * Following can  be renumbered:
 
	 * - GC_DEFAULT (29) is the defa ult cargo for the purpose of spritesets
 
	 * - GC_PURCHASE (30) is the purchase list image (the equivalent of 0xff) for the purpose of spritesets
 
	 */
 
};
 

	
 
/** BEGIN --- TRANSLATE FROM GLOBAL CARGO TO LOCAL CARGO ID'S **/
 
/** Map global cargo ID's to local-cargo ID's */
 
const CargoID _local_cargo_id_ctype[NUM_GLOBAL_CID] = {
 
	CT_PASSENGERS, CT_COAL,    CT_MAIL,         CT_OIL,       CT_LIVESTOCK, CT_GOODS,  CT_GRAIN,      CT_WOOD,         /*  0- 7 */
 
	CT_IRON_ORE,   CT_STEEL,   CT_VALUABLES,    CT_PAPER,     CT_FOOD,      CT_FRUIT,  CT_COPPER_ORE, CT_WATER,        /*  8-15 */
 
	CT_RUBBER,     CT_SUGAR,   CT_TOYS,         CT_BATTERIES, CT_CANDY,     CT_TOFFEE, CT_COLA,       CT_COTTON_CANDY, /* 16-23 */
 
	CT_BUBBLES,    CT_PLASTIC, CT_FIZZY_DRINKS, CT_PAPER      /* unsup. */, CT_HILLY_UNUSED,                           /* 24-28 */
 
	CT_INVALID,    CT_INVALID                                                                                          /* 29-30 */
 
};
 

	
 
/** Bitmasked value where the global cargo ID is available in landscape
 
 * 0: LT_NORMAL, 1: LT_HILLY, 2: LT_DESERT, 3: LT_CANDY */
 
#define MC(cargo) (1 << cargo)
 
const uint32 _landscape_global_cargo_mask[NUM_LANDSCAPE] =
 
{ /* LT_NORMAL: temperate */
 
	MC(GC_PASSENGERS) | MC(GC_COAL) | MC(GC_MAIL)  | MC(GC_OIL)   | MC(GC_LIVESTOCK) | MC(GC_GOODS) | MC(GC_GRAIN)     | MC(GC_WOOD) | MC(GC_IRON_ORE)     | MC(GC_STEEL)      | MC(GC_VALUABLES),
 
	/* LT_HILLY: arctic */
 
	MC(GC_PASSENGERS) | MC(GC_COAL) | MC(GC_MAIL)  | MC(GC_OIL)   | MC(GC_LIVESTOCK) | MC(GC_GOODS) | MC(GC_GRAIN)     | MC(GC_WOOD) | MC(GC_VALUABLES)    | MC(GC_PAPER)      | MC(GC_FOOD),
 
	/* LT_DESERT: rainforest/desert */
 
	MC(GC_PASSENGERS) | MC(GC_MAIL) | MC(GC_OIL)   | MC(GC_GOODS) | MC(GC_GRAIN)     | MC(GC_WOOD)  | MC(GC_VALUABLES) | MC(GC_FOOD) | MC(GC_FRUIT)        | MC(GC_COPPER_ORE) | MC(GC_WATER)   | MC(GC_RUBBER),
 
	/* LT_CANDY: toyland */
 
	MC(GC_PASSENGERS) | MC(GC_MAIL) | MC(GC_SUGAR) | MC(GC_TOYS)  | MC(GC_BATTERIES) | MC(GC_CANDY) | MC(GC_TOFFEE)    | MC(GC_COLA) | MC(GC_COTTON_CANDY) | MC(GC_BUBBLES)    | MC(GC_PLASTIC) | MC(GC_FIZZY_DRINKS)
 
};
 
/** END   --- TRANSLATE FROM GLOBAL CARGO TO LOCAL CARGO ID'S **/
 

	
 
/**
 
 * Bitmask of classes for cargo types.
 
 */
 
const uint32 cargo_classes[16] = {
 
	/* Passengers */ MC(GC_PASSENGERS),
 
	/* Mail       */ MC(GC_MAIL),
 
	/* Express    */ MC(GC_GOODS)     | MC(GC_FOOD)  | MC(GC_CANDY),
 
	/* Armoured   */ MC(GC_VALUABLES),
 
	/* Bulk       */ MC(GC_COAL)      | MC(GC_GRAIN) | MC(GC_IRON_ORE) | MC(GC_COPPER_ORE) | MC(GC_FRUIT)   | MC(GC_SUGAR)     | MC(GC_TOFFEE)  | MC(GC_COTTON_CANDY),
 
	/* Piece      */ MC(GC_LIVESTOCK) | MC(GC_WOOD)  | MC(GC_STEEL)    | MC(GC_PAPER)      | MC(GC_TOYS)    | MC(GC_BATTERIES) | MC(GC_BUBBLES) | MC(GC_FIZZY_DRINKS),
 
	/* Liquids    */ MC(GC_OIL)       | MC(GC_WATER) | MC(GC_RUBBER)   | MC(GC_COLA)       | MC(GC_PLASTIC),
 
	/* Chilled    */ MC(GC_FOOD)      | MC(GC_FRUIT),
 
	/* Undefined  */ 0, 0, 0, 0, 0, 0, 0, 0
 
};
 
#undef MC
 

	
 
/**
 
 *there are 32 slots available per climate with newcargo.*/
 
#define MAXSLOTS 32
src/newgrf_config.c
Show inline comments
 
deleted file
src/newgrf_config.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "macros.h"
 
#include "debug.h"
 
#include "variables.h"
 
#include "string.h"
 
#include "saveload.h"
 
#include "md5.h"
 
#include "network/network_data.h"
 
#include "newgrf.h"
 
#include "newgrf_config.h"
 

	
 
#include "fileio.h"
 
#include "fios.h"
 
#include <sys/types.h>
 
#include <sys/stat.h>
 

	
 
#ifdef WIN32
 
# include <io.h>
 
#else
 
# include <unistd.h>
 
# include <dirent.h>
 
#endif /* WIN32 */
 

	
 

	
 
GRFConfig *_all_grfs;
 
GRFConfig *_grfconfig;
 
GRFConfig *_grfconfig_newgame;
 
GRFConfig *_grfconfig_static;
 

	
 

	
 
/* Calculate the MD5 Sum for a GRF */
 
static bool CalcGRFMD5Sum(GRFConfig *config)
 
{
 
	FILE *f;
 
	char filename[MAX_PATH];
 
	md5_state_t md5state;
 
	md5_byte_t buffer[1024];
 
	size_t len;
 

	
 
	/* open the file */
 
	snprintf(filename, lengthof(filename), "%s%s", _paths.data_dir, config->filename);
 
	f = fopen(filename, "rb");
 
	if (f == NULL) return false;
 

	
 
	/* calculate md5sum */
 
	md5_init(&md5state);
 
	while ((len = fread(buffer, 1, sizeof(buffer), f)) != 0) {
 
		md5_append(&md5state, buffer, len);
 
	}
 
	md5_finish(&md5state, config->md5sum);
 

	
 
	fclose(f);
 

	
 
	return true;
 
}
 

	
 

	
 
/* Find the GRFID and calculate the md5sum */
 
bool FillGRFDetails(GRFConfig *config, bool is_static)
 
{
 
	if (!FioCheckFileExists(config->filename)) {
 
		SETBIT(config->flags, GCF_NOT_FOUND);
 
		return false;
 
	}
 

	
 
	/* Find and load the Action 8 information */
 
	/* 62 is the last file slot before sample.cat.
 
	 * Should perhaps be some "don't care" value */
 
	LoadNewGRFFile(config, 62, GLS_FILESCAN);
 

	
 
	/* Skip if the grfid is 0 (not read) or 0xFFFFFFFF (ttdp system grf) */
 
	if (config->grfid == 0 || config->grfid == 0xFFFFFFFF) return false;
 

	
 
	if (is_static) {
 
		/* Perform a 'safety scan' for static GRFs */
 
		LoadNewGRFFile(config, 62, GLS_SAFETYSCAN);
 

	
 
		/* GCF_UNSAFE is set if GLS_SAFETYSCAN finds unsafe actions */
 
		if (HASBIT(config->flags, GCF_UNSAFE)) return false;
 
	}
 

	
 
	return CalcGRFMD5Sum(config);
 
}
 

	
 

	
 
void ClearGRFConfig(GRFConfig **config)
 
{
 
	/* GCF_COPY as in NOT strdupped/alloced the filename, name and info */
 
	if (!HASBIT((*config)->flags, GCF_COPY)) {
 
		free((*config)->filename);
 
		free((*config)->name);
 
		free((*config)->info);
 
	}
 
	free(*config);
 
	*config = NULL;
 
}
 

	
 

	
 
/* Clear a GRF Config list */
 
void ClearGRFConfigList(GRFConfig **config)
 
{
 
	GRFConfig *c, *next;
 
	for (c = *config; c != NULL; c = next) {
 
		next = c->next;
 
		ClearGRFConfig(&c);
 
	}
 
	*config = NULL;
 
}
 

	
 

	
 
/** Copy a GRF Config list
 
 * @param dst pointer to destination list
 
 * @param srt pointer to source list values
 
 * @return pointer to the last value added to the destination list */
 
GRFConfig **CopyGRFConfigList(GRFConfig **dst, const GRFConfig *src)
 
{
 
	GRFConfig *c;
 

	
 
	/* Clear destination as it will be overwritten */
 
	ClearGRFConfigList(dst);
 
	for (; src != NULL; src = src->next) {
 
		c = calloc(1, sizeof(*c));
 
		*c = *src;
 
		if (src->filename != NULL) c->filename = strdup(src->filename);
 
		if (src->name     != NULL) c->name     = strdup(src->name);
 
		if (src->info     != NULL) c->info     = strdup(src->info);
 

	
 
		*dst = c;
 
		dst = &c->next;
 
	}
 

	
 
	return dst;
 
}
 

	
 
/**
 
 * Removes duplicates from lists of GRFConfigs. These duplicates
 
 * are introduced when the _grfconfig_static GRFs are appended
 
 * to the _grfconfig on a newgame or savegame. As the parameters
 
 * of the static GRFs could be different that the parameters of
 
 * the ones used non-statically. This can result in desyncs in
 
 * multiplayers, so the duplicate static GRFs have to be removed.
 
 *
 
 * This function _assumes_ that all static GRFs are placed after
 
 * the non-static GRFs.
 
 *
 
 * @param list the list to remove the duplicates from
 
 */
 
static void RemoveDuplicatesFromGRFConfigList(GRFConfig *list)
 
{
 
	GRFConfig *prev;
 
	GRFConfig *cur;
 

	
 
	if (list == NULL) return;
 

	
 
	for (prev = list, cur = list->next; cur != NULL; prev = cur, cur = cur->next) {
 
		if (cur->grfid != list->grfid) continue;
 
		assert(HASBIT(cur->flags, GCF_STATIC));
 
		prev->next = cur->next;
 
		ClearGRFConfig(&cur);
 
		cur = prev; // Just go back one so it continues as normal later on
 
	}
 

	
 
	RemoveDuplicatesFromGRFConfigList(list->next);
 
}
 

	
 
/**
 
 * Appends the static GRFs to a list of GRFs
 
 * @param dst the head of the list to add to
 
 */
 
void AppendStaticGRFConfigs(GRFConfig **dst)
 
{
 
	GRFConfig **tail = dst;
 
	while (*tail != NULL) tail = &(*tail)->next;
 

	
 
	CopyGRFConfigList(tail, _grfconfig_static);
 
	RemoveDuplicatesFromGRFConfigList(*dst);
 
}
 

	
 

	
 
/* Reset the current GRF Config to either blank or newgame settings */
 
void ResetGRFConfig(bool defaults)
 
{
 
	GRFConfig **c = &_grfconfig;
 

	
 
	if (defaults) {
 
		c = CopyGRFConfigList(c, _grfconfig_newgame);
 
	} else {
 
		ClearGRFConfigList(c);
 
	}
 

	
 
	AppendStaticGRFConfigs(&_grfconfig);
 
}
 

	
 

	
 
/* Check if all GRFs in the GRF Config can be loaded */
 
bool IsGoodGRFConfigList(void)
 
{
 
	bool res = true;
 
	GRFConfig *c;
 

	
 
	for (c = _grfconfig; c != NULL; c = c->next) {
 
		const GRFConfig *f = FindGRFConfig(c->grfid, c->md5sum);
 
		if (f == NULL) {
 
			char buf[512], *p = buf;
 
			uint i;
 

	
 
			p += snprintf(p, lastof(buf) - p, "Couldn't find NewGRF %08X (%s) checksum ", BSWAP32(c->grfid), c->filename);
 
			for (i = 0; i < lengthof(c->md5sum); i++) {
 
				p += snprintf(p, lastof(buf) - p, "%02X", c->md5sum[i]);
 
			}
 
			ShowInfo(buf);
 

	
 
			res = false;
 
		} else {
 
			DEBUG(grf, 1, "Loading GRF %08X from '%s'", BSWAP32(c->grfid), f->filename);
 
			/* The filename could be the filename as in the savegame. As we need
 
			 * to load the GRF here, we need the correct filename, so overwrite that
 
			 * in any case and set the name and info when it is not set already.
 
			 * When the GCF_COPY flag is set, it is certain that the filename is
 
			 * already a local one, so there is no need to replace it. */
 
			if (!HASBIT(c->flags, GCF_COPY)) {
 
				free(c->filename);
 
				c->filename = strdup(f->filename);
 
				if (c->name == NULL) c->name = strdup(f->name);
 
				if (c->info == NULL) c->info = strdup(f->info);
 
			}
 
		}
 
	}
 

	
 
	return res;
 
}
 

	
 

	
 
extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
 

	
 
/* Scan a path for NewGRFs */
 
static uint ScanPath(const char *path)
 
{
 
	uint num = 0;
 
	struct stat sb;
 
	struct dirent *dirent;
 
	DIR *dir;
 
	GRFConfig *c;
 

	
 
	if ((dir = opendir(path)) == NULL) return 0;
 

	
 
	while ((dirent = readdir(dir)) != NULL) {
 
		const char *d_name = FS2OTTD(dirent->d_name);
 
		char filename[MAX_PATH];
 

	
 
		if (!FiosIsValidFile(path, dirent, &sb)) continue;
 

	
 
		snprintf(filename, lengthof(filename), "%s" PATHSEP "%s", path, d_name);
 

	
 
		if (sb.st_mode & S_IFDIR) {
 
			/* Directory */
 
			if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
 
			num += ScanPath(filename);
 
		} else if (sb.st_mode & S_IFREG) {
 
			/* File */
 
			char *ext = strrchr(filename, '.');
 
			char *file = filename + strlen(_paths.data_dir) + 1; // Crop base path
 

	
 
			/* If no extension or extension isn't .grf, skip the file */
 
			if (ext == NULL) continue;
 
			if (strcasecmp(ext, ".grf") != 0) continue;
 

	
 
			c = calloc(1, sizeof(*c));
 
			c->filename = strdup(file);
 

	
 
			if (FillGRFDetails(c, false)) {
 
				if (_all_grfs == NULL) {
 
					_all_grfs = c;
 
				} else {
 
					/* Insert file into list at a position determined by its
 
					 * name, so the list is sorted as we go along */
 
					GRFConfig **pd, *d;
 
					for (pd = &_all_grfs; (d = *pd) != NULL; pd = &d->next) {
 
						if (strcasecmp(c->name, d->name) <= 0) break;
 
					}
 
					c->next = d;
 
					*pd = c;
 
				}
 

	
 
				num++;
 
			} else {
 
				/* File couldn't be opened, or is either not a NewGRF or is a
 
				 * 'system' NewGRF, so forget about it. */
 
				free(c->filename);
 
				free(c->name);
 
				free(c->info);
 
				free(c);
 
			}
 
		}
 
	}
 

	
 
	closedir(dir);
 

	
 
	return num;
 
}
 

	
 

	
 
/* Scan for all NewGRFs */
 
void ScanNewGRFFiles(void)
 
{
 
	uint num;
 

	
 
	ClearGRFConfigList(&_all_grfs);
 

	
 
	DEBUG(grf, 1, "Scanning for NewGRFs");
 
	num = ScanPath(_paths.data_dir);
 
	DEBUG(grf, 1, "Scan complete, found %d files", num);
 
}
 

	
 

	
 
/* Find a NewGRF in the scanned list */
 
const GRFConfig *FindGRFConfig(uint32 grfid, uint8 *md5sum)
 
{
 
	GRFConfig *c;
 
	static const uint8 blanksum[sizeof(c->md5sum)] = { 0 };
 

	
 
	for (c = _all_grfs; c != NULL; c = c->next) {
 
		if (c->grfid == grfid) {
 
			if (memcmp(blanksum, c->md5sum, sizeof(c->md5sum)) == 0) CalcGRFMD5Sum(c);
 
			if (memcmp(md5sum, c->md5sum, sizeof(c->md5sum)) == 0) return c;
 
		}
 
	}
 

	
 
	return NULL;
 
}
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
/** Structure for UnknownGRFs; this is a lightweight variant of GRFConfig */
 
typedef struct UnknownGRF UnknownGRF;
 
struct UnknownGRF {
 
	UnknownGRF *next;
 
	uint32 grfid;
 
	uint8  md5sum[16];
 
	char   name[NETWORK_GRF_NAME_LENGTH];
 
};
 

	
 
/**
 
 * Finds the name of a NewGRF in the list of names for unknown GRFs. An
 
 * unknown GRF is a GRF where the .grf is not found during scanning.
 
 *
 
 * The names are resolved via UDP calls to servers that should know the name,
 
 * though the replies may not come. This leaves "<Unknown>" as name, though
 
 * that shouldn't matter _very_ much as they need GRF crawler or so to look
 
 * up the GRF anyway and that works better with the GRF ID.
 
 *
 
 * @param grfid  the GRF ID part of the 'unique' GRF identifier
 
 * @param md5sum the MD5 checksum part of the 'unique' GRF identifier
 
 * @param create whether to create a new GRFConfig if the GRFConfig did not
 
 *               exist in the fake list of GRFConfigs.
 
 * @return the GRFConfig with the given GRF ID and MD5 checksum or NULL when
 
 *         it does not exist and create is false. This value must NEVER be
 
 *         freed by the caller.
 
 */
 
char *FindUnknownGRFName(uint32 grfid, uint8 *md5sum, bool create)
 
{
 
	UnknownGRF *grf;
 
	static UnknownGRF *unknown_grfs = NULL;
 

	
 
	for (grf = unknown_grfs; grf != NULL; grf = grf->next) {
 
		if (grf->grfid == grfid) {
 
			if (memcmp(md5sum, grf->md5sum, sizeof(grf->md5sum)) == 0) return grf->name;
 
		}
 
	}
 

	
 
	if (!create) return NULL;
 

	
 
	grf = calloc(1, sizeof(*grf));
 
	grf->grfid = grfid;
 
	grf->next  = unknown_grfs;
 
	ttd_strlcpy(grf->name, UNKNOWN_GRF_NAME_PLACEHOLDER, sizeof(grf->name));
 
	memcpy(grf->md5sum, md5sum, sizeof(grf->md5sum));
 

	
 
	unknown_grfs = grf;
 
	return grf->name;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 

	
 
/* Retrieve a NewGRF from the current config by its grfid */
 
GRFConfig *GetGRFConfig(uint32 grfid)
 
{
 
	GRFConfig *c;
 

	
 
	for (c = _grfconfig; c != NULL; c = c->next) {
 
		if (c->grfid == grfid) return c;
 
	}
 

	
 
	return NULL;
 
}
 

	
 

	
 
/* Build a space separated list of parameters, and terminate */
 
char *GRFBuildParamList(char *dst, const GRFConfig *c, const char *last)
 
{
 
	uint i;
 

	
 
	/* Return an empty string if there are no parameters */
 
	if (c->num_params == 0) return strecpy(dst, "", last);
 

	
 
	for (i = 0; i < c->num_params; i++) {
 
		if (i > 0) dst = strecpy(dst, " ", last);
 
		dst += snprintf(dst, last - dst, "%d", c->param[i]);
 
	}
 
	return dst;
 
}
 

	
 

	
 
static const SaveLoad _grfconfig_desc[] = {
 
	SLE_STR(GRFConfig, filename,   SLE_STR, 0x40),
 
	SLE_VAR(GRFConfig, grfid,      SLE_UINT32),
 
	SLE_ARR(GRFConfig, md5sum,     SLE_UINT8, 16),
 
	SLE_ARR(GRFConfig, param,      SLE_UINT32, 0x80),
 
	SLE_VAR(GRFConfig, num_params, SLE_UINT8),
 
	SLE_END()
 
};
 

	
 

	
 
static void Save_NGRF(void)
 
{
 
	GRFConfig *c;
 
	int index = 0;
 

	
 
	for (c = _grfconfig; c != NULL; c = c->next) {
 
		if (HASBIT(c->flags, GCF_STATIC)) continue;
 
		SlSetArrayIndex(index++);
 
		SlObject(c, _grfconfig_desc);
 
	}
 
}
 

	
 

	
 
static void Load_NGRF(void)
 
{
 
	GRFConfig *first = NULL;
 
	GRFConfig **last = &first;
 

	
 
	while (SlIterateArray() != -1) {
 
		GRFConfig *c = calloc(1, sizeof(*c));
 
		SlObject(c, _grfconfig_desc);
 

	
 
		/* Append our configuration to the list */
 
		*last = c;
 
		last = &c->next;
 
	}
 

	
 
	/* Append static NewGRF configuration */
 
	CopyGRFConfigList(last, _grfconfig_static);
 

	
 
	ClearGRFConfigList(&_grfconfig);
 
	_grfconfig = first;
 
	AppendStaticGRFConfigs(&_grfconfig);
 
}
 

	
 
const ChunkHandler _newgrf_chunk_handlers[] = {
 
	{ 'NGRF', Save_NGRF, Load_NGRF, CH_ARRAY | CH_LAST }
 
};
 

	
 

	
src/newgrf_engine.c
Show inline comments
 
deleted file
src/newgrf_engine.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "variables.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "engine.h"
 
#include "train.h"
 
#include "player.h"
 
#include "station.h"
 
#include "airport.h"
 
#include "newgrf.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_station.h"
 
#include "newgrf_spritegroup.h"
 
#include "newgrf_cargo.h"
 
#include "date.h"
 

	
 

	
 

	
 
/* Default cargo classes */
 
static const uint16 _cargo_classes[NUM_GLOBAL_CID] = {
 
	CC_PASSENGERS,
 
	CC_BULK,
 
	CC_MAIL,
 
	CC_LIQUID,
 
	CC_PIECE_GOODS,
 
	CC_EXPRESS,
 
	CC_BULK,
 
	CC_PIECE_GOODS,
 
	CC_BULK,
 
	CC_PIECE_GOODS,
 
	CC_ARMOURED,
 
	CC_PIECE_GOODS,
 
	CC_REFRIGERATED | CC_EXPRESS,
 
	CC_REFRIGERATED | CC_EXPRESS,
 
	CC_BULK,
 
	CC_LIQUID,
 
	CC_LIQUID,
 
	CC_BULK,
 
	CC_PIECE_GOODS,
 
	CC_PIECE_GOODS,
 
	CC_EXPRESS,
 
	CC_BULK,
 
	CC_LIQUID,
 
	CC_BULK,
 
	CC_PIECE_GOODS,
 
	CC_LIQUID,
 
	CC_PIECE_GOODS,
 
	CC_PIECE_GOODS,
 
	CC_NOAVAILABLE,
 
	CC_NOAVAILABLE,
 
	CC_NOAVAILABLE,
 
};
 

	
 
int _traininfo_vehicle_pitch = 0;
 
int _traininfo_vehicle_width = 29;
 

	
 
typedef struct WagonOverride {
 
	byte *train_id;
 
	int trains;
 
	CargoID cargo;
 
	const SpriteGroup *group;
 
} WagonOverride;
 

	
 
typedef struct WagonOverrides {
 
	int overrides_count;
 
	WagonOverride *overrides;
 
} WagonOverrides;
 

	
 
static WagonOverrides _engine_wagon_overrides[TOTAL_NUM_ENGINES];
 

	
 
void SetWagonOverrideSprites(EngineID engine, CargoID cargo, const SpriteGroup *group, byte *train_id, int trains)
 
{
 
	WagonOverrides *wos;
 
	WagonOverride *wo;
 

	
 
	assert(engine < TOTAL_NUM_ENGINES);
 
	assert(cargo < NUM_GLOBAL_CID);
 

	
 
	wos = &_engine_wagon_overrides[engine];
 
	wos->overrides_count++;
 
	wos->overrides = realloc(wos->overrides,
 
		wos->overrides_count * sizeof(*wos->overrides));
 

	
 
	wo = &wos->overrides[wos->overrides_count - 1];
 
	/* FIXME: If we are replacing an override, release original SpriteGroup
 
	 * to prevent leaks. But first we need to refcount the SpriteGroup.
 
	 * --pasky */
 
	wo->group = group;
 
	wo->cargo = cargo;
 
	wo->trains = trains;
 
	wo->train_id = malloc(trains);
 
	memcpy(wo->train_id, train_id, trains);
 
}
 

	
 
static const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, CargoID cargo, byte overriding_engine)
 
{
 
	const WagonOverrides *wos = &_engine_wagon_overrides[engine];
 
	int i;
 

	
 
	// XXX: This could turn out to be a timesink on profiles. We could
 
	// always just dedicate 65535 bytes for an [engine][train] trampoline
 
	// for O(1). Or O(logMlogN) and searching binary tree or smt. like
 
	// that. --pasky
 

	
 
	for (i = 0; i < wos->overrides_count; i++) {
 
		const WagonOverride *wo = &wos->overrides[i];
 
		int j;
 

	
 
		for (j = 0; j < wo->trains; j++) {
 
			if (wo->train_id[j] == overriding_engine && (wo->cargo == cargo || wo->cargo == GC_DEFAULT)) return wo->group;
 
		}
 
	}
 
	return NULL;
 
}
 

	
 
/**
 
 * Unload all wagon override sprite groups.
 
 */
 
void UnloadWagonOverrides(void)
 
{
 
	WagonOverrides *wos;
 
	WagonOverride *wo;
 
	EngineID engine;
 
	int i;
 

	
 
	for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) {
 
		wos = &_engine_wagon_overrides[engine];
 
		for (i = 0; i < wos->overrides_count; i++) {
 
			wo = &wos->overrides[i];
 
			wo->group = NULL;
 
			free(wo->train_id);
 
		}
 
		free(wos->overrides);
 
		wos->overrides_count = 0;
 
		wos->overrides = NULL;
 
	}
 
}
 

	
 
// 0 - 28 are cargos, 29 is default, 30 is the advert (purchase list)
 
// (It isn't and shouldn't be like this in the GRF files since new cargo types
 
// may appear in future - however it's more convenient to store it like this in
 
// memory. --pasky)
 
static const SpriteGroup *engine_custom_sprites[TOTAL_NUM_ENGINES][NUM_GLOBAL_CID];
 
static const GRFFile *_engine_grf[TOTAL_NUM_ENGINES];
 

	
 
void SetCustomEngineSprites(EngineID engine, byte cargo, const SpriteGroup *group)
 
{
 
	assert(engine < TOTAL_NUM_ENGINES);
 
	assert(cargo < NUM_GLOBAL_CID);
 

	
 
	if (engine_custom_sprites[engine][cargo] != NULL) {
 
		grfmsg(6, "SetCustomEngineSprites: engine %d cargo %d already has group -- replacing", engine, cargo);
 
	}
 
	engine_custom_sprites[engine][cargo] = group;
 
}
 

	
 
/**
 
 * Unload all engine sprite groups.
 
 */
 
void UnloadCustomEngineSprites(void)
 
{
 
	EngineID engine;
 
	CargoID cargo;
 

	
 
	for (engine = 0; engine < TOTAL_NUM_ENGINES; engine++) {
 
		for (cargo = 0; cargo < NUM_GLOBAL_CID; cargo++) {
 
			engine_custom_sprites[engine][cargo] = NULL;
 
		}
 
		_engine_grf[engine] = 0;
 
	}
 
}
 

	
 
static const SpriteGroup *heli_rotor_custom_sprites[NUM_AIRCRAFT_ENGINES];
 

	
 
/** Load a rotor override sprite group for an aircraft */
 
void SetRotorOverrideSprites(EngineID engine, const SpriteGroup *group)
 
{
 
	assert(engine >= AIRCRAFT_ENGINES_INDEX);
 
	assert(engine < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES);
 

	
 
	if (heli_rotor_custom_sprites[engine - AIRCRAFT_ENGINES_INDEX] != NULL) {
 
		grfmsg(6, "SetRotorOverrideSprites: engine %d already has group -- replacing.", engine);
 
	}
 
	heli_rotor_custom_sprites[engine - AIRCRAFT_ENGINES_INDEX] = group;
 
}
 

	
 
/** Unload all rotor override sprite groups */
 
void UnloadRotorOverrideSprites(void)
 
{
 
	EngineID engine;
 

	
 
	/* Starting at AIRCRAFT_ENGINES_INDEX may seem pointless, but it means
 
	 * the context of EngineID is correct */
 
	for (engine = AIRCRAFT_ENGINES_INDEX; engine < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES; engine++) {
 
		heli_rotor_custom_sprites[engine - AIRCRAFT_ENGINES_INDEX] = NULL;
 
	}
 
}
 

	
 

	
 
/**
 
 * Tie a GRFFile entry to an engine, to allow us to retrieve GRF parameters
 
 * etc during a game.
 
 * @param engine Engine ID to tie the GRFFile to.
 
 * @param file   Pointer of GRFFile to tie.
 
 */
 
void SetEngineGRF(EngineID engine, const GRFFile *file)
 
{
 
	assert(engine < TOTAL_NUM_ENGINES);
 
	_engine_grf[engine] = file;
 
}
 

	
 

	
 
/**
 
 * Retrieve the GRFFile tied to an engine
 
 * @param engine Engine ID to retrieve.
 
 * @return Pointer to GRFFile.
 
 */
 
const GRFFile *GetEngineGRF(EngineID engine)
 
{
 
	assert(engine < TOTAL_NUM_ENGINES);
 
	return _engine_grf[engine];
 
}
 

	
 

	
 
/**
 
 * Retrieve the GRF ID of the GRFFile tied to an engine
 
 * @param engine Engine ID to retrieve.
 
 * @return 32 bit GRFID value.
 
 */
 
uint32 GetEngineGRFID(EngineID engine)
 
{
 
	assert(engine < TOTAL_NUM_ENGINES);
 
	return _engine_grf[engine]->grfid;
 
}
 

	
 

	
 
static int MapOldSubType(const Vehicle *v)
 
{
 
	if (v->type != VEH_Train) return v->subtype;
 
	if (IsTrainEngine(v)) return 0;
 
	if (IsFreeWagon(v)) return 4;
 
	return 2;
 
}
 

	
 

	
 
/* TTDP style aircraft movement states for GRF Action 2 Var 0xE2 */
 
enum {
 
	AMS_TTDP_HANGAR,
 
	AMS_TTDP_TO_HANGAR,
 
	AMS_TTDP_TO_PAD1,
 
	AMS_TTDP_TO_PAD2,
 
	AMS_TTDP_TO_PAD3,
 
	AMS_TTDP_TO_ENTRY_2_AND_3,
 
	AMS_TTDP_TO_ENTRY_2_AND_3_AND_H,
 
	AMS_TTDP_TO_JUNCTION,
 
	AMS_TTDP_LEAVE_RUNWAY,
 
	AMS_TTDP_TO_INWAY,
 
	AMS_TTDP_TO_RUNWAY,
 
	AMS_TTDP_TO_OUTWAY,
 
	AMS_TTDP_WAITING,
 
	AMS_TTDP_TAKEOFF,
 
	AMS_TTDP_TO_TAKEOFF,
 
	AMS_TTDP_CLIMBING,
 
	AMS_TTDP_FLIGHT_APPROACH,
 
	AMS_TTDP_UNUSED_0x11,
 
	AMS_TTDP_FLIGHT_TO_TOWER,
 
	AMS_TTDP_UNUSED_0x13,
 
	AMS_TTDP_FLIGHT_FINAL,
 
	AMS_TTDP_FLIGHT_DESCENT,
 
	AMS_TTDP_BRAKING,
 
	AMS_TTDP_HELI_TAKEOFF_AIRPORT,
 
	AMS_TTDP_HELI_TO_TAKEOFF_AIRPORT,
 
	AMS_TTDP_HELI_LAND_AIRPORT,
 
	AMS_TTDP_HELI_TAKEOFF_HELIPORT,
 
	AMS_TTDP_HELI_TO_TAKEOFF_HELIPORT,
 
	AMS_TTDP_HELI_LAND_HELIPORT,
 
};
 

	
 

	
 
/**
 
 * Map OTTD aircraft movement states to TTDPatch style movement states
 
 * (VarAction 2 Variable 0xE2)
 
 */
 
static byte MapAircraftMovementState(const Vehicle *v)
 
{
 
	const Station *st = GetStation(v->u.air.targetairport);
 
	byte amdflag = GetAirportMovingData(st->airport_type, v->u.air.pos)->flag;
 

	
 
	switch (v->u.air.state) {
 
		case HANGAR:
 
			/* The international airport is a special case as helicopters can land in
 
			 * front of the hanger. Helicopters also change their air.state to
 
			 * AMED_HELI_LOWER some time before actually descending. */
 

	
 
			/* This condition only occurs for helicopters, during descent,
 
			 * to a landing by the hanger of an international airport. */
 
			if (amdflag & AMED_HELI_LOWER) return AMS_TTDP_HELI_LAND_AIRPORT;
 

	
 
			/* This condition only occurs for helicopters, before starting descent,
 
			 * to a landing by the hanger of an international airport. */
 
			if (amdflag & AMED_SLOWTURN) return AMS_TTDP_FLIGHT_TO_TOWER;
 

	
 
			// The final two conditions apply to helicopters or aircraft.
 
			/* Has reached hanger? */
 
			if (amdflag & AMED_EXACTPOS) return AMS_TTDP_HANGAR;
 

	
 
			// Still moving towards hanger.
 
			return AMS_TTDP_TO_HANGAR;
 

	
 
		case TERM1:
 
			if (amdflag & AMED_EXACTPOS) return AMS_TTDP_TO_PAD1;
 
			return AMS_TTDP_TO_JUNCTION;
 

	
 
		case TERM2:
 
			if (amdflag & AMED_EXACTPOS) return AMS_TTDP_TO_PAD2;
 
			return AMS_TTDP_TO_ENTRY_2_AND_3_AND_H;
 

	
 
		case TERM3:
 
		case TERM4:
 
		case TERM5:
 
		case TERM6:
 
		case TERM7:
 
		case TERM8:
 
			/* TTDPatch only has 3 terminals, so treat these states the same */
 
			if (amdflag & AMED_EXACTPOS) return AMS_TTDP_TO_PAD3;
 
			return AMS_TTDP_TO_ENTRY_2_AND_3_AND_H;
 

	
 
		case HELIPAD1:
 
		case HELIPAD2:
 
		case HELIPAD3:
 
		case HELIPAD4: // Will only occur for helicopters.
 
			if (amdflag & AMED_HELI_LOWER) return AMS_TTDP_HELI_LAND_AIRPORT; // Descending.
 
			if (amdflag & AMED_SLOWTURN)   return AMS_TTDP_FLIGHT_TO_TOWER;   // Still hasn't started descent.
 
			return AMS_TTDP_TO_JUNCTION; // On the ground.
 

	
 
		case TAKEOFF: // Moving to takeoff position.
 
			return AMS_TTDP_TO_OUTWAY;
 

	
 
		case STARTTAKEOFF: // Accelerating down runway.
 
			return AMS_TTDP_TAKEOFF;
 

	
 
		case ENDTAKEOFF: // Ascent
 
			return AMS_TTDP_CLIMBING;
 

	
 
		case HELITAKEOFF: // Helicopter is moving to take off position.
 
			switch (st->airport_type) {
 
				case AT_SMALL:
 
				case AT_LARGE:
 
				case AT_METROPOLITAN:
 
				case AT_INTERNATIONAL:
 
				case AT_COMMUTER:
 
				case AT_INTERCON:
 
				/* Note, Helidepot and Helistation are treated as airports as
 
				 * helicopters are taking off from ground level. */
 
				case AT_HELIDEPOT:
 
				case AT_HELISTATION:
 
					if (amdflag & AMED_HELI_RAISE) return AMS_TTDP_HELI_TAKEOFF_AIRPORT;
 
					return AMS_TTDP_TO_JUNCTION;
 

	
 
				case AT_HELIPORT:
 
				case AT_OILRIG:
 
					return AMS_TTDP_HELI_TAKEOFF_HELIPORT;
 

	
 
				default:
 
					return AMS_TTDP_HELI_TAKEOFF_AIRPORT;
 
			}
 

	
 
		case FLYING:
 
			return AMS_TTDP_FLIGHT_TO_TOWER;
 

	
 
		case LANDING: // Descent
 
			return AMS_TTDP_FLIGHT_DESCENT;
 

	
 
		case ENDLANDING: // On the runway braking
 
			if (amdflag & AMED_BRAKE) return AMS_TTDP_BRAKING;
 
			// Landed - moving off runway
 
			return AMS_TTDP_TO_INWAY;
 

	
 
		case HELILANDING:
 
		case HELIENDLANDING: // Helicoptor is decending.
 
			if (amdflag & AMED_HELI_LOWER) {
 
				switch (st->airport_type) {
 
					case AT_HELIPORT:
 
					case AT_OILRIG:
 
						return AMS_TTDP_HELI_LAND_HELIPORT;
 

	
 
					default:
 
						/* Note, Helidepot and Helistation are treated as airports as
 
						 * helicopters are landing at ground level. */
 
						return AMS_TTDP_HELI_LAND_AIRPORT;
 
				}
 
			}
 
			return AMS_TTDP_FLIGHT_TO_TOWER;
 

	
 
		default:
 
			return AMS_TTDP_HANGAR;
 
	}
 
}
 

	
 

	
 
/* TTDP style aircraft movement action for GRF Action 2 Var 0xE6 */
 
enum {
 
	AMA_TTDP_IN_HANGAR,
 
	AMA_TTDP_ON_PAD1,
 
	AMA_TTDP_ON_PAD2,
 
	AMA_TTDP_ON_PAD3,
 
	AMA_TTDP_HANGAR_TO_PAD1,
 
	AMA_TTDP_HANGAR_TO_PAD2,
 
	AMA_TTDP_HANGAR_TO_PAD3,
 
	AMA_TTDP_LANDING_TO_PAD1,
 
	AMA_TTDP_LANDING_TO_PAD2,
 
	AMA_TTDP_LANDING_TO_PAD3,
 
	AMA_TTDP_PAD1_TO_HANGAR,
 
	AMA_TTDP_PAD2_TO_HANGAR,
 
	AMA_TTDP_PAD3_TO_HANGAR,
 
	AMA_TTDP_PAD1_TO_TAKEOFF,
 
	AMA_TTDP_PAD2_TO_TAKEOFF,
 
	AMA_TTDP_PAD3_TO_TAKEOFF,
 
	AMA_TTDP_HANGAR_TO_TAKOFF,
 
	AMA_TTDP_LANDING_TO_HANGAR,
 
	AMA_TTDP_IN_FLIGHT,
 
};
 

	
 

	
 
/**
 
 * Map OTTD aircraft movement states to TTDPatch style movement actions
 
 * (VarAction 2 Variable 0xE6)
 
 * This is not fully supported yet but it's enough for Planeset.
 
 */
 
static byte MapAircraftMovementAction(const Vehicle *v)
 
{
 
	switch (v->u.air.state) {
 
		case HANGAR:
 
			return (v->cur_speed > 0) ? AMA_TTDP_LANDING_TO_HANGAR : AMA_TTDP_IN_HANGAR;
 

	
 
		case TERM1:
 
		case HELIPAD1:
 
			return (v->current_order.type == OT_LOADING) ? AMA_TTDP_ON_PAD1 : AMA_TTDP_LANDING_TO_PAD1;
 

	
 
		case TERM2:
 
		case HELIPAD2:
 
			return (v->current_order.type == OT_LOADING) ? AMA_TTDP_ON_PAD2 : AMA_TTDP_LANDING_TO_PAD2;
 

	
 
		case TERM3:
 
		case TERM4:
 
		case TERM5:
 
		case TERM6:
 
		case TERM7:
 
		case TERM8:
 
		case HELIPAD3:
 
		case HELIPAD4:
 
			return (v->current_order.type == OT_LOADING) ? AMA_TTDP_ON_PAD3 : AMA_TTDP_LANDING_TO_PAD3;
 

	
 
		case TAKEOFF:      // Moving to takeoff position
 
		case STARTTAKEOFF: // Accelerating down runway
 
		case ENDTAKEOFF:   // Ascent
 
		case HELITAKEOFF:
 
			// TODO Need to find which terminal (or hanger) we've come from. How?
 
			return AMA_TTDP_PAD1_TO_TAKEOFF;
 

	
 
		case FLYING:
 
			return AMA_TTDP_IN_FLIGHT;
 

	
 
		case LANDING:    // Descent
 
		case ENDLANDING: // On the runway braking
 
		case HELILANDING:
 
		case HELIENDLANDING:
 
			// TODO Need to check terminal we're landing to. Is it known yet?
 
			return (v->current_order.type == OT_GOTO_DEPOT) ?
 
				AMA_TTDP_LANDING_TO_HANGAR : AMA_TTDP_LANDING_TO_PAD1;
 

	
 
		default:
 
			return AMA_TTDP_IN_HANGAR;
 
	}
 
}
 

	
 

	
 
/* TTDP airport types. Used to map our types to TTDPatch's */
 
enum {
 
	ATP_TTDP_SMALL,
 
	ATP_TTDP_LARGE,
 
	ATP_TTDP_HELIPORT,
 
	ATP_TTDP_OILRIG,
 
};
 

	
 

	
 
/* Vehicle Resolver Functions */
 
static inline const Vehicle *GRV(const ResolverObject *object)
 
{
 
	return object->scope == VSG_SCOPE_SELF ? object->u.vehicle.self : object->u.vehicle.parent;
 
}
 

	
 

	
 
static uint32 VehicleGetRandomBits(const ResolverObject *object)
 
{
 
	return GRV(object) == NULL ? 0 : GRV(object)->random_bits;
 
}
 

	
 

	
 
static uint32 VehicleGetTriggers(const ResolverObject *object)
 
{
 
	return GRV(object) == NULL ? 0 : GRV(object)->waiting_triggers;
 
}
 

	
 

	
 
static void VehicleSetTriggers(const ResolverObject *object, int triggers)
 
{
 
	/* Evil cast to get around const-ness. This used to be achieved by an
 
	 * innocent looking function pointer cast... Currently I cannot see a
 
	 * way of avoiding this without removing consts deep within gui code.
 
	 */
 
	Vehicle *v = (Vehicle*)GRV(object);
 

	
 
	/* This function must only be called when processing triggers -- any
 
	 * other time is an error. */
 
	assert(object->trigger != 0);
 

	
 
	if (v != NULL) v->waiting_triggers = triggers;
 
}
 

	
 

	
 
static uint32 GetVehicleTypeInfo(EngineID engine_type)
 
{
 
	/* Bit 0  Vehicle type is available on the market
 
	 * Bit 1  Vehicle type is in the testing phase
 
	 * Bit 2  Exclusive testing offer for a human player active */
 
	const Engine *e = GetEngine(engine_type);
 
	uint32 var = 0;
 

	
 
	if (e->player_avail == 0xFF) SETBIT(var, 0);
 
	if (e->age < e->duration_phase_1) SETBIT(var, 1);
 
	if (e->player_avail > 0 && e->player_avail != 0xFF) SETBIT(var, 2);
 
	return var;
 
}
 

	
 

	
 
static uint32 GetGRFParameter(EngineID engine_type, byte parameter)
 
{
 
	const GRFFile *file = GetEngineGRF(engine_type);
 

	
 
	if (parameter >= file->param_end) return 0;
 
	return file->param[parameter];
 
}
 

	
 

	
 
static uint32 VehicleGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
 
{
 
	const Vehicle *v = GRV(object);
 

	
 
	if (v == NULL) {
 
		/* Vehicle does not exist, so we're in a purchase list */
 
		switch (variable) {
 
			case 0x43: return _current_player; /* Owner information */
 
			case 0x46: return 0;               /* Motion counter */
 
			case 0x48: return GetVehicleTypeInfo(object->u.vehicle.self_type); /* Vehicle Type Info */
 
			case 0xC4: return clamp(_cur_year, ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR) - ORIGINAL_BASE_YEAR; /* Build year */
 
			case 0xDA: return INVALID_VEHICLE; /* Next vehicle */
 
			case 0x7F: return GetGRFParameter(object->u.vehicle.self_type, parameter); /* Read GRF parameter */
 
		}
 

	
 
		*available = false;
 
		return -1;
 
	}
 

	
 
	/* Calculated vehicle parameters */
 
	switch (variable) {
 
		case 0x40: /* Get length of consist */
 
		case 0x41: /* Get length of same consecutive wagons */
 
			if (v->type != VEH_Train) return 1;
 

	
 
			{
 
				const Vehicle* u;
 
				byte chain_before = 0;
 
				byte chain_after  = 0;
 

	
 
				for (u = GetFirstVehicleInChain(v); u != v; u = u->next) {
 
					chain_before++;
 
					if (variable == 0x41 && u->engine_type != v->engine_type) chain_before = 0;
 
				}
 

	
 
				while (u->next != NULL && (variable == 0x40 || u->next->engine_type == v->engine_type)) {
 
					chain_after++;
 
					u = u->next;
 
				}
 

	
 
				return chain_before | chain_after << 8 | (chain_before + chain_after + (variable == 0x41)) << 16;
 
			}
 

	
 
		case 0x42: { /* Consist cargo information */
 
			/* XXX Missing support for common refit cycle and property 25 */
 
			const Vehicle *u;
 
			byte cargo_classes = 0;
 
			uint common_cargo_best = 0;
 
			uint common_cargos[NUM_GLOBAL_CID];
 
			byte user_def_data = 0;
 
			CargoID cargo;
 
			CargoID common_cargo_type = GC_PASSENGERS;
 

	
 
			/* Reset our arrays */
 
			memset(common_cargos, 0, sizeof(common_cargos));
 

	
 
			for (u = v; u != NULL; u = u->next) {
 
				/* Skip empty engines */
 
				if (u->cargo_cap == 0) continue;
 
				/* Map from climate to global cargo ID */
 
				cargo = _global_cargo_id[_opt.landscape][u->cargo_type];
 
				cargo_classes |= _cargo_classes[cargo];
 
				common_cargos[cargo]++;
 
				user_def_data |= RailVehInfo(u->engine_type)->user_def_data;
 
			}
 

	
 
			/* Pick the most common cargo type */
 
			for (cargo = 0; cargo < NUM_GLOBAL_CID; cargo++) {
 
				if (common_cargos[cargo] > common_cargo_best) {
 
					common_cargo_best = common_cargos[cargo];
 
					common_cargo_type = cargo;
 
				}
 
			}
 

	
 
			return cargo_classes | (common_cargo_type << 8) | (user_def_data << 24);
 
		}
 

	
 
		case 0x43: /* Player information */
 
			return v->owner;
 

	
 
		case 0x44: /* Aircraft information */
 
			if (v->type != VEH_Aircraft) return -1;
 

	
 
			{
 
				const Vehicle *w = v->next;
 
				uint16 altitude = v->z_pos - w->z_pos; /* Aircraft height - shadow height */
 
				byte airporttype;
 

	
 
				switch (GetStation(v->u.air.targetairport)->airport_type) {
 
					/* Note, Helidepot and Helistation are treated as small airports
 
					 * as they are at ground level. */
 
					case AT_HELIDEPOT:
 
					case AT_HELISTATION:
 
					case AT_COMMUTER:
 
					case AT_SMALL:         airporttype = ATP_TTDP_SMALL; break;
 
					case AT_METROPOLITAN:
 
					case AT_INTERNATIONAL:
 
					case AT_INTERCON:
 
					case AT_LARGE:         airporttype = ATP_TTDP_LARGE; break;
 
					case AT_HELIPORT:      airporttype = ATP_TTDP_HELIPORT; break;
 
					case AT_OILRIG:        airporttype = ATP_TTDP_OILRIG; break;
 
					default:               airporttype = ATP_TTDP_LARGE; break;
 
				}
 

	
 
				return (altitude << 8) | airporttype;
 
			}
 

	
 
		case 0x46: /* Motion counter */
 
			return v->motion_counter;
 

	
 
		case 0x47: { /* Vehicle cargo info */
 
			/* Format: ccccwwtt
 
			 * tt - the cargo type transported by the vehicle,
 
			 *     translated if a translation table has been installed.
 
			 * ww - cargo unit weight in 1/16 tons, same as cargo prop. 0F.
 
			 * cccc - the cargo class value of the cargo transported by the vehicle.
 
			 */
 
			CargoID cid = _global_cargo_id[_opt.landscape][v->cargo_type];
 

	
 
			return (_cargo_classes[cid] << 16) | (_cargoc.weights[v->cargo_type] << 8) | cid;
 
		}
 

	
 
		case 0x48: return GetVehicleTypeInfo(v->engine_type); /* Vehicle Type Info */
 

	
 
		/* Variables which use the parameter */
 
		case 0x60: /* Count consist's engine ID occurance */
 
			if (v->type != VEH_Train) return v->engine_type == parameter;
 

	
 
			{
 
				uint count = 0;
 
				for (; v != NULL; v = v->next) {
 
					if (v->engine_type == parameter) count++;
 
				}
 
				return count;
 
			}
 

	
 
		case 0x7F: return GetGRFParameter(v->engine_type, parameter); /* Read GRF parameter */
 
	}
 

	
 
	/* General vehicle properties */
 
	switch (variable - 0x80) {
 
		case 0x00: return v->type;
 
		case 0x01: return MapOldSubType(v);
 
		case 0x04: return v->index;
 
		case 0x05: return GB(v->index, 8, 8);
 
		case 0x0A: return PackOrder(&v->current_order);
 
		case 0x0B: return GB(PackOrder(&v->current_order), 8, 8);
 
		case 0x0C: return v->num_orders;
 
		case 0x0D: return v->cur_order_index;
 
		case 0x10: return v->load_unload_time_rem;
 
		case 0x11: return GB(v->load_unload_time_rem, 8, 8);
 
		case 0x12: return max(v->date_of_last_service - DAYS_TILL_ORIGINAL_BASE_YEAR, 0);
 
		case 0x13: return GB(max(v->date_of_last_service - DAYS_TILL_ORIGINAL_BASE_YEAR, 0), 8, 8);
 
		case 0x14: return v->service_interval;
 
		case 0x15: return GB(v->service_interval, 8, 8);
 
		case 0x16: return v->last_station_visited;
 
		case 0x17: return v->tick_counter;
 
		case 0x18: return v->max_speed;
 
		case 0x19: return GB(v->max_speed, 8, 8);
 
		case 0x1A: return v->x_pos;
 
		case 0x1B: return GB(v->x_pos, 8, 8);
 
		case 0x1C: return v->y_pos;
 
		case 0x1D: return GB(v->y_pos, 8, 8);
 
		case 0x1E: return v->z_pos;
 
		case 0x1F: return object->info_view ? DIR_W : v->direction;
 
		case 0x28: return v->cur_image;
 
		case 0x29: return GB(v->cur_image, 8, 8);
 
		case 0x32: return v->vehstatus;
 
		case 0x33: return 0; // non-existent high byte of vehstatus
 
		case 0x34: return v->cur_speed;
 
		case 0x35: return GB(v->cur_speed, 8, 8);
 
		case 0x36: return v->subspeed;
 
		case 0x37: return v->acceleration;
 
		case 0x39: return v->cargo_type;
 
		case 0x3A: return v->cargo_cap;
 
		case 0x3B: return GB(v->cargo_cap, 8, 8);
 
		case 0x3C: return v->cargo_count;
 
		case 0x3D: return GB(v->cargo_count, 8, 8);
 
		case 0x3E: return v->cargo_source;
 
		case 0x3F: return v->cargo_days;
 
		case 0x40: return v->age;
 
		case 0x41: return GB(v->age, 8, 8);
 
		case 0x42: return v->max_age;
 
		case 0x43: return GB(v->max_age, 8, 8);
 
		case 0x44: return clamp(v->build_year, ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR) - ORIGINAL_BASE_YEAR;
 
		case 0x45: return v->unitnumber;
 
		case 0x46: return v->engine_type;
 
		case 0x47: return GB(v->engine_type, 8, 8);
 
		case 0x48: return v->spritenum;
 
		case 0x49: return v->day_counter;
 
		case 0x4A: return v->breakdowns_since_last_service;
 
		case 0x4B: return v->breakdown_ctr;
 
		case 0x4C: return v->breakdown_delay;
 
		case 0x4D: return v->breakdown_chance;
 
		case 0x4E: return v->reliability;
 
		case 0x4F: return GB(v->reliability, 8, 8);
 
		case 0x50: return v->reliability_spd_dec;
 
		case 0x51: return GB(v->reliability_spd_dec, 8, 8);
 
		case 0x52: return v->profit_this_year;
 
		case 0x53: return GB(v->profit_this_year,  8, 24);
 
		case 0x54: return GB(v->profit_this_year, 16, 16);
 
		case 0x55: return GB(v->profit_this_year, 24,  8);
 
		case 0x56: return v->profit_last_year;
 
		case 0x57: return GB(v->profit_last_year,  8, 24);
 
		case 0x58: return GB(v->profit_last_year, 16, 16);
 
		case 0x59: return GB(v->profit_last_year, 24,  8);
 
		case 0x5A: return v->next == NULL ? INVALID_VEHICLE : v->next->index;
 
		case 0x5C: return v->value;
 
		case 0x5D: return GB(v->value,  8, 24);
 
		case 0x5E: return GB(v->value, 16, 16);
 
		case 0x5F: return GB(v->value, 24,  8);
 
		case 0x60: return v->string_id;
 
		case 0x61: return GB(v->string_id, 8, 8);
 
		case 0x72: return v->cargo_subtype;
 
		case 0x7A: return v->random_bits;
 
		case 0x7B: return v->waiting_triggers;
 
	}
 

	
 
	/* Vehicle specific properties */
 
	switch (v->type) {
 
		case VEH_Train:
 
			switch (variable - 0x80) {
 
				case 0x62: return v->u.rail.track;
 
				case 0x66: return v->u.rail.railtype;
 
				case 0x73: return v->u.rail.cached_veh_length;
 
				case 0x74: return v->u.rail.cached_power;
 
				case 0x75: return GB(v->u.rail.cached_power,  8, 24);
 
				case 0x76: return GB(v->u.rail.cached_power, 16, 16);
 
				case 0x77: return GB(v->u.rail.cached_power, 24,  8);
 
				case 0x7C: return v->first->index;
 
				case 0x7D: return GB(v->first->index, 8, 8);
 
				case 0x7F: return 0; // Used for vehicle reversing hack in TTDP
 
			}
 
			break;
 

	
 
		case VEH_Road:
 
			switch (variable - 0x80) {
 
				case 0x62: return v->u.road.state;
 
				case 0x64: return v->u.road.blocked_ctr;
 
				case 0x65: return GB(v->u.road.blocked_ctr, 8, 8);
 
				case 0x66: return v->u.road.overtaking;
 
				case 0x67: return v->u.road.overtaking_ctr;
 
				case 0x68: return v->u.road.crashed_ctr;
 
				case 0x69: return GB(v->u.road.crashed_ctr, 8, 8);
 
			}
 
			break;
 

	
 
		case VEH_Aircraft:
 
			switch (variable - 0x80) {
 
				case 0x62: return MapAircraftMovementState(v);  // Current movement state
 
				case 0x63: return v->u.air.targetairport;       // Airport to which the action refers
 
				case 0x66: return MapAircraftMovementAction(v); // Current movement action
 
			}
 
			break;
 
	}
 

	
 
	DEBUG(grf, 1, "Unhandled vehicle property 0x%X, type 0x%X", variable, v->type);
 

	
 
	*available = false;
 
	return -1;
 
}
 

	
 

	
 
static const SpriteGroup *VehicleResolveReal(const ResolverObject *object, const SpriteGroup *group)
 
{
 
	const Vehicle *v = object->u.vehicle.self;
 
	uint totalsets;
 
	uint set;
 
	bool in_motion;
 

	
 
	if (v == NULL) return group->g.real.loading[0];
 

	
 
	if (v->type == VEH_Train) {
 
		in_motion = GetFirstVehicleInChain(v)->current_order.type != OT_LOADING;
 
	} else {
 
		in_motion = v->current_order.type != OT_LOADING;
 
	}
 

	
 
	totalsets = in_motion ? group->g.real.num_loaded : group->g.real.num_loading;
 

	
 
	if (v->cargo_count == v->cargo_cap || totalsets == 1) {
 
		set = totalsets - 1;
 
	} else if (v->cargo_count == 0 || totalsets == 2) {
 
		set = 0;
 
	} else {
 
		set = v->cargo_count * (totalsets - 2) / max(1, v->cargo_cap) + 1;
 
	}
 

	
 
	return in_motion ? group->g.real.loaded[set] : group->g.real.loading[set];
 
}
 

	
 

	
 
static inline void NewVehicleResolver(ResolverObject *res, EngineID engine_type, const Vehicle *v)
 
{
 
	res->GetRandomBits = &VehicleGetRandomBits;
 
	res->GetTriggers   = &VehicleGetTriggers;
 
	res->SetTriggers   = &VehicleSetTriggers;
 
	res->GetVariable   = &VehicleGetVariable;
 
	res->ResolveReal   = &VehicleResolveReal;
 

	
 
	res->u.vehicle.self   = v;
 
	res->u.vehicle.parent = (v != NULL && v->type == VEH_Train) ? GetFirstVehicleInChain(v) : v;
 

	
 
	res->u.vehicle.self_type = engine_type;
 

	
 
	res->info_view = false;
 

	
 
	res->callback        = 0;
 
	res->callback_param1 = 0;
 
	res->callback_param2 = 0;
 
	res->last_value      = 0;
 
	res->trigger         = 0;
 
	res->reseed          = 0;
 
}
 

	
 

	
 
/** Retrieve the SpriteGroup for the specified vehicle.
 
 * If the vehicle is not specified, the purchase list group for the engine is
 
 * chosen. For trains, an additional engine override lookup is performed.
 
 * @param engine Engine type of the vehicle.
 
 * @param v      The vehicle itself.
 
 * @returns      The selected SpriteGroup for the vehicle.
 
 */
 
static const SpriteGroup *GetVehicleSpriteGroup(EngineID engine, const Vehicle *v)
 
{
 
	const SpriteGroup *group;
 
	CargoID cargo;
 

	
 
	if (v == NULL) {
 
		cargo = GC_PURCHASE;
 
	} else {
 
		cargo = _global_cargo_id[_opt.landscape][v->cargo_type];
 
		assert(cargo != GC_INVALID);
 

	
 
		if (v->type == VEH_Train) {
 
			group = GetWagonOverrideSpriteSet(engine, cargo, v->u.rail.first_engine);
 

	
 
			if (group != NULL) return group;
 
		}
 
	}
 

	
 
	group = engine_custom_sprites[engine][cargo];
 
	if (group != NULL) return group;
 

	
 
	/* Fall back to the default set if the selected cargo type is not defined */
 
	return engine_custom_sprites[engine][GC_DEFAULT];
 
}
 

	
 

	
 
SpriteID GetCustomEngineSprite(EngineID engine, const Vehicle *v, Direction direction)
 
{
 
	const SpriteGroup *group;
 
	ResolverObject object;
 

	
 
	NewVehicleResolver(&object, engine, v);
 

	
 
	group = Resolve(GetVehicleSpriteGroup(engine, v), &object);
 
	if (group == NULL || group->type != SGT_RESULT) return 0;
 

	
 
	return group->g.result.sprite + (direction % group->g.result.num_sprites);
 
}
 

	
 

	
 
SpriteID GetRotorOverrideSprite(EngineID engine, const Vehicle *v, bool info_view)
 
{
 
	const SpriteGroup *group;
 
	ResolverObject object;
 

	
 
	assert(engine >= AIRCRAFT_ENGINES_INDEX);
 
	assert(engine < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES);
 

	
 
	/* Only valid for helicopters */
 
	assert(!(AircraftVehInfo(engine)->subtype & AIR_CTOL));
 

	
 
	NewVehicleResolver(&object, engine, v);
 

	
 
	object.info_view = info_view;
 

	
 
	group = heli_rotor_custom_sprites[engine - AIRCRAFT_ENGINES_INDEX];
 
	group = Resolve(group, &object);
 

	
 
	if (group == NULL || group->type != SGT_RESULT) return 0;
 

	
 
	if (v == NULL) return group->g.result.sprite;
 

	
 
	return group->g.result.sprite + (info_view ? 0 : (v->next->next->u.air.state % group->g.result.num_sprites));
 
}
 

	
 

	
 
/**
 
 * Check if a wagon is currently using a wagon override
 
 * @param v The wagon to check
 
 * @return true if it is using an override, false otherwise
 
 */
 
bool UsesWagonOverride(const Vehicle* v)
 
{
 
	assert(v->type == VEH_Train);
 
	return GetWagonOverrideSpriteSet(v->engine_type, _global_cargo_id[_opt.landscape][v->cargo_type], v->u.rail.first_engine) != NULL;
 
}
 

	
 
/**
 
 * Evaluate a newgrf callback for vehicles
 
 * @param callback The callback to evalute
 
 * @param param1   First parameter of the callback
 
 * @param param2   Second parameter of the callback
 
 * @param engine   Engine type of the vehicle to evaluate the callback for
 
 * @param vehicle  The vehicle to evaluate the callback for, or NULL if it doesnt exist yet
 
 * @return The value the callback returned, or CALLBACK_FAILED if it failed
 
 */
 
uint16 GetVehicleCallback(uint16 callback, uint32 param1, uint32 param2, EngineID engine, const Vehicle *v)
 
{
 
	const SpriteGroup *group;
 
	ResolverObject object;
 

	
 
	NewVehicleResolver(&object, engine, v);
 

	
 
	object.callback        = callback;
 
	object.callback_param1 = param1;
 
	object.callback_param2 = param2;
 

	
 
	group = Resolve(GetVehicleSpriteGroup(engine, v), &object);
 
	if (group == NULL || group->type != SGT_CALLBACK) return CALLBACK_FAILED;
 

	
 
	return group->g.callback.result;
 
}
 

	
 
/**
 
 * Evaluate a newgrf callback for vehicles with a different vehicle for parent scope.
 
 * @param callback The callback to evalute
 
 * @param param1   First parameter of the callback
 
 * @param param2   Second parameter of the callback
 
 * @param engine   Engine type of the vehicle to evaluate the callback for
 
 * @param v        The vehicle to evaluate the callback for, or NULL if it doesnt exist yet
 
 * @param parent   The vehicle to use for parent scope
 
 * @return The value the callback returned, or CALLBACK_FAILED if it failed
 
 */
 
uint16 GetVehicleCallbackParent(uint16 callback, uint32 param1, uint32 param2, EngineID engine, const Vehicle *v, const Vehicle *parent)
 
{
 
	const SpriteGroup *group;
 
	ResolverObject object;
 

	
 
	NewVehicleResolver(&object, engine, v);
 

	
 
	object.callback        = callback;
 
	object.callback_param1 = param1;
 
	object.callback_param2 = param2;
 

	
 
	object.u.vehicle.parent = parent;
 

	
 
	group = Resolve(GetVehicleSpriteGroup(engine, v), &object);
 
	if (group == NULL || group->type != SGT_CALLBACK) return CALLBACK_FAILED;
 

	
 
	return group->g.callback.result;
 
}
 

	
 
static void DoTriggerVehicle(Vehicle *v, VehicleTrigger trigger, byte base_random_bits, bool first)
 
{
 
	const SpriteGroup *group;
 
	ResolverObject object;
 
	byte new_random_bits;
 

	
 
	/* We can't trigger a non-existent vehicle... */
 
	assert(v != NULL);
 

	
 
	NewVehicleResolver(&object, v->engine_type, v);
 

	
 
	object.trigger = trigger;
 

	
 
	group = Resolve(GetVehicleSpriteGroup(v->engine_type, v), &object);
 

	
 
	new_random_bits = Random();
 
	v->random_bits &= ~object.reseed;
 
	v->random_bits |= (first ? new_random_bits : base_random_bits) & object.reseed;
 

	
 
	switch (trigger) {
 
		case VEHICLE_TRIGGER_NEW_CARGO:
 
			/* All vehicles in chain get ANY_NEW_CARGO trigger now.
 
			 * So we call it for the first one and they will recurse. */
 
			/* Indexing part of vehicle random bits needs to be
 
			 * same for all triggered vehicles in the chain (to get
 
			 * all the random-cargo wagons carry the same cargo,
 
			 * i.e.), so we give them all the NEW_CARGO triggered
 
			 * vehicle's portion of random bits. */
 
			assert(first);
 
			DoTriggerVehicle(GetFirstVehicleInChain(v), VEHICLE_TRIGGER_ANY_NEW_CARGO, new_random_bits, false);
 
			break;
 

	
 
		case VEHICLE_TRIGGER_DEPOT:
 
			/* We now trigger the next vehicle in chain recursively.
 
			 * The random bits portions may be different for each
 
			 * vehicle in chain. */
 
			if (v->next != NULL) DoTriggerVehicle(v->next, trigger, 0, true);
 
			break;
 

	
 
		case VEHICLE_TRIGGER_EMPTY:
 
			/* We now trigger the next vehicle in chain
 
			 * recursively.  The random bits portions must be same
 
			 * for each vehicle in chain, so we give them all
 
			 * first chained vehicle's portion of random bits. */
 
			if (v->next != NULL) DoTriggerVehicle(v->next, trigger, first ? new_random_bits : base_random_bits, false);
 
			break;
 

	
 
		case VEHICLE_TRIGGER_ANY_NEW_CARGO:
 
			/* Now pass the trigger recursively to the next vehicle
 
			 * in chain. */
 
			assert(!first);
 
			if (v->next != NULL) DoTriggerVehicle(v->next, VEHICLE_TRIGGER_ANY_NEW_CARGO, base_random_bits, false);
 
			break;
 
	}
 
}
 

	
 
void TriggerVehicle(Vehicle *v, VehicleTrigger trigger)
 
{
 
	if (trigger == VEHICLE_TRIGGER_DEPOT) {
 
		// store that the vehicle entered a depot this tick
 
		VehicleEnteredDepotThisTick(v);
 
	}
 

	
 
	DoTriggerVehicle(v, trigger, 0, true);
 
}
 

	
 
StringID _engine_custom_names[TOTAL_NUM_ENGINES];
 

	
 
void SetCustomEngineName(EngineID engine, StringID name)
 
{
 
	assert(engine < lengthof(_engine_custom_names));
 
	_engine_custom_names[engine] = name;
 
}
 

	
 
void UnloadCustomEngineNames(void)
 
{
 
	EngineID i;
 
	for (i = 0; i < TOTAL_NUM_ENGINES; i++) {
 
		_engine_custom_names[i] = 0;
 
	}
 
}
 

	
 
StringID GetCustomEngineName(EngineID engine)
 
{
 
	return _engine_custom_names[engine] == 0 ? _engine_name_strings[engine] : _engine_custom_names[engine];
 
}
 

	
 
// Functions for changing the order of vehicle purchase lists
 
// This is currently only implemented for rail vehicles.
 
static EngineID _engine_list_order[NUM_TRAIN_ENGINES];
 
static byte _engine_list_position[NUM_TRAIN_ENGINES];
 

	
 
void ResetEngineListOrder(void)
 
{
 
	EngineID i;
 

	
 
	for (i = 0; i < NUM_TRAIN_ENGINES; i++) {
 
		_engine_list_order[i] = i;
 
		_engine_list_position[i] = i;
 
	}
 
}
 

	
 
/**
 
 * Get the EngineID at position pos.
 
 * Used when drawing a(n unsorted) list of engines.
 
 * @param pos List position/
 
 * @return The EngineID at the requested position.
 
 */
 
EngineID GetRailVehAtPosition(EngineID pos)
 
{
 
	if (pos < NUM_TRAIN_ENGINES) return _engine_list_order[pos];
 
	return pos;
 
}
 

	
 
/**
 
 * Get the list position of an engine.
 
 * Used when sorting a list of engines.
 
 * @param engine ID of the engine.
 
 * @return The list position of the engine.
 
 */
 
uint16 ListPositionOfEngine(EngineID engine)
 
{
 
	if (engine < NUM_TRAIN_ENGINES) return _engine_list_position[engine];
 
	return engine;
 
}
 

	
 
void AlterRailVehListOrder(EngineID engine, EngineID target)
 
{
 
	EngineID i;
 
	bool moving = false;
 

	
 
	if (engine == target) return;
 

	
 
	// First, remove our ID from the list.
 
	for (i = 0; i < NUM_TRAIN_ENGINES - 1; i++) {
 
		if (_engine_list_order[i] == engine) moving = true;
 
		if (moving) _engine_list_order[i] = _engine_list_order[i + 1];
 
	}
 

	
 
	// Now, insert it again, before the target engine.
 
	for (i = NUM_TRAIN_ENGINES - 1; i > 0; i--) {
 
		_engine_list_order[i] = _engine_list_order[i - 1];
 
		if (_engine_list_order[i] == target) {
 
			_engine_list_order[i - 1] = engine;
 
			break;
 
		}
 
	}
 

	
 
	// Update the engine list position (a reverse of engine list order)
 
	for (i = 0; i < NUM_TRAIN_ENGINES; i++) {
 
		_engine_list_position[_engine_list_order[i]] = i;
 
	}
 
}
src/newgrf_gui.c
Show inline comments
 
deleted file
src/newgrf_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "variables.h"
 
#include "gfx.h"
 
#include "gui.h"
 
#include "window.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "newgrf.h"
 
#include "newgrf_config.h"
 

	
 

	
 
/** Parse an integerlist string and set each found value
 
 * @param p the string to be parsed. Each element in the list is seperated by a
 
 * comma or a space character
 
 * @param items pointer to the integerlist-array that will be filled with values
 
 * @param maxitems the maximum number of elements the integerlist-array has
 
 * @return returns the number of items found, or -1 on an error */
 
static int parse_intlist(const char *p, int *items, int maxitems)
 
{
 
	int n = 0, v;
 
	char *end;
 

	
 
	for (;;) {
 
		v = strtol(p, &end, 0);
 
		if (p == end || n == maxitems) return -1;
 
		p = end;
 
		items[n++] = v;
 
		if (*p == '\0') break;
 
		if (*p != ',' && *p != ' ') return -1;
 
		p++;
 
	}
 

	
 
	return n;
 
}
 

	
 

	
 
static void ShowNewGRFInfo(const GRFConfig *c, uint x, uint y, uint w, bool show_params)
 
{
 
	char buff[512];
 
	char *s;
 
	uint i;
 

	
 
	/* Draw filename or not if it is not known (GRF sent over internet) */
 
	if (c->filename != NULL) {
 
		SetDParamStr(0, c->filename);
 
		y += DrawStringMultiLine(x, y, STR_NEWGRF_FILENAME, w);
 
	}
 

	
 
	/* Prepare and draw GRF ID */
 
	snprintf(buff, lengthof(buff), "%08X", (uint32)BSWAP32(c->grfid));
 
	SetDParamStr(0, buff);
 
	y += DrawStringMultiLine(x, y, STR_NEWGRF_GRF_ID, w);
 

	
 
	/* Prepare and draw MD5 sum */
 
	s = buff;
 
	for (i = 0; i < lengthof(c->md5sum); i++) {
 
		s += snprintf(s, lastof(buff) - s, "%02X", c->md5sum[i]);
 
	}
 
	SetDParamStr(0, buff);
 
	y += DrawStringMultiLine(x, y, STR_NEWGRF_MD5SUM, w);
 

	
 
	/* Show GRF parameter list */
 
	if (show_params) {
 
		if (c->num_params > 0) {
 
			GRFBuildParamList(buff, c, lastof(buff));
 
			SetDParamStr(0, buff);
 
		} else {
 
			SetDParam(0, STR_01A9_NONE);
 
		}
 
		y += DrawStringMultiLine(x, y, STR_NEWGRF_PARAMETER, w);
 
	}
 

	
 
	/* Show flags */
 
	if (HASBIT(c->flags, GCF_NOT_FOUND)) y += DrawStringMultiLine(x, y, STR_NEWGRF_NOT_FOUND, w);
 
	if (HASBIT(c->flags, GCF_DISABLED))  y += DrawStringMultiLine(x, y, STR_NEWGRF_DISABLED, w);
 

	
 
	/* Draw GRF info if it exists */
 
	if (c->info != NULL && strlen(c->info) != 0) {
 
		SetDParamStr(0, c->info);
 
		y += DrawStringMultiLine(x, y, STR_02BD, w);
 
	} else {
 
		y += DrawStringMultiLine(x, y, STR_NEWGRF_NO_INFO, w);
 
	}
 
}
 

	
 

	
 
/* Dialogue for adding NewGRF files to the selection */
 
typedef struct newgrf_add_d {
 
	GRFConfig **list;
 
	const GRFConfig *sel;
 
} newgrf_add_d;
 

	
 

	
 
static void NewGRFAddDlgWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			const GRFConfig *c;
 
			int y;
 
			int n = 0;
 

	
 
			/* Count the number of GRFs */
 
			for (c = _all_grfs; c != NULL; c = c->next) n++;
 

	
 
			w->vscroll.cap = (w->widget[3].bottom - w->widget[3].top) / 10;
 
			SetVScrollCount(w, n);
 

	
 
			SetWindowWidgetDisabledState(w, 6, WP(w, newgrf_add_d).sel == NULL);
 
			DrawWindowWidgets(w);
 

	
 
			GfxFillRect(w->widget[3].left + 1, w->widget[3].top + 1, w->widget[3].right, w->widget[3].bottom, 0xD7);
 

	
 
			n = 0;
 
			y = w->widget[3].top + 1;
 

	
 
			for (c = _all_grfs; c != NULL; c = c->next) {
 
				if (n >= w->vscroll.pos && n < w->vscroll.pos + w->vscroll.cap) {
 
					bool h = c == WP(w, newgrf_add_d).sel;
 
					const char *text = (c->name != NULL && strlen(c->name) != 0) ? c->name : c->filename;
 

	
 
					/* Draw selection background */
 
					if (h) GfxFillRect(3, y, w->width - 15, y + 9, 156);
 
					DoDrawStringTruncated(text, 4, y, h ? 0xC : 0x6, w->width - 18);
 
					y += 10;
 
				}
 
				n++;
 
			}
 

	
 
			if (WP(w, newgrf_add_d).sel != NULL) {
 
				const Widget *wi = &w->widget[5];
 
				ShowNewGRFInfo(WP(w, newgrf_add_d).sel, wi->left + 2, wi->top + 2, wi->right - wi->left - 2, false);
 
			}
 
			break;
 
		}
 

	
 
		case WE_CLICK:
 
			switch (e->we.click.widget) {
 
				case 3: {
 
					// Get row...
 
					const GRFConfig *c;
 
					uint i = (e->we.click.pt.y - w->widget[3].top) / 10 + w->vscroll.pos;
 

	
 
					for (c = _all_grfs; c != NULL && i > 0; c = c->next, i--);
 
					WP(w, newgrf_add_d).sel = c;
 
					SetWindowDirty(w);
 
					break;
 
				}
 

	
 
				case 6: /* Add selection to list */
 
					if (WP(w, newgrf_add_d).sel != NULL) {
 
						const GRFConfig *src = WP(w, newgrf_add_d).sel;
 
						GRFConfig **list, *c;
 

	
 
						/* Find last entry in the list, checking for duplicate grfid on the way */
 
						for (list = WP(w, newgrf_add_d).list; *list != NULL; list = &(*list)->next) {
 
							if ((*list)->grfid == src->grfid) {
 
								ShowErrorMessage(INVALID_STRING_ID, STR_NEWGRF_DUPLICATE_GRFID, 0, 0);
 
								return;
 
							}
 
						}
 

	
 
						/* Copy GRF details from scanned list */
 
						c = calloc(1, sizeof(*c));
 
						*c = *src;
 
						c->filename = strdup(src->filename);
 
						if (src->name != NULL) c->name = strdup(src->name);
 
						if (src->info != NULL) c->info = strdup(src->info);
 
						c->next = NULL;
 

	
 
						/* Append GRF config to configuration list */
 
						*list = c;
 

	
 
						DeleteWindowByClass(WC_SAVELOAD);
 
						InvalidateWindowData(WC_GAME_OPTIONS, 0);
 
					}
 
					break;
 

	
 
				case 7: /* Rescan list */
 
					WP(w, newgrf_add_d).sel = NULL;
 
					ScanNewGRFFiles();
 
					SetWindowDirty(w);
 
					break;
 
			}
 
			break;
 
	}
 
}
 

	
 

	
 
static const Widget _newgrf_add_dlg_widgets[] = {
 
{   WWT_CLOSEBOX,    RESIZE_NONE, 14,   0,  10,   0,  13, STR_00C5,                STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION,   RESIZE_RIGHT, 14,  11, 306,   0,  13, STR_NEWGRF_ADD_CAPTION,  STR_018C_WINDOW_TITLE_DRAG_THIS },
 

	
 
/* List of files */
 
{      WWT_PANEL,      RESIZE_RB, 14,   0, 294,  14, 221, 0x0,                     STR_NULL },
 
{      WWT_INSET,      RESIZE_RB, 14,   2, 292,  16, 219, 0x0,                     STR_NULL },
 
{  WWT_SCROLLBAR,     RESIZE_LRB, 14, 295, 306,  14, 221, 0x0,                     STR_NULL },
 

	
 
/* NewGRF file info */
 
{      WWT_PANEL,     RESIZE_RTB, 14,   0, 306, 222, 324, 0x0,                     STR_NULL },
 

	
 
{ WWT_PUSHTXTBTN,     RESIZE_RTB, 14,   0, 146, 325, 336, STR_NEWGRF_ADD_FILE,     STR_NEWGRF_ADD_FILE_TIP },
 
{ WWT_PUSHTXTBTN,    RESIZE_LRTB, 14, 147, 294, 325, 336, STR_NEWGRF_RESCAN_FILES, STR_NEWGRF_RESCAN_FILES_TIP },
 
{  WWT_RESIZEBOX,    RESIZE_LRTB, 14, 295, 306, 325, 336, 0x0,                     STR_RESIZE_BUTTON },
 
{   WIDGETS_END },
 
};
 

	
 

	
 
static const WindowDesc _newgrf_add_dlg_desc = {
 
	WDP_CENTER, WDP_CENTER, 307, 337,
 
	WC_SAVELOAD, 0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_newgrf_add_dlg_widgets,
 
	NewGRFAddDlgWndProc,
 
};
 

	
 

	
 
/* 'NewGRF Settings' dialogue */
 
typedef struct newgrf_d {
 
	GRFConfig **orig_list; ///< grf list the window is shown with
 
	GRFConfig **list;      ///< temporary grf list to which changes are made
 
	GRFConfig *sel;        ///< selected grf item
 
	bool editable;         ///< is the window editable
 
	bool show_params;      ///< are the grf-parameters shown in the info-panel
 
	bool execute;          ///< on pressing 'apply changes' are grf changes applied immediately, or only list is updated
 
} newgrf_d;
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(newgrf_d));
 

	
 

	
 
enum ShowNewGRFStateWidgets {
 
	SNGRFS_ADD = 3,
 
	SNGRFS_REMOVE,
 
	SNGRFS_MOVE_UP,
 
	SNGRFS_MOVE_DOWN,
 
	SNGRFS_FILE_LIST = 7,
 
	SNGRFS_NEWGRF_INFO = 9,
 
	SNGRFS_SET_PARAMETERS,
 
	SNGRFS_APPLY_CHANGES,
 
};
 

	
 

	
 
static void SetupNewGRFState(Window *w)
 
{
 
	bool disable_all = WP(w, newgrf_d).sel == NULL || !WP(w, newgrf_d).editable;
 

	
 
	SetWindowWidgetDisabledState(w, 3, !WP(w, newgrf_d).editable);
 
	SetWindowWidgetsDisabledState(w, disable_all,
 
		SNGRFS_REMOVE,
 
		SNGRFS_MOVE_UP,
 
		SNGRFS_MOVE_DOWN,
 
		WIDGET_LIST_END
 
	);
 
	SetWindowWidgetDisabledState(w, SNGRFS_SET_PARAMETERS, !WP(w, newgrf_d).show_params || disable_all);
 

	
 
	if (!disable_all) {
 
		/* All widgets are now enabled, so disable widgets we can't use */
 
		if (WP(w, newgrf_d).sel == *WP(w, newgrf_d).list) DisableWindowWidget(w, SNGRFS_MOVE_UP);
 
		if (WP(w, newgrf_d).sel->next == NULL) DisableWindowWidget(w, SNGRFS_MOVE_DOWN);
 
	}
 
}
 

	
 

	
 
static void SetupNewGRFWindow(Window *w)
 
{
 
	const GRFConfig *c;
 
	int i;
 

	
 
	for (c = *WP(w, newgrf_d).list, i = 0; c != NULL; c = c->next, i++);
 

	
 
	w->vscroll.cap = (w->widget[SNGRFS_FILE_LIST].bottom - w->widget[SNGRFS_FILE_LIST].top) / 14 + 1;
 
	SetVScrollCount(w, i);
 
	SetWindowWidgetDisabledState(w, SNGRFS_APPLY_CHANGES, !WP(w, newgrf_d).editable);
 
}
 

	
 

	
 
/** Callback function for the newgrf 'apply changes' confirmation window
 
 * @param yes_clicked boolean value, true when yes was clicked, false otherwise */
 
static void NewGRFConfirmationCallback(Window *w, bool confirmed)
 
{
 
	if (confirmed) {
 
		newgrf_d *nd = &WP(w, newgrf_d);
 

	
 
		CopyGRFConfigList(nd->orig_list, *nd->list);
 
		ReloadNewGRFData();
 
	}
 
}
 

	
 

	
 
static void NewGRFWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			const GRFConfig *c;
 
			int i, y;
 

	
 
			SetupNewGRFState(w);
 

	
 
			DrawWindowWidgets(w);
 

	
 
			/* Draw NewGRF list */
 
			y = w->widget[SNGRFS_FILE_LIST].top;
 
			for (c = *WP(w, newgrf_d).list, i = 0; c != NULL; c = c->next, i++) {
 
				if (i >= w->vscroll.pos && i < w->vscroll.pos + w->vscroll.cap) {
 
					const char *text = (c->name != NULL && strlen(c->name) != 0) ? c->name : c->filename;
 
					PalSpriteID pal;
 

	
 
					/* Pick a colour */
 
					if (HASBIT(c->flags, GCF_NOT_FOUND) || HASBIT(c->flags, GCF_DISABLED)) {
 
						pal = PALETTE_TO_RED;
 
					} else if (HASBIT(c->flags, GCF_STATIC)) {
 
						pal = PALETTE_TO_YELLOW;
 
					} else if (HASBIT(c->flags, GCF_ACTIVATED)) {
 
						pal = PALETTE_TO_GREEN;
 
					} else {
 
						pal = PALETTE_TO_BLUE;
 
					}
 

	
 
					DrawSprite(SPRITE_PALETTE(SPR_SQUARE | pal), 5, y + 2);
 
					DoDrawString(text, 25, y + 3, WP(w, newgrf_d).sel == c ? 0xC : 0x10);
 
					y += 14;
 
				}
 
			}
 

	
 
			if (WP(w, newgrf_d).sel != NULL) {
 
				/* Draw NewGRF file info */
 
				const Widget *wi = &w->widget[SNGRFS_NEWGRF_INFO];
 
				ShowNewGRFInfo(WP(w, newgrf_d).sel, wi->left + 2, wi->top + 2, wi->right - wi->left - 2, WP(w, newgrf_d).show_params);
 
			}
 

	
 
			break;
 
		}
 

	
 
		case WE_INVALIDATE_DATA:
 
			SetupNewGRFWindow(w);
 
			break;
 

	
 
		case WE_CLICK:
 
			switch (e->we.click.widget) {
 
				case SNGRFS_ADD: { /* Add GRF */
 
					GRFConfig **list = WP(w, newgrf_d).list;
 
					Window *w;
 

	
 
					DeleteWindowByClass(WC_SAVELOAD);
 
					w = AllocateWindowDesc(&_newgrf_add_dlg_desc);
 
					w->resize.step_height = 10;
 

	
 
					WP(w, newgrf_add_d).list = list;
 
					break;
 
				}
 

	
 
				case SNGRFS_REMOVE: { /* Remove GRF */
 
					GRFConfig **pc, *c, *newsel;
 

	
 
					/* Choose the next GRF file to be the selected file */
 
					newsel = WP(w, newgrf_d).sel->next;
 

	
 
					for (pc = WP(w, newgrf_d).list; (c = *pc) != NULL; pc = &c->next) {
 
						/* If the new selection is empty (i.e. we're deleting the last item
 
						 * in the list, pick the file just before the selected file */
 
						if (newsel == NULL && c->next == WP(w, newgrf_d).sel) newsel = c;
 

	
 
						if (c == WP(w, newgrf_d).sel) {
 
							*pc = c->next;
 
							free(c);
 
							break;
 
						}
 
					}
 

	
 
					WP(w, newgrf_d).sel = newsel;
 
					SetupNewGRFWindow(w);
 
					SetWindowDirty(w);
 
					break;
 
				}
 

	
 
				case SNGRFS_MOVE_UP: { /* Move GRF up */
 
					GRFConfig **pc, *c;
 
					if (WP(w, newgrf_d).sel == NULL) break;
 

	
 
					for (pc = WP(w, newgrf_d).list; (c = *pc) != NULL; pc = &c->next) {
 
						if (c->next == WP(w, newgrf_d).sel) {
 
							c->next = WP(w, newgrf_d).sel->next;
 
							WP(w, newgrf_d).sel->next = c;
 
							*pc = WP(w, newgrf_d).sel;
 
							break;
 
						}
 
					}
 
					SetWindowDirty(w);
 
					break;
 
				}
 

	
 
				case SNGRFS_MOVE_DOWN: { /* Move GRF down */
 
					GRFConfig **pc, *c;
 
					if (WP(w, newgrf_d).sel == NULL) break;
 

	
 
					for (pc = WP(w, newgrf_d).list; (c = *pc) != NULL; pc = &c->next) {
 
						if (c == WP(w, newgrf_d).sel) {
 
							*pc = c->next;
 
							c->next = c->next->next;
 
							(*pc)->next = c;
 
							break;
 
						}
 
					}
 
					SetWindowDirty(w);
 
					break;
 
				}
 

	
 
				case SNGRFS_FILE_LIST: { /* Select a GRF */
 
					GRFConfig *c;
 
					uint i = (e->we.click.pt.y - w->widget[SNGRFS_FILE_LIST].top) / 14 + w->vscroll.pos;
 

	
 
					for (c = *WP(w, newgrf_d).list; c != NULL && i > 0; c = c->next, i--);
 
					WP(w, newgrf_d).sel = c;
 

	
 
					SetWindowDirty(w);
 
					break;
 
				}
 

	
 
				case SNGRFS_APPLY_CHANGES: /* Apply changes made to GRF list */
 
					if (WP(w, newgrf_d).execute) {
 
						ShowQuery(
 
							STR_POPUP_CAUTION_CAPTION,
 
							STR_NEWGRF_CONFIRMATION_TEXT,
 
							w,
 
							NewGRFConfirmationCallback
 
						);
 
					} else {
 
						CopyGRFConfigList(WP(w, newgrf_d).orig_list, *WP(w, newgrf_d).list);
 
					}
 
					break;
 

	
 
				case SNGRFS_SET_PARAMETERS: { /* Edit parameters */
 
					char buff[512];
 
					if (WP(w, newgrf_d).sel == NULL) break;
 

	
 
					GRFBuildParamList(buff, WP(w, newgrf_d).sel, lastof(buff));
 
					ShowQueryString(BindCString(buff), STR_NEWGRF_PARAMETER_QUERY, 63, 250, w, CS_ALPHANUMERAL);
 
					break;
 
				}
 
			}
 
			break;
 

	
 
		case WE_ON_EDIT_TEXT:
 
			if (e->we.edittext.str != NULL) {
 
				/* Parse our new "int list" */
 
				GRFConfig *c = WP(w, newgrf_d).sel;
 
				c->num_params = parse_intlist(e->we.edittext.str, (int*)c->param, lengthof(c->param));
 

	
 
				/* parse_intlist returns -1 on error */
 
				if (c->num_params == (byte)-1) c->num_params = 0;
 
			}
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case WE_DESTROY:
 
			/* Remove the temporary copy of grf-list used in window */
 
			ClearGRFConfigList(WP(w, newgrf_d).list);
 
			break;
 

	
 
		case WE_RESIZE:
 
			w->vscroll.cap += e->we.sizing.diff.y / 14;
 
			w->widget[SNGRFS_FILE_LIST].data = (w->vscroll.cap << 8) + 1;
 
			break;
 
	}
 
}
 

	
 

	
 
static const Widget _newgrf_widgets[] = {
 
{   WWT_CLOSEBOX,  RESIZE_NONE, 10,   0,  10,   0,  13, STR_00C5,                    STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION, RESIZE_RIGHT, 10,  11, 299,   0,  13, STR_NEWGRF_SETTINGS_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS },
 

	
 
/* NewGRF file Add, Remove, Move up, Move down */
 
{      WWT_PANEL, RESIZE_RIGHT, 10,   0, 299,  14,  29, STR_NULL,                    STR_NULL },
 
{ WWT_PUSHTXTBTN,  RESIZE_NONE,  3,  10,  79,  16,  27, STR_NEWGRF_ADD,              STR_NEWGRF_ADD_TIP },
 
{ WWT_PUSHTXTBTN,  RESIZE_NONE,  3,  80, 149,  16,  27, STR_NEWGRF_REMOVE,           STR_NEWGRF_REMOVE_TIP },
 
{ WWT_PUSHTXTBTN,  RESIZE_NONE,  3, 150, 219,  16,  27, STR_NEWGRF_MOVEUP,           STR_NEWGRF_MOVEUP_TIP },
 
{ WWT_PUSHTXTBTN,  RESIZE_NONE,  3, 220, 289,  16,  27, STR_NEWGRF_MOVEDOWN,         STR_NEWGRF_MOVEDOWN_TIP },
 

	
 
/* NewGRF file list */
 
{     WWT_MATRIX,    RESIZE_RB, 10,   0, 287,  30,  99, 0x501,                       STR_NEWGRF_FILE_TIP },
 
{  WWT_SCROLLBAR,   RESIZE_LRB, 10, 288, 299,  30,  99, 0x0,                         STR_0190_SCROLL_BAR_SCROLLS_LIST },
 

	
 
/* NewGRF file info */
 
{      WWT_PANEL,   RESIZE_RTB, 10,   0, 299, 100, 212, STR_NULL,                    STR_NULL },
 

	
 
/* Edit parameter and apply changes button... */
 
{ WWT_PUSHTXTBTN,    RESIZE_TB, 10,   0, 143, 213, 224, STR_NEWGRF_SET_PARAMETERS,   STR_NULL },
 
{ WWT_PUSHTXTBTN,   RESIZE_RTB, 10, 144, 287, 213, 224, STR_NEWGRF_APPLY_CHANGES,    STR_NULL },
 

	
 
{  WWT_RESIZEBOX,  RESIZE_LRTB, 10, 288, 299, 213, 224, 0x0,                         STR_RESIZE_BUTTON },
 

	
 
{ WIDGETS_END },
 
};
 

	
 

	
 
static const WindowDesc _newgrf_desc = {
 
	WDP_CENTER, WDP_CENTER, 300, 225,
 
	WC_GAME_OPTIONS, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_newgrf_widgets,
 
	NewGRFWndProc,
 
};
 

	
 

	
 
/** Setup the NewGRF gui
 
 * @param editable allow the user to make changes to the grfconfig in the window
 
 * @param show_params show information about what parameters are set for the grf files
 
 * @param exec_changes if changes are made to the list (editable is true), apply these
 
 *        changes immediately or only update the list
 
 * @param config pointer to a linked-list of grfconfig's that will be shown */
 
void ShowNewGRFSettings(bool editable, bool show_params, bool exec_changes, GRFConfig **config)
 
{
 
	static GRFConfig *local = NULL;
 
	Window *w;
 

	
 
	DeleteWindowByClass(WC_GAME_OPTIONS);
 
	w = AllocateWindowDesc(&_newgrf_desc);
 
	if (w == NULL) return;
 

	
 
	w->resize.step_height = 14;
 
	CopyGRFConfigList(&local, *config);
 

	
 
	/* Clear selections */
 
	WP(w, newgrf_d).sel         = NULL;
 
	WP(w, newgrf_d).list        = &local;
 
	WP(w, newgrf_d).orig_list   = config;
 
	WP(w, newgrf_d).editable    = editable;
 
	WP(w, newgrf_d).execute     = exec_changes;
 
	WP(w, newgrf_d).show_params = show_params;
 

	
 
	SetupNewGRFWindow(w);
 
}
src/newgrf_sound.c
Show inline comments
 
deleted file
src/newgrf_sound.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "oldpool.h"
 
#include "sound.h"
 
#include "engine.h"
 
#include "vehicle.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_sound.h"
 

	
 
static uint _sound_count = 0;
 
STATIC_OLD_POOL(SoundInternal, FileEntry, 3, 1000, NULL, NULL)
 

	
 

	
 
/* Allocate a new FileEntry */
 
FileEntry *AllocateFileEntry(void)
 
{
 
	if (_sound_count == GetSoundInternalPoolSize()) {
 
		if (!AddBlockToPool(&_SoundInternal_pool)) return NULL;
 
	}
 

	
 
	return GetSoundInternal(_sound_count++);
 
}
 

	
 

	
 
void InitializeSoundPool(void)
 
{
 
	CleanPool(&_SoundInternal_pool);
 
	_sound_count = 0;
 

	
 
	/* Copy original sound data to the pool */
 
	SndCopyToPool();
 
}
 

	
 

	
 
FileEntry *GetSound(uint index)
 
{
 
	if (index >= _sound_count) return NULL;
 
	return GetSoundInternal(index);
 
}
 

	
 

	
 
uint GetNumSounds(void)
 
{
 
	return _sound_count;
 
}
 

	
 

	
 
bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event)
 
{
 
	const GRFFile *file = GetEngineGRF(v->engine_type);
 
	uint16 callback;
 

	
 
	/* If the engine has no GRF ID associated it can't ever play any new sounds */
 
	if (file == NULL) return false;
 

	
 
	/* Check that the vehicle type uses the sound effect callback */
 
	if (!HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_SOUND_EFFECT)) return false;
 

	
 
	callback = GetVehicleCallback(CBID_VEHICLE_SOUND_EFFECT, event, 0, v->engine_type, v);
 
	if (callback == CALLBACK_FAILED) return false;
 
	if (callback >= GetNumOriginalSounds()) callback += file->sound_offset - GetNumOriginalSounds();
 

	
 
	if (callback < GetNumSounds()) SndPlayVehicleFx(callback, v);
 
	return true;
 
}
src/newgrf_spritegroup.c
Show inline comments
 
deleted file
src/newgrf_spritegroup.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "variables.h"
 
#include "macros.h"
 
#include "oldpool.h"
 
#include "newgrf_spritegroup.h"
 
#include "date.h"
 

	
 
static void SpriteGroupPoolCleanBlock(uint start_item, uint end_item);
 

	
 
static uint _spritegroup_count = 0;
 
STATIC_OLD_POOL(SpriteGroup, SpriteGroup, 9, 250, NULL, SpriteGroupPoolCleanBlock)
 

	
 
static void DestroySpriteGroup(SpriteGroup *group)
 
{
 
	/* Free dynamically allocated memory */
 
	/* XXX Cast away the consts due to MSVC being buggy... */
 
	switch (group->type) {
 
		case SGT_REAL:
 
			free((SpriteGroup**)group->g.real.loaded);
 
			free((SpriteGroup**)group->g.real.loading);
 
			break;
 

	
 
		case SGT_DETERMINISTIC:
 
			free(group->g.determ.adjusts);
 
			free(group->g.determ.ranges);
 
			break;
 

	
 
		case SGT_RANDOMIZED:
 
			free((SpriteGroup**)group->g.random.groups);
 
			break;
 

	
 
		default:
 
			break;
 
	}
 
}
 

	
 
static void SpriteGroupPoolCleanBlock(uint start_item, uint end_item)
 
{
 
	uint i;
 

	
 
	for (i = start_item; i <= end_item; i++) {
 
		DestroySpriteGroup(GetSpriteGroup(i));
 
	}
 
}
 

	
 

	
 
/* Allocate a new SpriteGroup */
 
SpriteGroup *AllocateSpriteGroup(void)
 
{
 
	/* This is totally different to the other pool allocators, as we never remove an item from the pool. */
 
	if (_spritegroup_count == GetSpriteGroupPoolSize()) {
 
		if (!AddBlockToPool(&_SpriteGroup_pool)) return NULL;
 
	}
 

	
 
	return GetSpriteGroup(_spritegroup_count++);
 
}
 

	
 

	
 
void InitializeSpriteGroupPool(void)
 
{
 
	CleanPool(&_SpriteGroup_pool);
 

	
 
	_spritegroup_count = 0;
 
}
 

	
 

	
 
static inline uint32 GetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
 
{
 
	/* Return common variables */
 
	switch (variable) {
 
		case 0x00: return max(_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0);
 
		case 0x01: return clamp(_cur_year, ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR) - ORIGINAL_BASE_YEAR;
 
		case 0x02: return _cur_month;
 
		case 0x03: return _opt.landscape;
 
		case 0x09: return _date_fract;
 
		case 0x0A: return _tick_counter;
 
		case 0x0C: return object->callback;
 
		case 0x10: return object->callback_param1;
 
		case 0x11: return 0;
 
		case 0x18: return object->callback_param2;
 
		case 0x1A: return -1;
 
		case 0x1B: return GB(_display_opt, 0, 6);
 
		case 0x1C: return object->last_value;
 
		case 0x20: return _opt.landscape == LT_HILLY ? _opt.snow_line : 0xFF;
 

	
 
		/* Not a common variable, so evalute the feature specific variables */
 
		default: return object->GetVariable(object, variable, parameter, available);
 
	}
 
}
 

	
 

	
 
/* Evaluate an adjustment for a variable of the given size. This is a bit of
 
 * an unwieldy macro, but it saves triplicating the code. */
 
#define BUILD_EVAL_ADJUST(size, usize) \
 
static inline usize EvalAdjust_ ## size(const DeterministicSpriteGroupAdjust *adjust, usize last_value, int32 value) \
 
{ \
 
	value >>= adjust->shift_num; \
 
	value  &= adjust->and_mask; \
 
\
 
	if (adjust->type != DSGA_TYPE_NONE) value += (size)adjust->add_val; \
 
\
 
	switch (adjust->type) { \
 
		case DSGA_TYPE_DIV:  value /= (size)adjust->divmod_val; break; \
 
		case DSGA_TYPE_MOD:  value %= (usize)adjust->divmod_val; break; \
 
		case DSGA_TYPE_NONE: break; \
 
	} \
 
\
 
	/* Get our value to the correct range */ \
 
	value = (usize)value; \
 
\
 
	switch (adjust->operation) { \
 
		case DSGA_OP_ADD:  return last_value + value; \
 
		case DSGA_OP_SUB:  return last_value - value; \
 
		case DSGA_OP_SMIN: return min(last_value, value); \
 
		case DSGA_OP_SMAX: return max(last_value, value); \
 
		case DSGA_OP_UMIN: return min((usize)last_value, (usize)value); \
 
		case DSGA_OP_UMAX: return max((usize)last_value, (usize)value); \
 
		case DSGA_OP_SDIV: return last_value / value; \
 
		case DSGA_OP_SMOD: return last_value % value; \
 
		case DSGA_OP_UDIV: return (usize)last_value / (usize)value; \
 
		case DSGA_OP_UMOD: return (usize)last_value % (usize)value; \
 
		case DSGA_OP_MUL:  return last_value * value; \
 
		case DSGA_OP_AND:  return last_value & value; \
 
		case DSGA_OP_OR:   return last_value | value; \
 
		case DSGA_OP_XOR:  return last_value ^ value; \
 
		default:           return value; \
 
	} \
 
}
 

	
 

	
 
BUILD_EVAL_ADJUST(int8, uint8)
 
BUILD_EVAL_ADJUST(int16, uint16)
 
BUILD_EVAL_ADJUST(int32, uint32)
 

	
 

	
 
static inline const SpriteGroup *ResolveVariable(const SpriteGroup *group, ResolverObject *object)
 
{
 
	static SpriteGroup nvarzero;
 
	int32 last_value = object->last_value;
 
	int32 value = -1;
 
	uint i;
 

	
 
	object->scope = group->g.determ.var_scope;
 

	
 
	for (i = 0; i < group->g.determ.num_adjusts; i++) {
 
		DeterministicSpriteGroupAdjust *adjust = &group->g.determ.adjusts[i];
 

	
 
		/* Try to get the variable. We shall assume it is available, unless told otherwise. */
 
		bool available = true;
 
		value = GetVariable(object, adjust->variable, adjust->parameter, &available);
 

	
 
		if (!available) {
 
			/* Unsupported property: skip further processing and return either
 
			 * the group from the first range or the default group. */
 
			return Resolve(group->g.determ.num_ranges > 0 ? group->g.determ.ranges[0].group : group->g.determ.default_group, object);
 
		}
 

	
 
		switch (group->g.determ.size) {
 
			case DSG_SIZE_BYTE:  value = EvalAdjust_int8(adjust, last_value, value); break;
 
			case DSG_SIZE_WORD:  value = EvalAdjust_int16(adjust, last_value, value); break;
 
			case DSG_SIZE_DWORD: value = EvalAdjust_int32(adjust, last_value, value); break;
 
			default: NOT_REACHED(); break;
 
		}
 
		last_value = value;
 
	}
 

	
 
	if (group->g.determ.num_ranges == 0) {
 
		/* nvar == 0 is a special case -- we turn our value into a callback result */
 
		nvarzero.type = SGT_CALLBACK;
 
		nvarzero.g.callback.result = GB(value, 0, 15);
 
		return &nvarzero;
 
	}
 

	
 
	for (i = 0; i < group->g.determ.num_ranges; i++) {
 
		if (group->g.determ.ranges[i].low <= (uint32)value && (uint32)value <= group->g.determ.ranges[i].high) {
 
			return Resolve(group->g.determ.ranges[i].group, object);
 
		}
 
	}
 

	
 
	return Resolve(group->g.determ.default_group, object);
 
}
 

	
 

	
 
static inline const SpriteGroup *ResolveRandom(const SpriteGroup *group, ResolverObject *object)
 
{
 
	uint32 mask;
 
	byte index;
 

	
 
	object->scope = group->g.random.var_scope;
 

	
 
	if (object->trigger != 0) {
 
		/* Handle triggers */
 
		/* Magic code that may or may not do the right things... */
 
		byte waiting_triggers = object->GetTriggers(object);
 
		byte match = group->g.random.triggers & (waiting_triggers | object->trigger);
 
		bool res;
 

	
 
		res = (group->g.random.cmp_mode == RSG_CMP_ANY) ?
 
			(match != 0) : (match == group->g.random.triggers);
 

	
 
		if (res) {
 
			waiting_triggers &= ~match;
 
			object->reseed |= (group->g.random.num_groups - 1) << group->g.random.lowest_randbit;
 
		} else {
 
			waiting_triggers |= object->trigger;
 
		}
 

	
 
		object->SetTriggers(object, waiting_triggers);
 
	}
 

	
 
	mask  = (group->g.random.num_groups - 1) << group->g.random.lowest_randbit;
 
	index = (object->GetRandomBits(object) & mask) >> group->g.random.lowest_randbit;
 

	
 
	return Resolve(group->g.random.groups[index], object);
 
}
 

	
 

	
 
/* ResolverObject (re)entry point */
 
const SpriteGroup *Resolve(const SpriteGroup *group, ResolverObject *object)
 
{
 
	/* We're called even if there is no group, so quietly return nothing */
 
	if (group == NULL) return NULL;
 

	
 
	switch (group->type) {
 
		case SGT_REAL:          return object->ResolveReal(object, group);
 
		case SGT_DETERMINISTIC: return ResolveVariable(group, object);
 
		case SGT_RANDOMIZED:    return ResolveRandom(group, object);
 
		default:                return group;
 
	}
 
}
src/newgrf_station.c
Show inline comments
 
deleted file
src/newgrf_station.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/** @file newgrf_station.c Functions for dealing with station classes and custom stations. */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "variables.h"
 
#include "functions.h"
 
#include "debug.h"
 
#include "sprite.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "station.h"
 
#include "station_map.h"
 
#include "newgrf.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_station.h"
 
#include "newgrf_spritegroup.h"
 
#include "date.h"
 

	
 
static StationClass station_classes[STAT_CLASS_MAX];
 

	
 
enum {
 
	MAX_SPECLIST = 255,
 
};
 

	
 
/**
 
 * Reset station classes to their default state.
 
 * This includes initialising the Default and Waypoint classes with an empty
 
 * entry, for standard stations and waypoints.
 
 */
 
void ResetStationClasses(void)
 
{
 
	StationClassID i;
 
	for (i = 0; i < STAT_CLASS_MAX; i++) {
 
		station_classes[i].id = 0;
 
		station_classes[i].name = STR_EMPTY;
 
		station_classes[i].stations = 0;
 

	
 
		free(station_classes[i].spec);
 
		station_classes[i].spec = NULL;
 
	}
 

	
 
	// Set up initial data
 
	station_classes[0].id = 'DFLT';
 
	station_classes[0].name = STR_STAT_CLASS_DFLT;
 
	station_classes[0].stations = 1;
 
	station_classes[0].spec = malloc(sizeof(*station_classes[0].spec));
 
	station_classes[0].spec[0] = NULL;
 

	
 
	station_classes[1].id = 'WAYP';
 
	station_classes[1].name = STR_STAT_CLASS_WAYP;
 
	station_classes[1].stations = 1;
 
	station_classes[1].spec = malloc(sizeof(*station_classes[1].spec));
 
	station_classes[1].spec[0] = NULL;
 
}
 

	
 
/**
 
 * Allocate a station class for the given class id.
 
 * @param classid A 32 bit value identifying the class.
 
 * @return Index into station_classes of allocated class.
 
 */
 
StationClassID AllocateStationClass(uint32 class)
 
{
 
	StationClassID i;
 

	
 
	for (i = 0; i < STAT_CLASS_MAX; i++) {
 
		if (station_classes[i].id == class) {
 
			// ClassID is already allocated, so reuse it.
 
			return i;
 
		} else if (station_classes[i].id == 0) {
 
			// This class is empty, so allocate it to the ClassID.
 
			station_classes[i].id = class;
 
			return i;
 
		}
 
	}
 

	
 
	grfmsg(2, "StationClassAllocate: already allocated %d classes, using default", STAT_CLASS_MAX);
 
	return STAT_CLASS_DFLT;
 
}
 

	
 
/** Set the name of a custom station class */
 
void SetStationClassName(StationClassID sclass, StringID name)
 
{
 
	assert(sclass < STAT_CLASS_MAX);
 
	station_classes[sclass].name = name;
 
}
 

	
 
/** Retrieve the name of a custom station class */
 
StringID GetStationClassName(StationClassID sclass)
 
{
 
	assert(sclass < STAT_CLASS_MAX);
 
	return station_classes[sclass].name;
 
}
 

	
 
/** Build a list of station class name StringIDs to use in a dropdown list
 
 * @return Pointer to a (static) array of StringIDs
 
 */
 
StringID *BuildStationClassDropdown(void)
 
{
 
	/* Allow room for all station classes, plus a terminator entry */
 
	static StringID names[STAT_CLASS_MAX + 1];
 
	uint i;
 

	
 
	/* Add each name */
 
	for (i = 0; i < STAT_CLASS_MAX && station_classes[i].id != 0; i++) {
 
		names[i] = station_classes[i].name;
 
	}
 
	/* Terminate the list */
 
	names[i] = INVALID_STRING_ID;
 

	
 
	return names;
 
}
 

	
 
/**
 
 * Get the number of station classes in use.
 
 * @return Number of station classes.
 
 */
 
uint GetNumStationClasses(void)
 
{
 
	uint i;
 
	for (i = 0; i < STAT_CLASS_MAX && station_classes[i].id != 0; i++);
 
	return i;
 
}
 

	
 
/**
 
 * Return the number of stations for the given station class.
 
 * @param sclass Index of the station class.
 
 * @return Number of stations in the class.
 
 */
 
uint GetNumCustomStations(StationClassID sclass)
 
{
 
	assert(sclass < STAT_CLASS_MAX);
 
	return station_classes[sclass].stations;
 
}
 

	
 
/**
 
 * Tie a station spec to its station class.
 
 * @param spec The station spec.
 
 */
 
void SetCustomStationSpec(StationSpec *statspec)
 
{
 
	StationClass *station_class;
 
	int i;
 

	
 
	/* If the station has already been allocated, don't reallocate it. */
 
	if (statspec->allocated) return;
 

	
 
	assert(statspec->sclass < STAT_CLASS_MAX);
 
	station_class = &station_classes[statspec->sclass];
 

	
 
	i = station_class->stations++;
 
	station_class->spec = realloc(station_class->spec, station_class->stations * sizeof(*station_class->spec));
 

	
 
	station_class->spec[i] = statspec;
 
	statspec->allocated = true;
 
}
 

	
 
/**
 
 * Retrieve a station spec from a class.
 
 * @param sclass Index of the station class.
 
 * @param station The station index with the class.
 
 * @return The station spec.
 
 */
 
const StationSpec *GetCustomStationSpec(StationClassID sclass, uint station)
 
{
 
	assert(sclass < STAT_CLASS_MAX);
 
	if (station < station_classes[sclass].stations)
 
		return station_classes[sclass].spec[station];
 

	
 
	// If the custom station isn't defined any more, then the GRF file
 
	// probably was not loaded.
 
	return NULL;
 
}
 

	
 

	
 
const StationSpec *GetCustomStationSpecByGrf(uint32 grfid, byte localidx)
 
{
 
	StationClassID i;
 
	uint j;
 

	
 
	for (i = STAT_CLASS_DFLT; i < STAT_CLASS_MAX; i++) {
 
		for (j = 0; j < station_classes[i].stations; j++) {
 
			const StationSpec *statspec = station_classes[i].spec[j];
 
			if (statspec == NULL) continue;
 
			if (statspec->grfid == grfid && statspec->localidx == localidx) return statspec;
 
		}
 
	}
 

	
 
	return NULL;
 
}
 

	
 

	
 
/* Evaluate a tile's position within a station, and return the result a bitstuffed format.
 
 * if not centred: .TNLcCpP, if centred: .TNL..CP
 
 * T = Tile layout number (GetStationGfx), N = Number of platforms, L = Length of platforms
 
 * C = Current platform number from start, c = from end
 
 * P = Position along platform from start, p = from end
 
 * if centred, C/P start from the centre and c/p are not available.
 
 */
 
uint32 GetPlatformInfo(Axis axis, byte tile, int platforms, int length, int x, int y, bool centred)
 
{
 
	uint32 retval = 0;
 

	
 
	if (axis == AXIS_X) {
 
		intswap(platforms, length);
 
		intswap(x, y);
 
	}
 

	
 
	/* Limit our sizes to 4 bits */
 
	platforms = min(15, platforms);
 
	length    = min(15, length);
 
	x = min(15, x);
 
	y = min(15, y);
 
	if (centred) {
 
		x -= platforms / 2;
 
		y -= length / 2;
 
		SB(retval,  0, 4, y & 0xF);
 
		SB(retval,  4, 4, x & 0xF);
 
	} else {
 
		SB(retval,  0, 4, y);
 
		SB(retval,  4, 4, length - y - 1);
 
		SB(retval,  8, 4, x);
 
		SB(retval, 12, 4, platforms - x - 1);
 
	}
 
	SB(retval, 16, 4, length);
 
	SB(retval, 20, 4, platforms);
 
	SB(retval, 24, 4, tile);
 

	
 
	return retval;
 
}
 

	
 

	
 
/* Find the end of a railway station, from the tile, in the direction of delta.
 
 * If check_type is set, we stop if the custom station type changes.
 
 * If check_axis is set, we stop if the station direction changes.
 
 */
 
static TileIndex FindRailStationEnd(TileIndex tile, TileIndexDiff delta, bool check_type, bool check_axis)
 
{
 
	bool waypoint;
 
	byte orig_type = 0;
 
	Axis orig_axis = AXIS_X;
 

	
 
	waypoint = IsTileType(tile, MP_RAILWAY);
 

	
 
	if (waypoint) {
 
		if (check_axis) orig_axis = GetWaypointAxis(tile);
 
	} else {
 
		if (check_type) orig_type = GetCustomStationSpecIndex(tile);
 
		if (check_axis) orig_axis = GetRailStationAxis(tile);
 
	}
 

	
 
	while (true) {
 
		TileIndex new_tile = TILE_ADD(tile, delta);
 

	
 
		if (waypoint) {
 
			if (!IsTileType(new_tile, MP_RAILWAY)) break;
 
			if (!IsRailWaypoint(new_tile)) break;
 
			if (check_axis && GetWaypointAxis(new_tile) != orig_axis) break;
 
		} else {
 
			if (!IsRailwayStationTile(new_tile)) break;
 
			if (check_type && GetCustomStationSpecIndex(new_tile) != orig_type) break;
 
			if (check_axis && GetRailStationAxis(new_tile) != orig_axis) break;
 
		}
 

	
 
		tile = new_tile;
 
	}
 
	return tile;
 
}
 

	
 

	
 
static uint32 GetPlatformInfoHelper(TileIndex tile, bool check_type, bool check_axis, bool centred)
 
{
 
	int tx = TileX(tile);
 
	int ty = TileY(tile);
 
	int sx = TileX(FindRailStationEnd(tile, TileDiffXY(-1,  0), check_type, check_axis));
 
	int sy = TileY(FindRailStationEnd(tile, TileDiffXY( 0, -1), check_type, check_axis));
 
	int ex = TileX(FindRailStationEnd(tile, TileDiffXY( 1,  0), check_type, check_axis)) + 1;
 
	int ey = TileY(FindRailStationEnd(tile, TileDiffXY( 0,  1), check_type, check_axis)) + 1;
 
	Axis axis = IsTileType(tile, MP_RAILWAY) ? GetWaypointAxis(tile) : GetRailStationAxis(tile);
 

	
 
	tx -= sx; ex -= sx;
 
	ty -= sy; ey -= sy;
 

	
 
	return GetPlatformInfo(axis, IsTileType(tile, MP_RAILWAY) ? 2 : GetStationGfx(tile), ex, ey, tx, ty, centred);
 
}
 

	
 

	
 
static uint32 GetRailContinuationInfo(TileIndex tile)
 
{
 
	/* Tile offsets and exit dirs for X axis */
 
	static Direction x_dir[8] = { DIR_SW, DIR_NE, DIR_SE, DIR_NW, DIR_S, DIR_E, DIR_W, DIR_N };
 
	static DiagDirection x_exits[8] = { DIAGDIR_SW, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NE, DIAGDIR_SW, DIAGDIR_NE };
 

	
 
	/* Tile offsets and exit dirs for Y axis */
 
	static Direction y_dir[8] = { DIR_SE, DIR_NW, DIR_SW, DIR_NE, DIR_S, DIR_W, DIR_E, DIR_N };
 
	static DiagDirection y_exits[8] = { DIAGDIR_SE, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NW, DIAGDIR_SE, DIAGDIR_NW };
 

	
 
	Axis axis = IsTileType(tile, MP_RAILWAY) ? GetWaypointAxis(tile) : GetRailStationAxis(tile);
 

	
 
	/* Choose appropriate lookup table to use */
 
	Direction *dir = axis == AXIS_X ? x_dir : y_dir;
 
	DiagDirection *diagdir = axis == AXIS_X ? x_exits : y_exits;
 

	
 
	uint32 res = 0;
 
	uint i;
 

	
 
	for (i = 0; i < lengthof(x_dir); i++, dir++, diagdir++) {
 
		uint32 ts = GetTileTrackStatus(tile + TileOffsByDir(*dir), TRANSPORT_RAIL);
 
		if (ts != 0) {
 
			/* If there is any track on the tile, set the bit in the second byte */
 
			SETBIT(res, i + 8);
 

	
 
			/* If any track reaches our exit direction, set the bit in the lower byte */
 
			if (ts & DiagdirReachesTracks(*diagdir)) SETBIT(res, i);
 
		}
 
	}
 

	
 
	return res;
 
}
 

	
 

	
 
/* Station Resolver Functions */
 
static uint32 StationGetRandomBits(const ResolverObject *object)
 
{
 
	const Station *st = object->u.station.st;
 
	const TileIndex tile = object->u.station.tile;
 
	return (st == NULL ? 0 : st->random_bits) | (tile == INVALID_TILE ? 0 : GetStationTileRandomBits(tile) << 16);
 
}
 

	
 

	
 
static uint32 StationGetTriggers(const ResolverObject *object)
 
{
 
	const Station *st = object->u.station.st;
 
	return st == NULL ? 0 : st->waiting_triggers;
 
}
 

	
 

	
 
static void StationSetTriggers(const ResolverObject *object, int triggers)
 
{
 
	Station *st = (Station*)object->u.station.st;
 
	assert(st != NULL);
 
	st->waiting_triggers = triggers;
 
}
 

	
 

	
 
static uint32 StationGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
 
{
 
	const Station *st = object->u.station.st;
 
	TileIndex tile = object->u.station.tile;
 

	
 
	if (st == NULL) {
 
		/* Station does not exist, so we're in a purchase list */
 
		switch (variable) {
 
			case 0x40:
 
			case 0x41:
 
			case 0x46:
 
			case 0x47:
 
			case 0x49: return 0x2110000;       /* Platforms, tracks & position */
 
			case 0x42: return 0;               /* Rail type (XXX Get current type from GUI?) */
 
			case 0x43: return _current_player; /* Station owner */
 
			case 0x44: return 2;               /* PBS status */
 
			case 0xFA: return max(_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0); /* Build date */
 
		}
 

	
 
		*available = false;
 
		return -1;
 
	}
 

	
 
	switch (variable) {
 
		/* Calculated station variables */
 
		case 0x40: return GetPlatformInfoHelper(tile, false, false, false);
 
		case 0x41: return GetPlatformInfoHelper(tile, true,  false, false);
 
		case 0x42: /* Terrain and rail type */
 
			return ((_opt.landscape == LT_HILLY && GetTileZ(tile) > _opt.snow_line) ? 4 : 0) |
 
			       (GetRailType(tile) << 8);
 
		case 0x43: return st->owner; /* Station owner */
 
		case 0x44: return 2;         /* PBS status */
 
		case 0x45: return GetRailContinuationInfo(tile);
 
		case 0x46: return GetPlatformInfoHelper(tile, false, false, true);
 
		case 0x47: return GetPlatformInfoHelper(tile, true,  false, true);
 
		case 0x48: { /* Accepted cargo types */
 
			CargoID cargo_type;
 
			uint32 value = 0;
 

	
 
			for (cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
 
				if (HASBIT(st->goods[cargo_type].waiting_acceptance, 15)) SETBIT(value, cargo_type);
 
			}
 
			return value;
 
		}
 
		case 0x49: return GetPlatformInfoHelper(tile, false, true, false);
 

	
 
		/* Variables which use the parameter */
 
		case 0x60: return GB(st->goods[parameter].waiting_acceptance, 0, 12);
 
		case 0x61: return st->goods[parameter].days_since_pickup;
 
		case 0x62: return st->goods[parameter].rating;
 
		case 0x63: return st->goods[parameter].enroute_time;
 
		case 0x64: return st->goods[parameter].last_speed | (st->goods[parameter].last_age << 8);
 
		case 0x65: return GB(st->goods[parameter].waiting_acceptance, 12, 4);
 

	
 
		/* General station properties */
 
		case 0x82: return 50;
 
		case 0x84: return st->string_id;
 
		case 0x86: return 0;
 
		case 0x8A: return st->had_vehicle_of_type;
 
		case 0xF0: return st->facilities;
 
		case 0xF1: return st->airport_type;
 
		case 0xF2: return st->truck_stops->status;
 
		case 0xF3: return st->bus_stops->status;
 
		case 0xF6: return st->airport_flags;
 
		case 0xF7: return GB(st->airport_flags, 8, 8);
 
		case 0xFA: return max(st->build_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0);
 
	}
 

	
 
	/* Handle cargo variables (deprecated) */
 
	if (variable >= 0x8C && variable <= 0xEC) {
 
		const GoodsEntry *g = &st->goods[GB(variable - 0x8C, 3, 4)];
 
		switch (GB(variable - 0x8C, 0, 3)) {
 
			case 0: return g->waiting_acceptance;
 
			case 1: return GB(g->waiting_acceptance, 8, 8);
 
			case 2: return g->days_since_pickup;
 
			case 3: return g->rating;
 
			case 4: return g->enroute_from;
 
			case 5: return g->enroute_time;
 
			case 6: return g->last_speed;
 
			case 7: return g->last_age;
 
		}
 
	}
 

	
 
	DEBUG(grf, 1, "Unhandled station property 0x%X", variable);
 

	
 
	*available = false;
 
	return -1;
 
}
 

	
 

	
 
static const SpriteGroup *StationResolveReal(const ResolverObject *object, const SpriteGroup *group)
 
{
 
	const Station *st = object->u.station.st;
 
	const StationSpec *statspec = object->u.station.statspec;
 
	uint set;
 

	
 
	uint cargo = 0;
 
	CargoID cargo_type = object->u.station.cargo_type;
 

	
 
	if (st == NULL || statspec->sclass == STAT_CLASS_WAYP) {
 
		return group->g.real.loading[0];
 
	}
 

	
 
	switch (cargo_type) {
 
		case GC_INVALID:
 
		case GC_DEFAULT_NA:
 
		case GC_PURCHASE:
 
			cargo = 0;
 
			break;
 

	
 
		case GC_DEFAULT:
 
			for (cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
 
				cargo += GB(st->goods[cargo_type].waiting_acceptance, 0, 12);
 
			}
 
			break;
 

	
 
		default:
 
			cargo = GB(st->goods[_local_cargo_id_ctype[cargo_type]].waiting_acceptance, 0, 12);
 
			break;
 
	}
 

	
 
	if (HASBIT(statspec->flags, 1)) cargo /= (st->trainst_w + st->trainst_h);
 
	cargo = min(0xfff, cargo);
 

	
 
	if (cargo > statspec->cargo_threshold) {
 
		if (group->g.real.num_loading > 0) {
 
			set = ((cargo - statspec->cargo_threshold) * group->g.real.num_loading) / (4096 - statspec->cargo_threshold);
 
			return group->g.real.loading[set];
 
		}
 
	} else {
 
		if (group->g.real.num_loaded > 0) {
 
			set = (cargo * group->g.real.num_loaded) / (statspec->cargo_threshold + 1);
 
			return group->g.real.loaded[set];
 
		}
 
	}
 

	
 
	return group->g.real.loading[0];
 
}
 

	
 

	
 
static void NewStationResolver(ResolverObject *res, const StationSpec *statspec, const Station *st, TileIndex tile)
 
{
 
	res->GetRandomBits = StationGetRandomBits;
 
	res->GetTriggers   = StationGetTriggers;
 
	res->SetTriggers   = StationSetTriggers;
 
	res->GetVariable   = StationGetVariable;
 
	res->ResolveReal   = StationResolveReal;
 

	
 
	res->u.station.st       = st;
 
	res->u.station.statspec = statspec;
 
	res->u.station.tile     = tile;
 

	
 
	res->callback        = 0;
 
	res->callback_param1 = 0;
 
	res->callback_param2 = 0;
 
	res->last_value      = 0;
 
	res->trigger         = 0;
 
	res->reseed          = 0;
 
}
 

	
 
static const SpriteGroup *ResolveStation(const StationSpec *statspec, const Station *st, ResolverObject *object)
 
{
 
	const SpriteGroup *group;
 
	CargoID ctype = GC_DEFAULT_NA;
 

	
 
	if (st == NULL) {
 
		/* No station, so we are in a purchase list */
 
		ctype = GC_PURCHASE;
 
	} else {
 
		CargoID cargo;
 

	
 
		/* Pick the first cargo that we have waiting */
 
		for (cargo = 0; cargo < NUM_GLOBAL_CID; cargo++) {
 
			CargoID lcid = _local_cargo_id_ctype[cargo];
 
			if (lcid != CT_INVALID && statspec->spritegroup[cargo] != NULL && GB(st->goods[lcid].waiting_acceptance, 0, 12) != 0) {
 
				ctype = cargo;
 
				break;
 
			}
 
		}
 
	}
 

	
 
	group = statspec->spritegroup[ctype];
 
	if (group == NULL) {
 
		ctype = GC_DEFAULT;
 
		group = statspec->spritegroup[ctype];
 
	}
 

	
 
	if (group == NULL) return NULL;
 

	
 
	/* Remember the cargo type we've picked */
 
	object->u.station.cargo_type = ctype;
 

	
 
	return Resolve(group, object);
 
}
 

	
 
SpriteID GetCustomStationRelocation(const StationSpec *statspec, const Station *st, TileIndex tile)
 
{
 
	const SpriteGroup *group;
 
	ResolverObject object;
 

	
 
	NewStationResolver(&object, statspec, st, tile);
 

	
 
	group = ResolveStation(statspec, st, &object);
 
	if (group == NULL || group->type != SGT_RESULT) return 0;
 
	return group->g.result.sprite - 0x42D;
 
}
 

	
 

	
 
SpriteID GetCustomStationGroundRelocation(const StationSpec *statspec, const Station *st, TileIndex tile)
 
{
 
	const SpriteGroup *group;
 
	ResolverObject object;
 

	
 
	NewStationResolver(&object, statspec, st, tile);
 
	object.callback_param1 = 1; /* Indicate we are resolving the ground sprite */
 

	
 
	group = ResolveStation(statspec, st, &object);
 
	if (group == NULL || group->type != SGT_RESULT) return 0;
 
	return group->g.result.sprite - 0x42D;
 
}
 

	
 

	
 
uint16 GetStationCallback(uint16 callback, uint32 param1, uint32 param2, const StationSpec *statspec, const Station *st, TileIndex tile)
 
{
 
	const SpriteGroup *group;
 
	ResolverObject object;
 

	
 
	NewStationResolver(&object, statspec, st, tile);
 

	
 
	object.callback        = callback;
 
	object.callback_param1 = param1;
 
	object.callback_param2 = param2;
 

	
 
	group = ResolveStation(statspec, st, &object);
 
	if (group == NULL || group->type != SGT_CALLBACK) return CALLBACK_FAILED;
 
	return group->g.callback.result;
 
}
 

	
 

	
 
/**
 
 * Allocate a StationSpec to a Station. This is called once per build operation.
 
 * @param spec StationSpec to allocate.
 
 * @param st Station to allocate it to.
 
 * @param exec Whether to actually allocate the spec.
 
 * @return Index within the Station's spec list, or -1 if the allocation failed.
 
 */
 
int AllocateSpecToStation(const StationSpec *statspec, Station *st, bool exec)
 
{
 
	uint i;
 

	
 
	if (statspec == NULL) return 0;
 

	
 
	/* Check if this spec has already been allocated */
 
	for (i = 1; i < st->num_specs && i < MAX_SPECLIST; i++) {
 
		if (st->speclist[i].spec == statspec) return i;
 
	}
 

	
 
	for (i = 1; i < st->num_specs && i < MAX_SPECLIST; i++) {
 
		if (st->speclist[i].spec == NULL && st->speclist[i].grfid == 0) break;
 
	}
 

	
 
	if (i == MAX_SPECLIST) return -1;
 

	
 
	if (exec) {
 
		if (i >= st->num_specs) {
 
			st->num_specs = i + 1;
 
			st->speclist = realloc(st->speclist, st->num_specs * sizeof(*st->speclist));
 

	
 
			if (st->num_specs == 2) {
 
				/* Initial allocation */
 
				st->speclist[0].spec     = NULL;
 
				st->speclist[0].grfid    = 0;
 
				st->speclist[0].localidx = 0;
 
			}
 
		}
 

	
 
		st->speclist[i].spec     = statspec;
 
		st->speclist[i].grfid    = statspec->grfid;
 
		st->speclist[i].localidx = statspec->localidx;
 
	}
 

	
 
	return i;
 
}
 

	
 

	
 
/** Deallocate a StationSpec from a Station. Called when removing a single station tile.
 
 * @param st Station to work with.
 
 * @param specindex Index of the custom station within the Station's spec list.
 
 * @return Indicates whether the StationSpec was deallocated.
 
 */
 
void DeallocateSpecFromStation(Station* st, byte specindex)
 
{
 
	/* specindex of 0 (default) is never freeable */
 
	if (specindex == 0) return;
 

	
 
	/* Check all tiles over the station to check if the specindex is still in use */
 
	BEGIN_TILE_LOOP(tile, st->trainst_w, st->trainst_h, st->train_tile) {
 
		if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st->index && IsRailwayStation(tile) && GetCustomStationSpecIndex(tile) == specindex) {
 
			return;
 
		}
 
	} END_TILE_LOOP(tile, st->trainst_w, st->trainst_h, st->train_tile)
 

	
 
	/* This specindex is no longer in use, so deallocate it */
 
	st->speclist[specindex].spec     = NULL;
 
	st->speclist[specindex].grfid    = 0;
 
	st->speclist[specindex].localidx = 0;
 

	
 
	/* If this was the highest spec index, reallocate */
 
	if (specindex == st->num_specs - 1) {
 
		for (; st->speclist[st->num_specs - 1].grfid == 0 && st->num_specs > 1; st->num_specs--);
 

	
 
		if (st->num_specs > 1) {
 
			st->speclist = realloc(st->speclist, st->num_specs * sizeof(*st->speclist));
 
		} else {
 
			free(st->speclist);
 
			st->num_specs = 0;
 
			st->speclist  = NULL;
 
		}
 
	}
 
}
 

	
 
/** Draw representation of a station tile for GUI purposes.
 
 * @param x, y Position of image.
 
 * @param dir Direction.
 
 * @param railtype Rail type.
 
 * @param sclass, station Type of station.
 
 * @return True if the tile was drawn (allows for fallback to default graphic)
 
 */
 
bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID sclass, uint station)
 
{
 
	const StationSpec *statspec;
 
	const DrawTileSprites *sprites;
 
	const DrawTileSeqStruct *seq;
 
	const RailtypeInfo *rti = GetRailTypeInfo(railtype);
 
	SpriteID relocation;
 
	PalSpriteID image;
 
	PalSpriteID colourmod = SPRITE_PALETTE(PLAYER_SPRITE_COLOR(_local_player));
 
	uint tile = 2;
 

	
 
	statspec = GetCustomStationSpec(sclass, station);
 
	if (statspec == NULL) return false;
 

	
 
	relocation = GetCustomStationRelocation(statspec, NULL, INVALID_TILE);
 

	
 
	if (HASBIT(statspec->callbackmask, CBM_CUSTOM_LAYOUT)) {
 
		uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0x2110000, 0, statspec, NULL, INVALID_TILE);
 
		if (callback != CALLBACK_FAILED) tile = callback;
 
	}
 

	
 
	if (statspec->renderdata == NULL) {
 
		sprites = GetStationTileLayout(tile + axis);
 
	} else {
 
		sprites = &statspec->renderdata[(tile < statspec->tiles) ? tile + axis : axis];
 
	}
 

	
 
	image = sprites->ground_sprite;
 
	if (HASBIT(image, 31)) {
 
		CLRBIT(image, 31);
 
		image += GetCustomStationGroundRelocation(statspec, NULL, INVALID_TILE);
 
		image += rti->custom_ground_offset;
 
	} else {
 
		image += rti->total_offset;
 
	}
 

	
 
	if (image & PALETTE_MODIFIER_COLOR) image &= SPRITE_MASK;
 
	DrawSprite(image, x, y);
 

	
 
	foreach_draw_tile_seq(seq, sprites->seq) {
 
		Point pt;
 
		image = seq->image;
 
		if (HASBIT(image, 30)) {
 
			CLRBIT(image, 30);
 
			image += rti->total_offset;
 
		} else {
 
			image += relocation;
 
		}
 

	
 
		if ((byte)seq->delta_z != 0x80) {
 
			pt = RemapCoords(seq->delta_x, seq->delta_y, seq->delta_z);
 
			DrawSprite((image & SPRITE_MASK) | colourmod, x + pt.x, y + pt.y);
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 

	
 
static const StationSpec* GetStationSpec(TileIndex t)
 
{
 
	const Station* st;
 
	uint specindex;
 

	
 
	if (!IsCustomStationSpecIndex(t)) return NULL;
 

	
 
	st = GetStationByTile(t);
 
	specindex = GetCustomStationSpecIndex(t);
 
	return specindex < st->num_specs ? st->speclist[specindex].spec : NULL;
 
}
 

	
 

	
 
/* Check if a rail station tile is traversable.
 
 * XXX This could be cached (during build) in the map array to save on all the dereferencing */
 
bool IsStationTileBlocked(TileIndex tile)
 
{
 
	const StationSpec* statspec = GetStationSpec(tile);
 

	
 
	return statspec != NULL && HASBIT(statspec->blocked, GetStationGfx(tile));
 
}
 

	
 
/* Check if a rail station tile is electrifiable.
 
 * XXX This could be cached (during build) in the map array to save on all the dereferencing */
 
bool IsStationTileElectrifiable(TileIndex tile)
 
{
 
	const StationSpec* statspec = GetStationSpec(tile);
 

	
 
	return
 
		statspec == NULL ||
 
		HASBIT(statspec->pylons, GetStationGfx(tile)) ||
 
		!HASBIT(statspec->wires, GetStationGfx(tile));
 
}
src/newgrf_text.c
Show inline comments
 
deleted file
src/newgrf_text.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/** @file
 
 * Implementation of  Action 04 "universal holder" structure and functions.
 
 * This file implements a linked-lists of strings,
 
 * holding everything that the newgrf action 04 will send over to OpenTTD.
 
 * One of the biggest problems is that Dynamic lang Array uses ISO codes
 
 * as way to identifying current user lang, while newgrf uses bit shift codes
 
 * not related to ISO.  So equivalence functionnality had to be set.
 
 */
 

	
 
#include "stdafx.h"
 
#include "debug.h"
 
#include "openttd.h"
 
#include "string.h"
 
#include "strings.h"
 
#include "variables.h"
 
#include "macros.h"
 
#include "table/strings.h"
 
#include "newgrf.h"
 
#include "newgrf_text.h"
 
#include "table/control_codes.h"
 

	
 
#define GRFTAB  28
 
#define TABSIZE 11
 

	
 
/**
 
 * Explains the newgrf shift bit positionning.
 
 * the grf base will not be used in order to find the string, but rather for
 
 * jumping from standard langID scheme to the new one.
 
 */
 
typedef enum grf_base_languages {
 
	GRFLB_AMERICAN    = 0x01,
 
	GRFLB_ENGLISH     = 0x02,
 
	GRFLB_GERMAN      = 0x04,
 
	GRFLB_FRENCH      = 0x08,
 
	GRFLB_SPANISH     = 0x10,
 
	GRFLB_GENERIC     = 0x80,
 
} grf_base_language;
 

	
 
typedef enum grf_extended_languages {
 
	GRFLX_AMERICAN    = 0x00,
 
	GRFLX_ENGLISH     = 0x01,
 
	GRFLX_GERMAN      = 0x02,
 
	GRFLX_FRENCH      = 0x03,
 
	GRFLX_SPANISH     = 0x04,
 
	GRFLX_RUSSIAN     = 0x07,
 
	GRFLX_CZECH       = 0x15,
 
	GRFLX_SLOVAK      = 0x16,
 
	GRFLX_AFRIKAANS   = 0x1B,
 
	GRFLX_GREEK       = 0x1E,
 
	GRFLX_DUTCH       = 0x1F,
 
	GRFLX_CATALAN     = 0x22,
 
	GRFLX_HUNGARIAN   = 0x24,
 
	GRFLX_ITALIAN     = 0x27,
 
	GRFLX_ROMANIAN    = 0x28,
 
	GRFLX_ICELANDIC   = 0x29,
 
	GRFLX_LATVIAN     = 0x2A,
 
	GRFLX_LITHUANIAN  = 0x2B,
 
	GRFLX_SLOVENIAN   = 0x2C,
 
	GRFLX_DANISH      = 0x2D,
 
	GRFLX_SWEDISH     = 0x2E,
 
	GRFLX_NORWEGIAN   = 0x2F,
 
	GRFLX_POLISH      = 0x30,
 
	GRFLX_GALICIAN    = 0x31,
 
	GRFLX_FRISIAN     = 0x32,
 
	GRFLX_UKRAINIAN   = 0x33,
 
	GRFLX_ESTONIAN    = 0x34,
 
	GRFLX_FINNISH     = 0x35,
 
	GRFLX_PORTUGUESE  = 0x36,
 
	GRFLX_BRAZILIAN   = 0x37,
 
	GRFLX_CROATIAN    = 0x38,
 
	GRFLX_TURKISH     = 0x3E,
 
	GRFLX_UNSPECIFIED = 0x7F,
 
} grf_language;
 

	
 

	
 
typedef struct iso_grf {
 
	char code[6];
 
	byte grfLangID;
 
} iso_grf;
 

	
 
/**
 
 * ISO code VS NewGrf langID conversion array.
 
 * This array is used in two ways:
 
 * 1-its ISO part is matching OpenTTD dynamic language id
 
 *   with newgrf bit positionning language id
 
 * 2-its shift part is used to know what is the shift to
 
 *   watch for when inserting new strings, hence analysing newgrf langid
 
 */
 
const iso_grf iso_codes[] = {
 
	{"en_US", GRFLX_AMERICAN},
 
	{"en_GB", GRFLX_ENGLISH},
 
	{"de_DE", GRFLX_GERMAN},
 
	{"fr_FR", GRFLX_FRENCH},
 
	{"es_ES", GRFLX_SPANISH},
 
	{"af_ZA", GRFLX_AFRIKAANS},
 
	{"hr_HR", GRFLX_CROATIAN},
 
	{"cs_CS", GRFLX_CZECH},
 
	{"ca_ES", GRFLX_CATALAN},
 
	{"da_DA", GRFLX_DANISH},
 
	{"nl_NL", GRFLX_DUTCH},
 
	{"et_ET", GRFLX_ESTONIAN},
 
	{"fi_FI", GRFLX_FINNISH},
 
	{"fy_NL", GRFLX_FRISIAN},
 
	{"gl_ES", GRFLX_GALICIAN},
 
	{"el_GR", GRFLX_GREEK},
 
	{"hu_HU", GRFLX_HUNGARIAN},
 
	{"is_IS", GRFLX_ICELANDIC},
 
	{"it_IT", GRFLX_ITALIAN},
 
	{"lv_LV", GRFLX_LATVIAN},
 
	{"lt_LT", GRFLX_LITHUANIAN},
 
	{"nb_NO", GRFLX_NORWEGIAN},
 
	{"pl_PL", GRFLX_POLISH},
 
	{"pt_PT", GRFLX_PORTUGUESE},
 
	{"pt_BR", GRFLX_BRAZILIAN},
 
	{"ro_RO", GRFLX_ROMANIAN},
 
	{"ru_RU", GRFLX_RUSSIAN},
 
	{"sk_SK", GRFLX_SLOVAK},
 
	{"sl_SL", GRFLX_SLOVENIAN},
 
	{"sv_SE", GRFLX_SWEDISH},
 
	{"tr_TR", GRFLX_TURKISH},
 
	{"uk_UA", GRFLX_UKRAINIAN},
 
	{"gen",   GRFLB_GENERIC}   //this is not iso code, but there has to be something...
 
};
 

	
 

	
 
/**
 
 * Element of the linked list.
 
 * Each of those elements represent the string,
 
 * but according to a different lang.
 
 */
 
typedef struct GRFText {
 
	struct GRFText *next;
 
	byte langid;
 
	char text[VARARRAY_SIZE];
 
} GRFText;
 

	
 

	
 
/**
 
 * Holder of the above structure.
 
 * Putting both grfid and stringid together allows us to avoid duplicates,
 
 * since it is NOT SUPPOSED to happen.
 
 */
 
typedef struct GRFTextEntry {
 
	uint32 grfid;
 
	uint16 stringid;
 
	StringID def_string;
 
	GRFText *textholder;
 
} GRFTextEntry;
 

	
 

	
 
static uint _num_grf_texts = 0;
 
static GRFTextEntry _grf_text[(1 << TABSIZE) * 3];
 
static byte _currentLangID = GRFLX_ENGLISH;  //by default, english is used.
 

	
 

	
 
char *TranslateTTDPatchCodes(const char *str)
 
{
 
	char *tmp = malloc(strlen(str) * 10 + 1); /* Allocate space to allow for expansion */
 
	char *d = tmp;
 
	bool unicode = false;
 
	WChar c;
 
	size_t len = Utf8Decode(&c, str);
 

	
 
	if (c == 0x00DE) {
 
		/* The thorn ('þ') indicates a unicode string to TTDPatch */
 
		unicode = true;
 
		str += len;
 
	}
 

	
 
	for (;;) {
 
		const char *tmp = str; /* Used for UTF-8 decoding */
 

	
 
		c = (byte)*str++;
 
		if (c == 0) break;
 

	
 
		switch (c) {
 
			case 0x01:
 
				d += Utf8Encode(d, SCC_SETX);
 
				*d++ = *str++;
 
				break;
 
			case 0x0A: break;
 
			case 0x0D: *d++ = 0x0A; break;
 
			case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break;
 
			case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break;
 
			case 0x1F:
 
				d += Utf8Encode(d, SCC_SETXY);
 
				*d++ = *str++;
 
				*d++ = *str++;
 
				break;
 
			case 0x7B:
 
			case 0x7C:
 
			case 0x7D:
 
			case 0x7E: d += Utf8Encode(d, SCC_NUM); break;
 
			case 0x7F: d += Utf8Encode(d, SCC_CURRENCY); break;
 
			case 0x80: d += Utf8Encode(d, SCC_STRING); break;
 
			case 0x81: {
 
				StringID string;
 
				string  = *str++;
 
				string |= *str++ << 8;
 
				d += Utf8Encode(d, SCC_STRING_ID);
 
				d += Utf8Encode(d, string);
 
				break;
 
			}
 
			case 0x82: d += Utf8Encode(d, SCC_DATE_TINY); break;
 
			case 0x83: d += Utf8Encode(d, SCC_DATE_SHORT); break;
 
			case 0x84: d += Utf8Encode(d, SCC_VELOCITY); break;
 
			case 0x85: d += Utf8Encode(d, SCC_SKIP);    break;
 
			case 0x86: /* "Rotate down top 4 words on stack" */ break;
 
			case 0x87: d += Utf8Encode(d, SCC_VOLUME);  break;
 
			case 0x88: d += Utf8Encode(d, SCC_BLUE);    break;
 
			case 0x89: d += Utf8Encode(d, SCC_SILVER);  break;
 
			case 0x8A: d += Utf8Encode(d, SCC_GOLD);    break;
 
			case 0x8B: d += Utf8Encode(d, SCC_RED);     break;
 
			case 0x8C: d += Utf8Encode(d, SCC_PURPLE);  break;
 
			case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break;
 
			case 0x8E: d += Utf8Encode(d, SCC_ORANGE);  break;
 
			case 0x8F: d += Utf8Encode(d, SCC_GREEN);   break;
 
			case 0x90: d += Utf8Encode(d, SCC_YELLOW);  break;
 
			case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break;
 
			case 0x92: d += Utf8Encode(d, SCC_CREAM);   break;
 
			case 0x93: d += Utf8Encode(d, SCC_BROWN);   break;
 
			case 0x94: d += Utf8Encode(d, SCC_WHITE);   break;
 
			case 0x95: d += Utf8Encode(d, SCC_LTBLUE);  break;
 
			case 0x96: d += Utf8Encode(d, SCC_GRAY);    break;
 
			case 0x97: d += Utf8Encode(d, SCC_DKBLUE);  break;
 
			case 0x98: d += Utf8Encode(d, SCC_BLACK);   break;
 
			case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro
 
			case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis
 
			case 0xA0: d += Utf8Encode(d, SCC_UPARROW); break;
 
			case 0xAA: d += Utf8Encode(d, SCC_DOWNARROW); break;
 
			case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK); break;
 
			case 0xAD: d += Utf8Encode(d, SCC_CROSS); break;
 
			case 0xAF: d += Utf8Encode(d, SCC_RIGHTARROW); break;
 
			case 0xB4: d += Utf8Encode(d, SCC_TRAIN); break;
 
			case 0xB5: d += Utf8Encode(d, SCC_LORRY); break;
 
			case 0xB6: d += Utf8Encode(d, SCC_BUS); break;
 
			case 0xB7: d += Utf8Encode(d, SCC_PLANE); break;
 
			case 0xB8: d += Utf8Encode(d, SCC_SHIP); break;
 
			default:
 
				if (unicode) {
 
					d += Utf8Encode(d, Utf8Consume(&tmp));
 
					str = tmp;
 
					break;
 
				}
 

	
 
				/* Validate any unhandled character */
 
				if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
 
				d += Utf8Encode(d, c);
 
				break;
 
		}
 
	}
 

	
 
	*d = '\0';
 
	return realloc(tmp, strlen(tmp) + 1);
 
}
 

	
 

	
 
/**
 
 * Add the new read string into our structure.
 
 */
 
StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool new_scheme, const char *text_to_add, StringID def_string)
 
{
 
	char *translatedtext;
 
	GRFText *newtext;
 
	uint id;
 

	
 
	/* When working with the old language scheme (grf_version is less than 7) and
 
	 * English or American is among the set bits, simply add it as English in
 
	 * the new scheme, i.e. as langid = 1.
 
	 * If English is set, it is pretty safe to assume the translations are not
 
	 * actually translated.
 
	 */
 
	if (!new_scheme) {
 
		if (HASBITS(langid_to_add, GRFLB_AMERICAN | GRFLB_ENGLISH)) {
 
			langid_to_add = GRFLX_ENGLISH;
 
		} else {
 
			StringID ret = STR_EMPTY;
 
			if (langid_to_add & GRFLB_GERMAN)  ret = AddGRFString(grfid, stringid, GRFLX_GERMAN,  true, text_to_add, def_string);
 
			if (langid_to_add & GRFLB_FRENCH)  ret = AddGRFString(grfid, stringid, GRFLX_FRENCH,  true, text_to_add, def_string);
 
			if (langid_to_add & GRFLB_SPANISH) ret = AddGRFString(grfid, stringid, GRFLX_SPANISH, true, text_to_add, def_string);
 
			return ret;
 
		}
 
	}
 

	
 
	for (id = 0; id < _num_grf_texts; id++) {
 
		if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
 
			break;
 
		}
 
	}
 

	
 
	/* Too many strings allocated, return empty */
 
	if (id == lengthof(_grf_text)) return STR_EMPTY;
 

	
 
	translatedtext = TranslateTTDPatchCodes(text_to_add);
 

	
 
	newtext = malloc(sizeof(*newtext) + strlen(translatedtext) + 1);
 
	newtext->next   = NULL;
 
	newtext->langid = langid_to_add;
 
	strcpy(newtext->text, translatedtext);
 

	
 
	free(translatedtext);
 

	
 
	/* If we didn't find our stringid and grfid in the list, allocate a new id */
 
	if (id == _num_grf_texts) _num_grf_texts++;
 

	
 
	if (_grf_text[id].textholder == NULL) {
 
		_grf_text[id].grfid      = grfid;
 
		_grf_text[id].stringid   = stringid;
 
		_grf_text[id].def_string = def_string;
 
		_grf_text[id].textholder = newtext;
 
	} else {
 
		GRFText **ptext, *text;
 
		bool replaced = false;
 

	
 
		/* Loop through all languages and see if we can replace a string */
 
		for (ptext = &_grf_text[id].textholder; (text = *ptext) != NULL; ptext = &text->next) {
 
			if (text->langid != langid_to_add) continue;
 
			newtext->next = text->next;
 
			*ptext = newtext;
 
			free(text);
 
			replaced = true;
 
			break;
 
		}
 

	
 
		/* If a string wasn't replaced, then we must append the new string */
 
		if (!replaced) *ptext = newtext;
 
	}
 

	
 
	grfmsg(3, "Added 0x%X: grfid %08X string 0x%X lang 0x%X string '%s'", id, grfid, stringid, newtext->langid, newtext->text);
 

	
 
	return (GRFTAB << TABSIZE) + id;
 
}
 

	
 
/* Used to remember the grfid that the last retrieved string came from */
 
static uint32 _last_grfid = 0;
 

	
 
/**
 
 * Returns the index for this stringid associated with its grfID
 
 */
 
StringID GetGRFStringID(uint32 grfid, uint16 stringid)
 
{
 
	uint id;
 

	
 
	/* grfid is zero when we're being called via an include */
 
	if (grfid == 0) grfid = _last_grfid;
 

	
 
	for (id = 0; id < _num_grf_texts; id++) {
 
		if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
 
			return (GRFTAB << TABSIZE) + id;
 
		}
 
	}
 

	
 
	return STR_UNDEFINED;
 
}
 

	
 

	
 
char *GetGRFString(char *buff, uint16 stringid, const char* last)
 
{
 
	const GRFText *default_text = NULL;
 
	const GRFText *search_text;
 

	
 
	assert(_grf_text[stringid].grfid != 0);
 

	
 
	/* Remember this grfid in case the string has included text */
 
	_last_grfid = _grf_text[stringid].grfid;
 

	
 
	/*Search the list of lang-strings of this stringid for current lang */
 
	for (search_text = _grf_text[stringid].textholder; search_text != NULL; search_text = search_text->next) {
 
		if (search_text->langid == _currentLangID) {
 
			return strecpy(buff, search_text->text, last);
 
		}
 

	
 
		/* If the current string is English or American, set it as the
 
		 * fallback language if the specific language isn't available. */
 
		if (search_text->langid == GRFLX_UNSPECIFIED || (default_text == NULL && (search_text->langid == GRFLX_ENGLISH || search_text->langid == GRFLX_AMERICAN))) {
 
			default_text = search_text;
 
		}
 
	}
 

	
 
	/* If there is a fallback string, return that */
 
	if (default_text != NULL) return strecpy(buff, default_text->text, last);
 

	
 
	/* Use the default string ID if the fallback string isn't available */
 
	return GetString(buff, _grf_text[stringid].def_string, last);
 
}
 

	
 
/**
 
 * Equivalence Setter function between game and newgrf langID.
 
 * This function will adjust _currentLangID as to what is the LangID
 
 * of the current language set by the user.
 
 * The array iso_codes will be used to find that match.
 
 * If not found, it will have to be standard english
 
 * This function is called after the user changed language,
 
 * from strings.c:ReadLanguagePack
 
 * @param iso code of current selection
 
 */
 
void SetCurrentGrfLangID(const char *iso_name)
 
{
 
	/* Use English by default, if we can't match up the iso_code. */
 
	byte ret = GRFLX_ENGLISH;
 
	byte i;
 

	
 
	for (i=0; i < lengthof(iso_codes); i++) {
 
		if (strncmp(iso_codes[i].code, iso_name, strlen(iso_codes[i].code)) == 0) {
 
			/* We found a match, so let's use it. */
 
			ret = i;
 
			break;
 
		}
 
	}
 
	_currentLangID = ret;
 
}
 

	
 
/**
 
 * House cleaning.
 
 * Remove all strings and reset the text counter.
 
 */
 
void CleanUpStrings(void)
 
{
 
	uint id;
 

	
 
	for (id = 0; id < _num_grf_texts; id++) {
 
		GRFText *grftext = _grf_text[id].textholder;
 
		while (grftext != NULL) {
 
			GRFText *grftext2 = grftext->next;
 
			free(grftext);
 
			grftext = grftext2;
 
		}
 
		_grf_text[id].grfid      = 0;
 
		_grf_text[id].stringid   = 0;
 
		_grf_text[id].textholder = NULL;
 
	}
 

	
 
	_num_grf_texts = 0;
 
}
src/news_gui.c
Show inline comments
 
deleted file
src/news_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "strings.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "news.h"
 
#include "vehicle.h"
 
#include "sound.h"
 
#include "variables.h"
 
#include "date.h"
 
#include "string.h"
 

	
 
/* News system
 
 * News system is realized as a FIFO queue (in an array)
 
 * The positions in the queue can't be rearranged, we only access
 
 * the array elements through pointers to the elements. Once the
 
 * array is full, the oldest entry (_oldest_news) is being overwritten
 
 * by the newest (_latest news).
 
 *
 
 * oldest                   current   lastest
 
 *  |                          |         |
 
 * [O------------F-------------C---------L           ]
 
 *               |
 
 *            forced
 
 *
 
 * Of course by using an array we can have situations like
 
 *
 
 * [----L          O-----F---------C-----------------]
 
 * This is where we have wrapped around the array and have
 
 * (MAX_NEWS - O) + L news items
 
 */
 

	
 
#define MAX_NEWS 30
 

	
 
typedef byte NewsID;
 
#define INVALID_NEWS 255
 

	
 
static NewsItem _news_items[MAX_NEWS];
 
static NewsID _current_news = INVALID_NEWS; // points to news item that should be shown next
 
static NewsID _oldest_news = 0;    // points to first item in fifo queue
 
static NewsID _latest_news = INVALID_NEWS;  // points to last item in fifo queue
 
/* if the message being shown was forced by the user, its index is stored in
 
 * _forced_news. forced_news is INVALID_NEWS otherwise.
 
 * (Users can force messages through history or "last message") */
 
static NewsID _forced_news = INVALID_NEWS;
 

	
 
static byte _total_news = 0; // total news count
 

	
 
void DrawNewsNewTrainAvail(Window *w);
 
void DrawNewsNewRoadVehAvail(Window *w);
 
void DrawNewsNewShipAvail(Window *w);
 
void DrawNewsNewAircraftAvail(Window *w);
 
void DrawNewsBankrupcy(Window *w);
 
static void MoveToNextItem(void);
 

	
 
StringID GetNewsStringNewTrainAvail(const NewsItem *ni);
 
StringID GetNewsStringNewRoadVehAvail(const NewsItem *ni);
 
StringID GetNewsStringNewShipAvail(const NewsItem *ni);
 
StringID GetNewsStringNewAircraftAvail(const NewsItem *ni);
 
StringID GetNewsStringBankrupcy(const NewsItem *ni);
 

	
 
static DrawNewsCallbackProc * const _draw_news_callback[] = {
 
	DrawNewsNewTrainAvail,    /* DNC_TRAINAVAIL */
 
	DrawNewsNewRoadVehAvail,  /* DNC_ROADAVAIL */
 
	DrawNewsNewShipAvail,     /* DNC_SHIPAVAIL */
 
	DrawNewsNewAircraftAvail, /* DNC_AIRCRAFTAVAIL */
 
	DrawNewsBankrupcy,        /* DNC_BANKRUPCY */
 
};
 

	
 
GetNewsStringCallbackProc * const _get_news_string_callback[] = {
 
	GetNewsStringNewTrainAvail,    /* DNC_TRAINAVAIL */
 
	GetNewsStringNewRoadVehAvail,  /* DNC_ROADAVAIL */
 
	GetNewsStringNewShipAvail,     /* DNC_SHIPAVAIL */
 
	GetNewsStringNewAircraftAvail, /* DNC_AIRCRAFTAVAIL */
 
	GetNewsStringBankrupcy,        /* DNC_BANKRUPCY */
 
};
 

	
 
void InitNewsItemStructs(void)
 
{
 
	memset(_news_items, 0, sizeof(_news_items));
 
	_current_news = INVALID_NEWS;
 
	_oldest_news = 0;
 
	_latest_news = INVALID_NEWS;
 
	_forced_news = INVALID_NEWS;
 
	_total_news = 0;
 
}
 

	
 
void DrawNewsBorder(const Window *w)
 
{
 
	int left = 0;
 
	int right = w->width - 1;
 
	int top = 0;
 
	int bottom = w->height - 1;
 

	
 
	GfxFillRect(left, top, right, bottom, 0xF);
 

	
 
	GfxFillRect(left, top, left, bottom, 0xD7);
 
	GfxFillRect(right, top, right, bottom, 0xD7);
 
	GfxFillRect(left, top, right, top, 0xD7);
 
	GfxFillRect(left, bottom, right, bottom, 0xD7);
 

	
 
	DrawString(left + 2, top + 1, STR_00C6, 0);
 
}
 

	
 
static void NewsWindowProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: { /* If chatbar is open at creation time, we need to go above it */
 
		const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0);
 
		w->message.msg = (w1 != NULL) ? w1->height : 0;
 
	} break;
 

	
 
	case WE_PAINT: {
 
		const NewsItem *ni = WP(w, news_d).ni;
 
		ViewPort *vp;
 

	
 
		switch (ni->display_mode) {
 
			case NM_NORMAL:
 
			case NM_THIN: {
 
				DrawNewsBorder(w);
 

	
 
				DrawString(2, 1, STR_00C6, 0);
 

	
 
				SetDParam(0, ni->date);
 
				DrawStringRightAligned(428, 1, STR_01FF, 0);
 

	
 
				if (!(ni->flags & NF_VIEWPORT)) {
 
					COPY_IN_DPARAM(0, ni->params, lengthof(ni->params));
 
					DrawStringMultiCenter(215, ni->display_mode == NM_NORMAL ? 76 : 56,
 
						ni->string_id, 426);
 
				} else {
 
					byte bk = _display_opt;
 
					_display_opt &= ~DO_TRANS_BUILDINGS;
 
					DrawWindowViewport(w);
 
					_display_opt = bk;
 

	
 
					/* Shade the viewport into gray, or color*/
 
					vp = w->viewport;
 
					GfxFillRect(vp->left - w->left, vp->top - w->top,
 
						vp->left - w->left + vp->width - 1, vp->top - w->top + vp->height - 1,
 
						(ni->flags & NF_INCOLOR ? 0x322 : 0x323) | USE_COLORTABLE
 
					);
 

	
 
					COPY_IN_DPARAM(0, ni->params, lengthof(ni->params));
 
					DrawStringMultiCenter(w->width / 2, 20, ni->string_id, 428);
 
				}
 
				break;
 
			}
 

	
 
			case NM_CALLBACK: {
 
				_draw_news_callback[ni->callback](w);
 
				break;
 
			}
 

	
 
			default: {
 
				DrawWindowWidgets(w);
 
				if (!(ni->flags & NF_VIEWPORT)) {
 
					COPY_IN_DPARAM(0, ni->params, lengthof(ni->params));
 
					DrawStringMultiCenter(140, 38, ni->string_id, 276);
 
				} else {
 
					DrawWindowViewport(w);
 
					COPY_IN_DPARAM(0, ni->params, lengthof(ni->params));
 
					DrawStringMultiCenter(w->width / 2, w->height - 16, ni->string_id, 276);
 
				}
 
				break;
 
			}
 
		}
 
	} break;
 

	
 
	case WE_CLICK: {
 
		switch (e->we.click.widget) {
 
		case 1: {
 
			NewsItem *ni = WP(w, news_d).ni;
 
			DeleteWindow(w);
 
			ni->duration = 0;
 
			_forced_news = INVALID_NEWS;
 
		} break;
 
		case 0: {
 
			NewsItem *ni = WP(w, news_d).ni;
 
			if (ni->flags & NF_VEHICLE) {
 
				Vehicle *v = GetVehicle(ni->data_a);
 
				ScrollMainWindowTo(v->x_pos, v->y_pos);
 
			} else if (ni->flags & NF_TILE) {
 
				if (!ScrollMainWindowToTile(ni->data_a) && ni->data_b != 0)
 
					ScrollMainWindowToTile(ni->data_b);
 
			}
 
		} break;
 
		}
 
	} break;
 

	
 
	case WE_KEYPRESS:
 
		if (e->we.keypress.keycode == WKC_SPACE) {
 
			// Don't continue.
 
			e->we.keypress.cont = false;
 
			DeleteWindow(w);
 
		}
 
		break;
 

	
 
	case WE_MESSAGE: /* The chatbar has notified us that is was either created or closed */
 
		switch (e->we.message.msg) {
 
			case WE_CREATE: w->message.msg = e->we.message.wparam; break;
 
			case WE_DESTROY: w->message.msg = 0; break;
 
		}
 
		break;
 

	
 
	case WE_TICK: { /* Scroll up newsmessages from the bottom in steps of 4 pixels */
 
		int diff;
 
		int y = max(w->top - 4, _screen.height - w->height - 12 - w->message.msg);
 
		if (y == w->top) return;
 

	
 
		if (w->viewport != NULL)
 
			w->viewport->top += y - w->top;
 

	
 
		diff = abs(w->top - y);
 
		w->top = y;
 

	
 
		SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height);
 
	} break;
 
	}
 
}
 

	
 
/** Return the correct index in the pseudo-fifo
 
 * queue and deals with overflows when increasing the index */
 
static inline NewsID increaseIndex(NewsID i)
 
{
 
	assert(i != INVALID_NEWS);
 
	return (i + 1) % MAX_NEWS;
 
}
 

	
 
/** Return the correct index in the pseudo-fifo
 
 * queue and deals with overflows when decreasing the index */
 
static inline NewsID decreaseIndex(NewsID i)
 
{
 
	assert(i != INVALID_NEWS);
 
	return (i + MAX_NEWS - 1) % MAX_NEWS;
 
}
 

	
 
/** Add a new newsitem to be shown.
 
 * @param string String to display, can have special values based on parameter 'flags'
 
 * @param flags various control bits that will show various news-types. See macro NEWS_FLAGS()
 
 * @param data_a news-specific value based on news type
 
 * @param data_b news-specific value based on news type
 
 * @note flags exists of 4 byte-sized extra parameters.<br/>
 
 * 1.  0 -  7 display_mode, any of the NewsMode enums (NM_)<br/>
 
 * 2.  8 - 15 news flags, any of the NewsFlags enums (NF_) NF_INCOLOR are set automatically if needed<br/>
 
 * 3. 16 - 23 news category, any of the NewsType enums (NT_)<br/>
 
 * 4. 24 - 31 news callback function, any of the NewsCallback enums (DNC_)<br/>
 
 * If the display mode is NM_CALLBACK special news is shown and parameter
 
 * stringid has a special meaning.<br/>
 
 * DNC_TRAINAVAIL, DNC_ROADAVAIL, DNC_SHIPAVAIL, DNC_AIRCRAFTAVAIL: StringID is
 
 * the index of the engine that is shown<br/>
 
 * DNC_BANKRUPCY: bytes 0-3 of StringID contains the player that is in trouble,
 
 * and 4-7 contains what kind of bankrupcy message is shown, NewsBankrupcy enum (NB_)<br/>
 
 * @see NewsMode
 
 * @see NewsFlags
 
 * @see NewsType
 
 * @see NewsCallback */
 
void AddNewsItem(StringID string, uint32 flags, uint data_a, uint data_b)
 
{
 
	NewsID l_news;
 

	
 
	if (_game_mode == GM_MENU) return;
 

	
 
	// check the rare case that the oldest (to be overwritten) news item is open
 
	if (_total_news == MAX_NEWS && (_oldest_news == _current_news || _oldest_news == _forced_news))
 
		MoveToNextItem();
 

	
 
	_forced_news = INVALID_NEWS;
 
	if (_total_news < MAX_NEWS) _total_news++;
 

	
 
	/* Increase _latest_news. If we have no news yet, use _oldest news as an
 
	 * index. We cannot use 0 as _oldest_news can jump around due to
 
	 * DeleteVehicleNews */
 
	l_news = _latest_news;
 
	_latest_news = (_latest_news == INVALID_NEWS) ? _oldest_news : increaseIndex(_latest_news);
 

	
 
	/* If the fifo-buffer is full, overwrite the oldest entry */
 
	if (l_news != INVALID_NEWS && _latest_news == _oldest_news) {
 
		assert(_total_news == MAX_NEWS);
 
		_oldest_news = increaseIndex(_oldest_news);
 
	}
 

	
 
	/*DEBUG(misc, 0) ("+cur %3d, old %2d, lat %3d, for %3d, tot %2d",
 
	  _current_news, _oldest_news, _latest_news, _forced_news, _total_news); */
 

	
 
	{ /* Add news to _latest_news */
 
		Window *w;
 
		NewsItem *ni = &_news_items[_latest_news];
 
		memset(ni, 0, sizeof(*ni));
 

	
 
		ni->string_id = string;
 
		ni->display_mode = (byte)flags;
 
		ni->flags = (byte)(flags >> 8);
 

	
 
		// show this news message in color?
 
		if (_cur_year >= _patches.colored_news_year) ni->flags |= NF_INCOLOR;
 

	
 
		ni->type = (byte)(flags >> 16);
 
		ni->callback = (byte)(flags >> 24);
 
		ni->data_a = data_a;
 
		ni->data_b = data_b;
 
		ni->date = _date;
 
		COPY_OUT_DPARAM(ni->params, 0, lengthof(ni->params));
 

	
 
		w = FindWindowById(WC_MESSAGE_HISTORY, 0);
 
		if (w == NULL) return;
 
		SetWindowDirty(w);
 
		w->vscroll.count = _total_news;
 
	}
 
}
 

	
 

	
 
/* Don't show item if it's older than x days, corresponds with NewsType in news.h */
 
static const byte _news_items_age[] = {60, 60, 90, 60, 90, 30, 150, 30, 90, 180};
 

	
 
static const Widget _news_type13_widgets[] = {
 
{      WWT_PANEL,   RESIZE_NONE,    15,     0,   429,     0,   169, 0x0, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    15,     0,    10,     0,    11, 0x0, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static WindowDesc _news_type13_desc = {
 
	WDP_CENTER, 476, 430, 170,
 
	WC_NEWS_WINDOW, 0,
 
	WDF_DEF_WIDGET,
 
	_news_type13_widgets,
 
	NewsWindowProc
 
};
 

	
 
static const Widget _news_type2_widgets[] = {
 
{      WWT_PANEL,   RESIZE_NONE,    15,     0,   429,     0,   129, 0x0, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    15,     0,    10,     0,    11, 0x0, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static WindowDesc _news_type2_desc = {
 
	WDP_CENTER, 476, 430, 130,
 
	WC_NEWS_WINDOW, 0,
 
	WDF_DEF_WIDGET,
 
	_news_type2_widgets,
 
	NewsWindowProc
 
};
 

	
 
static const Widget _news_type0_widgets[] = {
 
{      WWT_PANEL,   RESIZE_NONE,     5,     0,   279,    14,    86, 0x0,              STR_NULL},
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     5,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     5,    11,   279,     0,    13, STR_012C_MESSAGE, STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,     5,     2,   277,    16,    64, 0x0,              STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static WindowDesc _news_type0_desc = {
 
	WDP_CENTER, 476, 280, 87,
 
	WC_NEWS_WINDOW, 0,
 
	WDF_DEF_WIDGET,
 
	_news_type0_widgets,
 
	NewsWindowProc
 
};
 

	
 
static const SoundFx _news_sounds[] = {
 
	SND_1D_APPLAUSE,
 
	SND_1D_APPLAUSE,
 
	0,
 
	0,
 
	0,
 
	0,
 
	SND_1E_OOOOH,
 
	0,
 
	0,
 
	0
 
};
 

	
 
/** Get the value of an item of the news-display settings. This is
 
 * a little tricky since on/off/summary must use 2 bits to store the value
 
 * @param item the item whose value is requested
 
 * @return return the found value which is between 0-2
 
 */
 
static inline byte GetNewsDisplayValue(byte item)
 
{
 
	assert(item < 10 && GB(_news_display_opt, item * 2, 2) <= 2);
 
	return GB(_news_display_opt, item * 2, 2);
 
}
 

	
 
/** Set the value of an item in the news-display settings. This is
 
 * a little tricky since on/off/summary must use 2 bits to store the value
 
 * @param item the item whose value is being set
 
 * @param val new value
 
 */
 
static inline void SetNewsDisplayValue(byte item, byte val)
 
{
 
	assert(item < 10 && val <= 2);
 
	SB(_news_display_opt, item * 2, 2, val);
 
}
 

	
 
// open up an own newspaper window for the news item
 
static void ShowNewspaper(NewsItem *ni)
 
{
 
	Window *w;
 
	SoundFx sound;
 
	int top;
 
	ni->flags &= ~NF_FORCE_BIG;
 
	ni->duration = 555;
 

	
 
	sound = _news_sounds[ni->type];
 
	if (sound != 0) SndPlayFx(sound);
 

	
 
	top = _screen.height;
 
	switch (ni->display_mode) {
 
		case NM_NORMAL:
 
		case NM_CALLBACK: {
 
			_news_type13_desc.top = top;
 
			w = AllocateWindowDesc(&_news_type13_desc);
 
			if (ni->flags & NF_VIEWPORT)
 
				AssignWindowViewport(w, 2, 58, 0x1AA, 0x6E,
 
					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), 0);
 
			break;
 
		}
 

	
 
		case NM_THIN: {
 
			_news_type2_desc.top = top;
 
			w = AllocateWindowDesc(&_news_type2_desc);
 
			if (ni->flags & NF_VIEWPORT)
 
				AssignWindowViewport(w, 2, 58, 0x1AA, 0x46,
 
					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), 0);
 
			break;
 
		}
 

	
 
		default: {
 
			_news_type0_desc.top = top;
 
			w = AllocateWindowDesc(&_news_type0_desc);
 
			if (ni->flags & NF_VIEWPORT)
 
				AssignWindowViewport(w, 3, 17, 0x112, 0x2F,
 
					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), 0);
 
			break;
 
		}
 
	}
 

	
 
	/*DEBUG(misc, 0) (" cur %3d, old %2d, lat %3d, for %3d, tot %2d",
 
	  _current_news, _oldest_news, _latest_news, _forced_news, _total_news); */
 

	
 
	WP(w, news_d).ni = &_news_items[_forced_news == INVALID_NEWS ? _current_news : _forced_news];
 
	w->flags4 |= WF_DISABLE_VP_SCROLL;
 
}
 

	
 
// show news item in the ticker
 
static void ShowTicker(const NewsItem *ni)
 
{
 
	Window *w;
 

	
 
	if (_news_ticker_sound) SndPlayFx(SND_16_MORSE);
 

	
 
	_statusbar_news_item = *ni;
 
	w = FindWindowById(WC_STATUS_BAR, 0);
 
	if (w != NULL) WP(w, def_d).data_1 = 360;
 
}
 

	
 

	
 
// Are we ready to show another news item?
 
// Only if nothing is in the newsticker and no newspaper is displayed
 
static bool ReadyForNextItem(void)
 
{
 
	const Window *w;
 
	NewsID item = (_forced_news == INVALID_NEWS) ? _current_news : _forced_news;
 
	NewsItem *ni;
 

	
 
	if (item >= MAX_NEWS) return true;
 
	ni = &_news_items[item];
 

	
 
	// Ticker message
 
	// Check if the status bar message is still being displayed?
 
	w = FindWindowById(WC_STATUS_BAR, 0);
 
	if (w != NULL && WP(w, const def_d).data_1 > -1280) return false;
 

	
 
	// Newspaper message, decrement duration counter
 
	if (ni->duration != 0) ni->duration--;
 

	
 
	// neither newsticker nor newspaper are running
 
	return (ni->duration == 0 || FindWindowById(WC_NEWS_WINDOW, 0) == NULL);
 
}
 

	
 
static void MoveToNextItem(void)
 
{
 
	DeleteWindowById(WC_NEWS_WINDOW, 0);
 
	_forced_news = INVALID_NEWS;
 

	
 
	// if we're not at the last item, then move on
 
	if (_current_news != _latest_news) {
 
		NewsItem *ni;
 

	
 
		_current_news = (_current_news == INVALID_NEWS) ? _oldest_news : increaseIndex(_current_news);
 
		ni = &_news_items[_current_news];
 

	
 
		// check the date, don't show too old items
 
		if (_date - _news_items_age[ni->type] > ni->date) return;
 

	
 
		switch (GetNewsDisplayValue(ni->type)) {
 
		case 0: { /* Off - show nothing only a small reminder in the status bar */
 
			Window *w = FindWindowById(WC_STATUS_BAR, 0);
 

	
 
			if (w != NULL) {
 
				WP(w, def_d).data_2 = 91;
 
				SetWindowDirty(w);
 
			}
 
			break;
 
		}
 

	
 
		case 1: /* Summary - show ticker, but if forced big, cascade to full */
 
			if (!(ni->flags & NF_FORCE_BIG)) {
 
				ShowTicker(ni);
 
				break;
 
			}
 
			/* Fallthrough */
 

	
 
		case 2: /* Full - show newspaper*/
 
			ShowNewspaper(ni);
 
			break;
 
		}
 
	}
 
}
 

	
 
void NewsLoop(void)
 
{
 
	// no news item yet
 
	if (_total_news == 0) return;
 

	
 
	if (ReadyForNextItem()) MoveToNextItem();
 
}
 

	
 
/* Do a forced show of a specific message */
 
static void ShowNewsMessage(NewsID i)
 
{
 
	if (_total_news == 0) return;
 

	
 
	// Delete the news window
 
	DeleteWindowById(WC_NEWS_WINDOW, 0);
 

	
 
	// setup forced news item
 
	_forced_news = i;
 

	
 
	if (_forced_news != INVALID_NEWS) {
 
		NewsItem *ni = &_news_items[_forced_news];
 
		ni->duration = 555;
 
		ni->flags |= NF_FORCE_BIG;
 
		DeleteWindowById(WC_NEWS_WINDOW, 0);
 
		ShowNewspaper(ni);
 
	}
 
}
 

	
 
void ShowLastNewsMessage(void)
 
{
 
	switch (_forced_news) {
 
		case INVALID_NEWS: // Not forced any news yet, show the current one
 
			ShowNewsMessage(_current_news);
 
			break;
 
		case 0: //
 
			ShowNewsMessage(_total_news != MAX_NEWS ? _latest_news : MAX_NEWS - 1);
 
			break;
 
		default: // 'Scrolling' through news history show each one in turn
 
			ShowNewsMessage(_forced_news - 1);
 
			break;
 
	}
 
}
 

	
 

	
 
/* return news by number, with 0 being the most
 
 * recent news. Returns INVALID_NEWS if end of queue reached. */
 
static NewsID getNews(NewsID i)
 
{
 
	if (i >= _total_news) return INVALID_NEWS;
 

	
 
	if (_latest_news < i) {
 
		i = _latest_news + MAX_NEWS - i;
 
	} else {
 
		i = _latest_news - i;
 
	}
 

	
 
	i %= MAX_NEWS;
 
	return i;
 
}
 

	
 
/** Draw an unformatted news message truncated to a maximum length. If
 
 * length exceeds maximum length it will be postfixed by '...'
 
 * @param x,y position of the string
 
 * @param color the color the string will be shown in
 
 * @param *ni NewsItem being printed
 
 * @param maxw maximum width of string in pixels
 
 */
 
static void DrawNewsString(int x, int y, uint16 color, const NewsItem *ni, uint maxw)
 
{
 
	char buffer[512], buffer2[512];
 
	const char *ptr;
 
	char *dest;
 
	StringID str;
 

	
 
	if (ni->display_mode == 3) {
 
		str = _get_news_string_callback[ni->callback](ni);
 
	} else {
 
		COPY_IN_DPARAM(0, ni->params, lengthof(ni->params));
 
		str = ni->string_id;
 
	}
 

	
 
	GetString(buffer, str, lastof(buffer));
 
	/* Copy the just gotten string to another buffer to remove any formatting
 
	 * from it such as big fonts, etc. */
 
	ptr  = buffer;
 
	dest = buffer2;
 
	for (;;) {
 
		WChar c = Utf8Consume(&ptr);
 
		if (c == 0) break;
 
		if (c == '\r') {
 
			dest[0] = dest[1] = dest[2] = dest[3] = ' ';
 
			dest += 4;
 
		} else if (IsPrintable(c)) {
 
			dest += Utf8Encode(dest, c);
 
		}
 
	}
 

	
 
	*dest = '\0';
 
	/* Truncate and show string; postfixed by '...' if neccessary */
 
	DoDrawStringTruncated(buffer2, x, y, color, maxw);
 
}
 

	
 

	
 
static void MessageHistoryWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		int y = 19;
 
		NewsID p, show;
 

	
 
		SetVScrollCount(w, _total_news);
 
		DrawWindowWidgets(w);
 

	
 
		if (_total_news == 0) break;
 
		show = min(_total_news, w->vscroll.cap);
 

	
 
		for (p = w->vscroll.pos; p < w->vscroll.pos + show; p++) {
 
			// get news in correct order
 
			const NewsItem *ni = &_news_items[getNews(p)];
 

	
 
			SetDParam(0, ni->date);
 
			DrawString(4, y, STR_SHORT_DATE, 12);
 

	
 
			DrawNewsString(82, y, 12, ni, w->width - 95);
 
			y += 12;
 
		}
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 3: {
 
			int y = (e->we.click.pt.y - 19) / 12;
 
			NewsID p = getNews(y + w->vscroll.pos);
 

	
 
			if (p == INVALID_NEWS) break;
 

	
 
			ShowNewsMessage(p);
 
			break;
 
		}
 
		}
 
		break;
 

	
 
	case WE_RESIZE:
 
		w->vscroll.cap += e->we.sizing.diff.y / 12;
 
		break;
 
	}
 
}
 

	
 
static const Widget _message_history_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,  RESIZE_RIGHT,    13,    11,   387,     0,    13, STR_MESSAGE_HISTORY, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,     RESIZE_LR,    13,   388,   399,     0,    13, 0x0,                 STR_STICKY_BUTTON},
 
{      WWT_PANEL,     RESIZE_RB,    13,     0,   387,    14,   139, 0x0,                 STR_MESSAGE_HISTORY_TIP},
 
{  WWT_SCROLLBAR,    RESIZE_LRB,    13,   388,   399,    14,   127, 0x0,                 STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{  WWT_RESIZEBOX,   RESIZE_LRTB,    13,   388,   399,   128,   139, 0x0,                 STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _message_history_desc = {
 
	240, 22, 400, 140,
 
	WC_MESSAGE_HISTORY, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_message_history_widgets,
 
	MessageHistoryWndProc
 
};
 

	
 
void ShowMessageHistory(void)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_MESSAGE_HISTORY, 0);
 
	w = AllocateWindowDesc(&_message_history_desc);
 

	
 
	if (w != NULL) {
 
		w->vscroll.cap = 10;
 
		w->vscroll.count = _total_news;
 
		w->resize.step_height = 12;
 
		w->resize.height = w->height - 12 * 6; // minimum of 4 items in the list, each item 12 high
 
		w->resize.step_width = 1;
 
		w->resize.width = 200; // can't make window any smaller than 200 pixel
 
		SetWindowDirty(w);
 
	}
 
}
 

	
 
/** Setup the disabled/enabled buttons in the message window
 
 * If the value is 'off' disable the [<] widget, and enable the [>] one
 
 * Same-wise for all the others. Starting value of 3 is the first widget
 
 * group. These are grouped as [<][>] .. [<][>], etc.
 
 */
 
static void SetMessageButtonStates(Window *w, byte value, int element)
 
{
 
	element *= 2;
 

	
 
	SetWindowWidgetDisabledState(w, element + 3, value == 0);
 
	SetWindowWidgetDisabledState(w, element + 3 + 1, value == 2);
 
}
 

	
 
static void MessageOptionsWndProc(Window *w, WindowEvent *e)
 
{
 
	static const StringID message_opt[] = {STR_OFF, STR_SUMMARY, STR_FULL, INVALID_STRING_ID};
 

	
 
	/* WP(w, def_d).data_1 are stores the clicked state of the fake widgets
 
	 * WP(w, def_d).data_2 stores state of the ALL on/off/summary button */
 
	switch (e->event) {
 
	case WE_CREATE: {
 
		uint32 val = _news_display_opt;
 
		int i;
 
		WP(w, def_d).data_1 = WP(w, def_d).data_2 = 0;
 

	
 
		// Set up the initial disabled buttons in the case of 'off' or 'full'
 
		for (i = 0; i != 10; i++, val >>= 2) SetMessageButtonStates(w, val & 0x3, i);
 
	} break;
 

	
 
	case WE_PAINT: {
 
		uint32 val = _news_display_opt;
 
		int click_state = WP(w, def_d).data_1;
 
		int i, y;
 

	
 
		if (_news_ticker_sound) LowerWindowWidget(w, 25);
 
		DrawWindowWidgets(w);
 

	
 
		/* XXX - Draw the fake widgets-buttons. Can't add these to the widget-desc since
 
		 * openttd currently can only handle 32 widgets. So hack it *g* */
 
		for (i = 0, y = 26; i != 10; i++, y += 12, click_state >>= 1, val >>= 2) {
 
			bool clicked = !!(click_state & 1);
 

	
 
			DrawFrameRect(13, y, 89, 11 + y, 3, (clicked) ? FR_LOWERED : 0);
 
			DrawStringCentered(((13 + 89 + 1) >> 1) + clicked, ((y + 11 + y + 1) >> 1) - 5 + clicked, message_opt[val & 0x3], 0x10);
 
			DrawString(103, y + 1, i + STR_0206_ARRIVAL_OF_FIRST_VEHICLE, 0);
 
		}
 

	
 
		DrawString(  8, y + 9, message_opt[WP(w, def_d).data_2], 0x10);
 
		DrawString(103, y + 9, STR_MESSAGES_ALL, 0);
 
		DrawString(103, y + 9 + 12, STR_MESSAGE_SOUND, 0);
 

	
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 2: /* Clicked on any of the fake widgets */
 
			if (e->we.click.pt.x > 13 && e->we.click.pt.x < 89 && e->we.click.pt.y > 26 && e->we.click.pt.y < 146) {
 
				int element = (e->we.click.pt.y - 26) / 12;
 
				byte val = (GetNewsDisplayValue(element) + 1) % 3;
 

	
 
				SetMessageButtonStates(w, val, element);
 
				SetNewsDisplayValue(element, val);
 

	
 
				WP(w, def_d).data_1 |= (1 << element);
 
				w->flags4 |= 5 << WF_TIMEOUT_SHL; // XXX - setup unclick (fake widget)
 
				SetWindowDirty(w);
 
			}
 
			break;
 
		case 23: case 24: /* Dropdown menu for all settings */
 
			ShowDropDownMenu(w, message_opt, WP(w, def_d).data_2, 24, 0, 0);
 
			break;
 
		case 25: /* Change ticker sound on/off */
 
			_news_ticker_sound ^= 1;
 
			ToggleWidgetLoweredState(w, e->we.click.widget);
 
			InvalidateWidget(w, e->we.click.widget);
 
			break;
 
		default: { /* Clicked on the [<] .. [>] widgets */
 
			int wid = e->we.click.widget;
 
			if (wid > 2 && wid < 23) {
 
				int element = (wid - 3) / 2;
 
				byte val = (GetNewsDisplayValue(element) + ((wid & 1) ? -1 : 1)) % 3;
 

	
 
				SetMessageButtonStates(w, val, element);
 
				SetNewsDisplayValue(element, val);
 
				SetWindowDirty(w);
 
			}
 
		} break;
 
		} break;
 

	
 
	case WE_DROPDOWN_SELECT: {/* Select all settings for newsmessages */
 
		int i;
 

	
 
		WP(w, def_d).data_2 = e->we.dropdown.index;
 

	
 
		for (i = 0; i != 10; i++) {
 
			SB(_news_display_opt, i*2, 2, e->we.dropdown.index);
 
			SetMessageButtonStates(w, e->we.dropdown.index, i);
 
		}
 
		SetWindowDirty(w);
 
		break;
 
		}
 

	
 
	case WE_TIMEOUT: /* XXX - Hack to animate 'fake' buttons */
 
		WP(w, def_d).data_1 = 0;
 
		SetWindowDirty(w);
 
		break;
 
	}
 
}
 

	
 
static const Widget _message_options_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,   10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    13,    11,  409,     0,    13, STR_0204_MESSAGE_OPTIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    13,     0,  409,    14,   184, 0x0,                      STR_NULL},
 

	
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    26,    37, SPR_ARROW_LEFT,           STR_HSCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    26,    37, SPR_ARROW_RIGHT,          STR_HSCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    38,    49, SPR_ARROW_LEFT,           STR_HSCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    38,    49, SPR_ARROW_RIGHT,          STR_HSCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    50,    61, SPR_ARROW_LEFT,           STR_HSCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    50,    61, SPR_ARROW_RIGHT,          STR_HSCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    62,    73, SPR_ARROW_LEFT,           STR_HSCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    62,    73, SPR_ARROW_RIGHT,          STR_HSCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    74,    85, SPR_ARROW_LEFT,           STR_HSCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    74,    85, SPR_ARROW_RIGHT,          STR_HSCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    86,    97, SPR_ARROW_LEFT,           STR_HSCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    86,    97, SPR_ARROW_RIGHT,          STR_HSCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,    98,   109, SPR_ARROW_LEFT,           STR_HSCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,    98,   109, SPR_ARROW_RIGHT,          STR_HSCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,   110,   121, SPR_ARROW_LEFT,           STR_HSCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,   110,   121, SPR_ARROW_RIGHT,          STR_HSCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,   122,   133, SPR_ARROW_LEFT,           STR_HSCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,   122,   133, SPR_ARROW_RIGHT,          STR_HSCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,     4,   12,   134,   145, SPR_ARROW_LEFT,           STR_HSCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHIMGBTN,   RESIZE_NONE,     3,    90,   98,   134,   145, SPR_ARROW_RIGHT,          STR_HSCROLL_BAR_SCROLLS_LIST},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,     3,     4,   86,   154,   165, 0x0,                      STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,     3,    87,   98,   154,   165, STR_0225,                 STR_NULL},
 
{  WWT_TEXTBTN_2,   RESIZE_NONE,     3,     4,   98,   166,   177, STR_02DB_OFF,             STR_NULL},
 

	
 
{      WWT_LABEL,   RESIZE_NONE,    13,     0,  409,    13,    26, STR_0205_MESSAGE_TYPES,   STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _message_options_desc = {
 
	270, 22, 410, 185,
 
	WC_GAME_OPTIONS, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_message_options_widgets,
 
	MessageOptionsWndProc
 
};
 

	
 
void ShowMessageOptions(void)
 
{
 
	DeleteWindowById(WC_GAME_OPTIONS, 0);
 
	AllocateWindowDesc(&_message_options_desc);
 
}
 

	
 

	
 
void DeleteVehicleNews(VehicleID vid, StringID news)
 
{
 
	NewsID n;
 

	
 
	for (n = _oldest_news; _latest_news != INVALID_NEWS; n = increaseIndex(n)) {
 
		const NewsItem *ni = &_news_items[n];
 

	
 
		if (ni->flags & NF_VEHICLE &&
 
				ni->data_a == vid &&
 
				(news == INVALID_STRING_ID || ni->string_id == news)) {
 
			Window *w;
 

	
 
			if (_forced_news == n || _current_news == n) MoveToNextItem();
 
			_total_news--;
 

	
 
			/* If this is the last news item, invalidate _latest_news */
 
			if (_total_news == 0) {
 
				assert(_latest_news == _oldest_news);
 
				_latest_news = INVALID_NEWS;
 
			}
 

	
 
			/* Since we only imitate a FIFO removing an arbitrary element does need
 
			 * some magic. Remove the item by shifting head towards the tail. eg
 
			 *    oldest    remove  last
 
			 *        |        |     |
 
			 * [------O--------n-----L--]
 
			 * will become (change dramatized to make clear)
 
			 * [---------O-----------L--]
 
			 * We also need an update of the current, forced and visible (open window)
 
			 * news's as this shifting could change the items they were pointing to */
 
			if (_total_news != 0) {
 
				NewsID i, visible_news;
 
				w = FindWindowById(WC_NEWS_WINDOW, 0);
 
				visible_news = (w != NULL) ? (NewsID)(WP(w, news_d).ni - _news_items) : INVALID_NEWS;
 

	
 
				for (i = n;; i = decreaseIndex(i)) {
 
					_news_items[i] = _news_items[decreaseIndex(i)];
 

	
 
					if (i == _current_news) _current_news = increaseIndex(_current_news);
 
					if (i == _forced_news) _forced_news = increaseIndex(_forced_news);
 
					if (i == visible_news) WP(w, news_d).ni = &_news_items[increaseIndex(visible_news)];
 

	
 
					if (i == _oldest_news) break;
 
				}
 
				_oldest_news = increaseIndex(_oldest_news);
 
			}
 

	
 
			/*DEBUG(misc, 0) ("-cur %3d, old %2d, lat %3d, for %3d, tot %2d",
 
			  _current_news, _oldest_news, _latest_news, _forced_news, _total_news); */
 

	
 
			w = FindWindowById(WC_MESSAGE_HISTORY, 0);
 
			if (w != NULL) {
 
				SetWindowDirty(w);
 
				w->vscroll.count = _total_news;
 
			}
 
		}
 

	
 
		if (n == _latest_news) break;
 
	}
 
}
src/npf.c
Show inline comments
 
deleted file
src/npf.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "npf.h"
 
#include "aystar.h"
 
#include "macros.h"
 
#include "pathfind.h"
 
#include "station.h"
 
#include "station_map.h"
 
#include "tile.h"
 
#include "depot.h"
 
#include "tunnel_map.h"
 
#include "network/network.h"
 
#include "water_map.h"
 

	
 
static AyStar _npf_aystar;
 

	
 
/* The cost of each trackdir. A diagonal piece is the full NPF_TILE_LENGTH,
 
 * the shorter piece is sqrt(2)/2*NPF_TILE_LENGTH =~ 0.7071
 
 */
 
#define NPF_STRAIGHT_LENGTH (uint)(NPF_TILE_LENGTH * STRAIGHT_TRACK_LENGTH)
 
static const uint _trackdir_length[TRACKDIR_END] = {
 
	NPF_TILE_LENGTH, NPF_TILE_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH,
 
	0, 0,
 
	NPF_TILE_LENGTH, NPF_TILE_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH
 
};
 

	
 
/**
 
 * Calculates the minimum distance traveled to get from t0 to t1 when only
 
 * using tracks (ie, only making 45 degree turns). Returns the distance in the
 
 * NPF scale, ie the number of full tiles multiplied by NPF_TILE_LENGTH to
 
 * prevent rounding.
 
 */
 
static uint NPFDistanceTrack(TileIndex t0, TileIndex t1)
 
{
 
	const uint dx = abs(TileX(t0) - TileX(t1));
 
	const uint dy = abs(TileY(t0) - TileY(t1));
 

	
 
	const uint straightTracks = 2 * min(dx, dy); /* The number of straight (not full length) tracks */
 
	/* OPTIMISATION:
 
	 * Original: diagTracks = max(dx, dy) - min(dx,dy);
 
	 * Proof:
 
	 * (dx+dy) - straightTracks  == (min + max) - straightTracks = min + max - 2 * min = max - min */
 
	const uint diagTracks = dx + dy - straightTracks; /* The number of diagonal (full tile length) tracks. */
 

	
 
	/* Don't factor out NPF_TILE_LENGTH below, this will round values and lose
 
	 * precision */
 
	return diagTracks * NPF_TILE_LENGTH + straightTracks * NPF_TILE_LENGTH * STRAIGHT_TRACK_LENGTH;
 
}
 

	
 

	
 
#if 0
 
static uint NTPHash(uint key1, uint key2)
 
{
 
	/* This function uses the old hash, which is fixed on 10 bits (1024 buckets) */
 
	return PATHFIND_HASH_TILE(key1);
 
}
 
#endif
 

	
 
/**
 
 * Calculates a hash value for use in the NPF.
 
 * @param key1 The TileIndex of the tile to hash
 
 * @param key2 The Trackdir of the track on the tile.
 
 *
 
 * @todo Think of a better hash.
 
 */
 
static uint NPFHash(uint key1, uint key2)
 
{
 
	/* TODO: think of a better hash? */
 
	uint part1 = TileX(key1) & NPF_HASH_HALFMASK;
 
	uint part2 = TileY(key1) & NPF_HASH_HALFMASK;
 

	
 
	assert(IsValidTrackdir(key2));
 
	assert(IsValidTile(key1));
 
	return ((part1 << NPF_HASH_HALFBITS | part2) + (NPF_HASH_SIZE * key2 / TRACKDIR_END)) % NPF_HASH_SIZE;
 
}
 

	
 
static int32 NPFCalcZero(AyStar* as, AyStarNode* current, OpenListNode* parent)
 
{
 
	return 0;
 
}
 

	
 
/* Calcs the tile of given station that is closest to a given tile
 
 * for this we assume the station is a rectangle,
 
 * as defined by its top tile (st->train_tile) and its width/height (st->trainst_w, st->trainst_h)
 
 */
 
static TileIndex CalcClosestStationTile(StationID station, TileIndex tile)
 
{
 
	const Station* st = GetStation(station);
 

	
 
	uint minx = TileX(st->train_tile);  // topmost corner of station
 
	uint miny = TileY(st->train_tile);
 
	uint maxx = minx + st->trainst_w - 1; // lowermost corner of station
 
	uint maxy = miny + st->trainst_h - 1;
 
	uint x;
 
	uint y;
 

	
 
	// we are going the aim for the x coordinate of the closest corner
 
	// but if we are between those coordinates, we will aim for our own x coordinate
 
	x = clamp(TileX(tile), minx, maxx);
 

	
 
	// same for y coordinate, see above comment
 
	y = clamp(TileY(tile), miny, maxy);
 

	
 
	// return the tile of our target coordinates
 
	return TileXY(x, y);
 
}
 

	
 
/* Calcs the heuristic to the target station or tile. For train stations, it
 
 * takes into account the direction of approach.
 
 */
 
static int32 NPFCalcStationOrTileHeuristic(AyStar* as, AyStarNode* current, OpenListNode* parent)
 
{
 
	NPFFindStationOrTileData* fstd = (NPFFindStationOrTileData*)as->user_target;
 
	NPFFoundTargetData* ftd = (NPFFoundTargetData*)as->user_path;
 
	TileIndex from = current->tile;
 
	TileIndex to = fstd->dest_coords;
 
	uint dist;
 

	
 
	// for train-stations, we are going to aim for the closest station tile
 
	if (as->user_data[NPF_TYPE] == TRANSPORT_RAIL && fstd->station_index != INVALID_STATION)
 
		to = CalcClosestStationTile(fstd->station_index, from);
 

	
 
	if (as->user_data[NPF_TYPE] == TRANSPORT_ROAD) {
 
		/* Since roads only have diagonal pieces, we use manhattan distance here */
 
		dist = DistanceManhattan(from, to) * NPF_TILE_LENGTH;
 
	} else {
 
		/* Ships and trains can also go diagonal, so the minimum distance is shorter */
 
		dist = NPFDistanceTrack(from, to);
 
	}
 

	
 
	DEBUG(npf, 4, "Calculating H for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), dist);
 

	
 
	if (dist < ftd->best_bird_dist) {
 
		ftd->best_bird_dist = dist;
 
		ftd->best_trackdir = current->user_data[NPF_TRACKDIR_CHOICE];
 
	}
 
	return dist;
 
}
 

	
 

	
 
/* Fills AyStarNode.user_data[NPF_TRACKDIRCHOICE] with the chosen direction to
 
 * get here, either getting it from the current choice or from the parent's
 
 * choice */
 
static void NPFFillTrackdirChoice(AyStarNode* current, OpenListNode* parent)
 
{
 
	if (parent->path.parent == NULL) {
 
		Trackdir trackdir = (Trackdir)current->direction;
 
		/* This is a first order decision, so we'd better save the
 
		 * direction we chose */
 
		current->user_data[NPF_TRACKDIR_CHOICE] = trackdir;
 
		DEBUG(npf, 6, "Saving trackdir: 0x%X", trackdir);
 
	} else {
 
		/* We've already made the decision, so just save our parent's decision */
 
		current->user_data[NPF_TRACKDIR_CHOICE] = parent->path.node.user_data[NPF_TRACKDIR_CHOICE];
 
	}
 
}
 

	
 
/* Will return the cost of the tunnel. If it is an entry, it will return the
 
 * cost of that tile. If the tile is an exit, it will return the tunnel length
 
 * including the exit tile. Requires that this is a Tunnel tile */
 
static uint NPFTunnelCost(AyStarNode* current)
 
{
 
	DiagDirection exitdir = TrackdirToExitdir((Trackdir)current->direction);
 
	TileIndex tile = current->tile;
 
	if (GetTunnelDirection(tile) == ReverseDiagDir(exitdir)) {
 
		/* We just popped out if this tunnel, since were
 
		 * facing the tunnel exit */
 
		FindLengthOfTunnelResult flotr;
 
		flotr = FindLengthOfTunnel(tile, ReverseDiagDir(exitdir));
 
		return flotr.length * NPF_TILE_LENGTH;
 
		//TODO: Penalty for tunnels?
 
	} else {
 
		/* We are entering the tunnel, the enter tile is just a
 
		 * straight track */
 
		return NPF_TILE_LENGTH;
 
	}
 
}
 

	
 
static inline uint NPFBridgeCost(AyStarNode *current)
 
{
 
	return NPF_TILE_LENGTH * GetBridgeLength(current->tile, GetOtherBridgeEnd(current->tile));
 
}
 

	
 
static uint NPFSlopeCost(AyStarNode* current)
 
{
 
	TileIndex next = current->tile + TileOffsByDiagDir(TrackdirToExitdir(current->direction));
 
	int x,y;
 
	int8 z1,z2;
 

	
 
	x = TileX(current->tile) * TILE_SIZE;
 
	y = TileY(current->tile) * TILE_SIZE;
 
	/* get the height of the center of the current tile */
 
	z1 = GetSlopeZ(x + TILE_SIZE / 2, y + TILE_SIZE / 2);
 

	
 
	x = TileX(next) * TILE_SIZE;
 
	y = TileY(next) * TILE_SIZE;
 
	/* get the height of the center of the next tile */
 
	z2 = GetSlopeZ(x + TILE_SIZE / 2, y + TILE_SIZE / 2);
 

	
 
	if (z2 - z1 > 1) {
 
		/* Slope up */
 
		return _patches.npf_rail_slope_penalty;
 
	}
 
	return 0;
 
	/* Should we give a bonus for slope down? Probably not, we
 
	 * could just substract that bonus from the penalty, because
 
	 * there is only one level of steepness... */
 
}
 

	
 
/**
 
 * Mark tiles by mowing the grass when npf debug level >= 1.
 
 * Will not work for multiplayer games, since it can (will) cause desyncs.
 
 */
 
static void NPFMarkTile(TileIndex tile)
 
{
 
#ifndef NO_DEBUG_MESSAGES
 
	if (_debug_npf_level < 1 || _networking) return;
 
	switch (GetTileType(tile)) {
 
		case MP_RAILWAY:
 
			/* DEBUG: mark visited tiles by mowing the grass under them ;-) */
 
			if (!IsTileDepotType(tile, TRANSPORT_RAIL)) {
 
				SetRailGroundType(tile, RAIL_GROUND_BARREN);
 
				MarkTileDirtyByTile(tile);
 
			}
 
			break;
 

	
 
		case MP_STREET:
 
			if (!IsTileDepotType(tile, TRANSPORT_ROAD)) {
 
				SetRoadside(tile, ROADSIDE_BARREN);
 
				MarkTileDirtyByTile(tile);
 
			}
 
			break;
 

	
 
		default:
 
			break;
 
	}
 
#endif
 
}
 

	
 
static int32 NPFWaterPathCost(AyStar* as, AyStarNode* current, OpenListNode* parent)
 
{
 
	//TileIndex tile = current->tile;
 
	int32 cost = 0;
 
	Trackdir trackdir = (Trackdir)current->direction;
 

	
 
	cost = _trackdir_length[trackdir]; /* Should be different for diagonal tracks */
 

	
 
	if (IsBuoyTile(current->tile) && IsDiagonalTrackdir(trackdir))
 
		cost += _patches.npf_buoy_penalty; /* A small penalty for going over buoys */
 

	
 
	if (current->direction != NextTrackdir((Trackdir)parent->path.node.direction))
 
		cost += _patches.npf_water_curve_penalty;
 

	
 
	/* TODO More penalties? */
 

	
 
	return cost;
 
}
 

	
 
/* Determine the cost of this node, for road tracks */
 
static int32 NPFRoadPathCost(AyStar* as, AyStarNode* current, OpenListNode* parent)
 
{
 
	TileIndex tile = current->tile;
 
	int32 cost = 0;
 

	
 
	/* Determine base length */
 
	switch (GetTileType(tile)) {
 
		case MP_TUNNELBRIDGE:
 
			cost = IsTunnel(tile) ? NPFTunnelCost(current) : NPFBridgeCost(current);
 
			break;
 

	
 
		case MP_STREET:
 
			cost = NPF_TILE_LENGTH;
 
			/* Increase the cost for level crossings */
 
			if (IsLevelCrossing(tile)) cost += _patches.npf_crossing_penalty;
 
			break;
 

	
 
		default:
 
			break;
 
	}
 

	
 
	/* Determine extra costs */
 

	
 
	/* Check for slope */
 
	cost += NPFSlopeCost(current);
 

	
 
	/* Check for turns. Road vehicles only really drive diagonal, turns are
 
	 * represented by non-diagonal tracks */
 
	if (!IsDiagonalTrackdir(current->direction))
 
		cost += _patches.npf_road_curve_penalty;
 

	
 
	NPFMarkTile(tile);
 
	DEBUG(npf, 4, "Calculating G for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), cost);
 
	return cost;
 
}
 

	
 

	
 
/* Determine the cost of this node, for railway tracks */
 
static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* parent)
 
{
 
	TileIndex tile = current->tile;
 
	Trackdir trackdir = (Trackdir)current->direction;
 
	int32 cost = 0;
 
	/* HACK: We create a OpenListNode manually, so we can call EndNodeCheck */
 
	OpenListNode new_node;
 

	
 
	/* Determine base length */
 
	switch (GetTileType(tile)) {
 
		case MP_TUNNELBRIDGE:
 
			cost = IsTunnel(tile) ? NPFTunnelCost(current) : NPFBridgeCost(current);
 
			break;
 

	
 
		case MP_RAILWAY:
 
			cost = _trackdir_length[trackdir]; /* Should be different for diagonal tracks */
 
			break;
 

	
 
		case MP_STREET: /* Railway crossing */
 
			cost = NPF_TILE_LENGTH;
 
			break;
 

	
 
		case MP_STATION:
 
			/* We give a station tile a penalty. Logically we would only want to give
 
			 * station tiles that are not our destination this penalty. This would
 
			 * discourage trains to drive through busy stations. But, we can just
 
			 * give any station tile a penalty, because every possible route will get
 
			 * this penalty exactly once, on its end tile (if it's a station) and it
 
			 * will therefore not make a difference. */
 
			cost = NPF_TILE_LENGTH + _patches.npf_rail_station_penalty;
 
			break;
 

	
 
		default:
 
			break;
 
	}
 

	
 
	/* Determine extra costs */
 

	
 
	/* Check for signals */
 
	if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir)) {
 
		/* Ordinary track with signals */
 
		if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_RED) {
 
			/* Signal facing us is red */
 
			if (!NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
 
				/* Penalize the first signal we
 
				 * encounter, if it is red */
 

	
 
				/* Is this a presignal exit or combo? */
 
				SignalType sigtype = GetSignalType(tile);
 
				if (sigtype == SIGTYPE_EXIT || sigtype == SIGTYPE_COMBO) {
 
					/* Penalise exit and combo signals differently (heavier) */
 
					cost += _patches.npf_rail_firstred_exit_penalty;
 
				} else {
 
					cost += _patches.npf_rail_firstred_penalty;
 
				}
 
			}
 
			/* Record the state of this signal */
 
			NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true);
 
		} else {
 
			/* Record the state of this signal */
 
			NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false);
 
		}
 
		NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true);
 
	}
 

	
 
	/* Penalise the tile if it is a target tile and the last signal was
 
	 * red */
 
	/* HACK: We create a new_node here so we can call EndNodeCheck. Ugly as hell
 
	 * of course... */
 
	new_node.path.node = *current;
 
	if (as->EndNodeCheck(as, &new_node) == AYSTAR_FOUND_END_NODE && NPFGetFlag(current, NPF_FLAG_LAST_SIGNAL_RED))
 
		cost += _patches.npf_rail_lastred_penalty;
 

	
 
	/* Check for slope */
 
	cost += NPFSlopeCost(current);
 

	
 
	/* Check for turns */
 
	if (current->direction != NextTrackdir((Trackdir)parent->path.node.direction))
 
		cost += _patches.npf_rail_curve_penalty;
 
	//TODO, with realistic acceleration, also the amount of straight track between
 
	//      curves should be taken into account, as this affects the speed limit.
 

	
 
	/* Check for reverse in depot */
 
	if (IsTileDepotType(tile, TRANSPORT_RAIL) && as->EndNodeCheck(as, &new_node) != AYSTAR_FOUND_END_NODE) {
 
		/* Penalise any depot tile that is not the last tile in the path. This
 
		 * _should_ penalise every occurence of reversing in a depot (and only
 
		 * that) */
 
		cost += _patches.npf_rail_depot_reverse_penalty;
 
	}
 

	
 
	/* Check for occupied track */
 
	//TODO
 

	
 
	NPFMarkTile(tile);
 
	DEBUG(npf, 4, "Calculating G for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), cost);
 
	return cost;
 
}
 

	
 
/* Will find any depot */
 
static int32 NPFFindDepot(AyStar* as, OpenListNode *current)
 
{
 
	/* It's not worth caching the result with NPF_FLAG_IS_TARGET here as below,
 
	 * since checking the cache not that much faster than the actual check */
 
	return IsTileDepotType(current->path.node.tile, as->user_data[NPF_TYPE]) ?
 
		AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
 
}
 

	
 
/* Will find a station identified using the NPFFindStationOrTileData */
 
static int32 NPFFindStationOrTile(AyStar* as, OpenListNode *current)
 
{
 
	NPFFindStationOrTileData* fstd = (NPFFindStationOrTileData*)as->user_target;
 
	AyStarNode *node = &current->path.node;
 
	TileIndex tile = node->tile;
 

	
 
	/* If GetNeighbours said we could get here, we assume the station type
 
	 * is correct */
 
	if (
 
		(fstd->station_index == INVALID_STATION && tile == fstd->dest_coords) || /* We've found the tile, or */
 
		(IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index) /* the station */
 
	) {
 
		return AYSTAR_FOUND_END_NODE;
 
	} else {
 
		return AYSTAR_DONE;
 
	}
 
}
 

	
 
/* To be called when current contains the (shortest route to) the target node.
 
 * Will fill the contents of the NPFFoundTargetData using
 
 * AyStarNode[NPF_TRACKDIR_CHOICE].
 
 */
 
static void NPFSaveTargetData(AyStar* as, OpenListNode* current)
 
{
 
	NPFFoundTargetData* ftd = (NPFFoundTargetData*)as->user_path;
 
	ftd->best_trackdir = (Trackdir)current->path.node.user_data[NPF_TRACKDIR_CHOICE];
 
	ftd->best_path_dist = current->g;
 
	ftd->best_bird_dist = 0;
 
	ftd->node = current->path.node;
 
}
 

	
 
/**
 
 * Finds out if a given player's vehicles are allowed to enter a given tile.
 
 * @param owner    The owner of the vehicle.
 
 * @param tile     The tile that is about to be entered.
 
 * @param enterdir The direction from which the vehicle wants to enter the tile.
 
 * @return         true if the vehicle can enter the tile.
 
 * @todo           This function should be used in other places than just NPF,
 
 *                 maybe moved to another file too.
 
 */
 
static bool VehicleMayEnterTile(Owner owner, TileIndex tile, DiagDirection enterdir)
 
{
 
	if (IsTileType(tile, MP_RAILWAY) ||           /* Rail tile (also rail depot) */
 
			IsRailwayStationTile(tile) ||               /* Rail station tile */
 
			IsTileDepotType(tile, TRANSPORT_ROAD) ||  /* Road depot tile */
 
			IsRoadStopTile(tile) ||                /* Road station tile */
 
			IsTileDepotType(tile, TRANSPORT_WATER)) { /* Water depot tile */
 
		return IsTileOwner(tile, owner); /* You need to own these tiles entirely to use them */
 
	}
 

	
 
	switch (GetTileType(tile)) {
 
		case MP_STREET:
 
			/* rail-road crossing : are we looking at the railway part? */
 
			if (IsLevelCrossing(tile) &&
 
					DiagDirToAxis(enterdir) != GetCrossingRoadAxis(tile)) {
 
				return IsTileOwner(tile, owner); /* Railway needs owner check, while the street is public */
 
			}
 
			break;
 

	
 
		case MP_TUNNELBRIDGE:
 
			if ((IsTunnel(tile) && GetTunnelTransportType(tile) == TRANSPORT_RAIL) ||
 
					(IsBridge(tile) && GetBridgeTransportType(tile) == TRANSPORT_RAIL)) {
 
				return IsTileOwner(tile, owner);
 
			}
 
			break;
 

	
 
		default:
 
			break;
 
	}
 

	
 
	return true; /* no need to check */
 
}
 

	
 

	
 
/**
 
 * Returns the direction the exit of the depot on the given tile is facing.
 
 */
 
static DiagDirection GetDepotDirection(TileIndex tile, TransportType type)
 
{
 
	assert(IsTileDepotType(tile, type));
 

	
 
	switch (type) {
 
		case TRANSPORT_RAIL:  return GetRailDepotDirection(tile);
 
		case TRANSPORT_ROAD:  return GetRoadDepotDirection(tile);
 
		case TRANSPORT_WATER: return GetShipDepotDirection(tile);
 
		default: return INVALID_DIAGDIR; /* Not reached */
 
	}
 
}
 

	
 

	
 
/* Will just follow the results of GetTileTrackStatus concerning where we can
 
 * go and where not. Uses AyStar.user_data[NPF_TYPE] as the transport type and
 
 * an argument to GetTileTrackStatus. Will skip tunnels, meaning that the
 
 * entry and exit are neighbours. Will fill
 
 * AyStarNode.user_data[NPF_TRACKDIR_CHOICE] with an appropriate value, and
 
 * copy AyStarNode.user_data[NPF_NODE_FLAGS] from the parent */
 
static void NPFFollowTrack(AyStar* aystar, OpenListNode* current)
 
{
 
	Trackdir src_trackdir = (Trackdir)current->path.node.direction;
 
	TileIndex src_tile = current->path.node.tile;
 
	DiagDirection src_exitdir = TrackdirToExitdir(src_trackdir);
 
	TileIndex dst_tile = INVALID_TILE;
 
	int i;
 
	TrackdirBits trackdirbits, ts;
 
	TransportType type = aystar->user_data[NPF_TYPE];
 
	bool override_dst_check = false;
 
	/* Initialize to 0, so we can jump out (return) somewhere an have no neighbours */
 
	aystar->num_neighbours = 0;
 
	DEBUG(npf, 4, "Expanding: (%d, %d, %d) [%d]", TileX(src_tile), TileY(src_tile), src_trackdir, src_tile);
 

	
 
	/* Find dest tile */
 
	if (IsTunnelTile(src_tile) && GetTunnelDirection(src_tile) == src_exitdir) {
 
		/* This is a tunnel. We know this tunnel is our type,
 
		 * otherwise we wouldn't have got here. It is also facing us,
 
		 * so we should skip it's body */
 
		dst_tile = GetOtherTunnelEnd(src_tile);
 
		override_dst_check = true;
 
	} else if (IsBridgeTile(src_tile) && GetBridgeRampDirection(src_tile) == src_exitdir) {
 
		dst_tile = GetOtherBridgeEnd(src_tile);
 
		override_dst_check = true;
 
	} else if (type != TRANSPORT_WATER && (IsRoadStopTile(src_tile) || IsTileDepotType(src_tile, type))) {
 
		/* This is a road station or a train or road depot. We can enter and exit
 
		 * those from one side only. Trackdirs don't support that (yet), so we'll
 
		 * do this here. */
 

	
 
		DiagDirection exitdir;
 
		/* Find out the exit direction first */
 
		if (IsRoadStopTile(src_tile)) {
 
			exitdir = GetRoadStopDir(src_tile);
 
		} else { /* Train or road depot */
 
			exitdir = GetDepotDirection(src_tile, type);
 
		}
 

	
 
		/* Let's see if were headed the right way into the depot */
 
		if (src_trackdir == DiagdirToDiagTrackdir(ReverseDiagDir(exitdir))) {
 
			/* We are headed inwards. We cannot go through the back of the depot.
 
			 * For rail, we can now reverse. Reversing for road vehicles is never
 
			 * useful, since you cannot take paths you couldn't take before
 
			 * reversing (as with rail). */
 
			if (type == TRANSPORT_RAIL) {
 
				/* We can only reverse here, so we'll not consider this direction, but
 
				 * jump ahead to the reverse direction.  It would be nicer to return
 
				 * one neighbour here (the reverse trackdir of the one we are
 
				 * considering now) and then considering that one to return the tracks
 
				 * outside of the depot. But, because the code layout is cleaner this
 
				 * way, we will just pretend we are reversed already */
 
				src_trackdir = ReverseTrackdir(src_trackdir);
 
				dst_tile = AddTileIndexDiffCWrap(src_tile, TileIndexDiffCByDiagDir(exitdir));
 
			} else {
 
				dst_tile = INVALID_TILE; /* Road vehicle heading inwards: dead end */
 
			}
 
		} else {
 
			dst_tile = AddTileIndexDiffCWrap(src_tile, TileIndexDiffCByDiagDir(exitdir));
 
		}
 
	} else {
 
		/* This a normal tile, a bridge, a tunnel exit, etc. */
 
		dst_tile = AddTileIndexDiffCWrap(src_tile, TileIndexDiffCByDiagDir(TrackdirToExitdir(src_trackdir)));
 
	}
 
	if (dst_tile == INVALID_TILE) {
 
		/* We reached the border of the map */
 
		/* TODO Nicer control flow for this */
 
		return;
 
	}
 

	
 
	/* I can't enter a tunnel entry/exit tile from a tile above the tunnel. Note
 
	 * that I can enter the tunnel from a tile below the tunnel entrance. This
 
	 * solves the problem of vehicles wanting to drive off a tunnel entrance */
 
	if (!override_dst_check) {
 
		if (IsTileType(dst_tile, MP_TUNNELBRIDGE)) {
 
			if (IsTunnel(dst_tile)) {
 
				if (GetTunnelDirection(dst_tile) != src_exitdir) return;
 
			} else {
 
				if (GetBridgeRampDirection(dst_tile) != src_exitdir) return;
 
			}
 
		}
 
	}
 

	
 
	/* check correct rail type (mono, maglev, etc) */
 
	if (type == TRANSPORT_RAIL) {
 
		RailType dst_type = GetTileRailType(dst_tile, src_trackdir);
 
		if (!HASBIT(aystar->user_data[NPF_RAILTYPES], dst_type))
 
			return;
 
	}
 

	
 
	/* Check the owner of the tile */
 
	if (!VehicleMayEnterTile(aystar->user_data[NPF_OWNER], dst_tile, TrackdirToExitdir(src_trackdir))) {
 
		return;
 
	}
 

	
 
	/* Determine available tracks */
 
	if (type != TRANSPORT_WATER && (IsRoadStopTile(dst_tile) || IsTileDepotType(dst_tile, type))){
 
		/* Road stations and road and train depots return 0 on GTTS, so we have to do this by hand... */
 
		DiagDirection exitdir;
 
		if (IsRoadStopTile(dst_tile)) {
 
			exitdir = GetRoadStopDir(dst_tile);
 
		} else { /* Road or train depot */
 
			exitdir = GetDepotDirection(dst_tile, type);
 
		}
 
		/* Find the trackdirs that are available for a depot or station with this
 
		 * orientation. They are only "inwards", since we are reaching this tile
 
		 * from some other tile. This prevents vehicles driving into depots from
 
		 * the back */
 
		ts = TrackdirToTrackdirBits(DiagdirToDiagTrackdir(ReverseDiagDir(exitdir)));
 
	} else {
 
		ts = GetTileTrackStatus(dst_tile, type);
 
	}
 
	trackdirbits = ts & TRACKDIR_BIT_MASK; /* Filter out signal status and the unused bits */
 

	
 
	DEBUG(npf, 4, "Next node: (%d, %d) [%d], possible trackdirs: 0x%X", TileX(dst_tile), TileY(dst_tile), dst_tile, trackdirbits);
 
	/* Select only trackdirs we can reach from our current trackdir */
 
	trackdirbits &= TrackdirReachesTrackdirs(src_trackdir);
 
	if (_patches.forbid_90_deg && (type == TRANSPORT_RAIL || type == TRANSPORT_WATER)) /* Filter out trackdirs that would make 90 deg turns for trains */
 
		trackdirbits &= ~TrackdirCrossesTrackdirs(src_trackdir);
 

	
 
	DEBUG(npf, 6, "After filtering: (%d, %d), possible trackdirs: 0x%X", TileX(dst_tile), TileY(dst_tile), trackdirbits);
 

	
 
	i = 0;
 
	/* Enumerate possible track */
 
	while (trackdirbits != 0) {
 
		Trackdir dst_trackdir;
 
		dst_trackdir =  FindFirstBit2x64(trackdirbits);
 
		trackdirbits = KillFirstBit2x64(trackdirbits);
 
		DEBUG(npf, 5, "Expanded into trackdir: %d, remaining trackdirs: 0x%X", dst_trackdir, trackdirbits);
 

	
 
		/* Check for oneway signal against us */
 
		if (IsTileType(dst_tile, MP_RAILWAY) && GetRailTileType(dst_tile) == RAIL_TILE_SIGNALS) {
 
			if (HasSignalOnTrackdir(dst_tile, ReverseTrackdir(dst_trackdir)) && !HasSignalOnTrackdir(dst_tile, dst_trackdir))
 
				// if one way signal not pointing towards us, stop going in this direction.
 
				break;
 
		}
 
		{
 
			/* We've found ourselves a neighbour :-) */
 
			AyStarNode* neighbour = &aystar->neighbours[i];
 
			neighbour->tile = dst_tile;
 
			neighbour->direction = dst_trackdir;
 
			/* Save user data */
 
			neighbour->user_data[NPF_NODE_FLAGS] = current->path.node.user_data[NPF_NODE_FLAGS];
 
			NPFFillTrackdirChoice(neighbour, current);
 
		}
 
		i++;
 
	}
 
	aystar->num_neighbours = i;
 
}
 

	
 
/*
 
 * Plan a route to the specified target (which is checked by target_proc),
 
 * from start1 and if not NULL, from start2 as well. The type of transport we
 
 * are checking is in type. reverse_penalty is applied to all routes that
 
 * originate from the second start node.
 
 * When we are looking for one specific target (optionally multiple tiles), we
 
 * should use a good heuristic to perform aystar search. When we search for
 
 * multiple targets that are spread around, we should perform a breadth first
 
 * search by specifiying CalcZero as our heuristic.
 
 */
 
static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start2, NPFFindStationOrTileData* target, AyStar_EndNodeCheck target_proc, AyStar_CalculateH heuristic_proc, TransportType type, Owner owner, RailTypeMask railtypes, uint reverse_penalty)
 
{
 
	int r;
 
	NPFFoundTargetData result;
 

	
 
	/* Initialize procs */
 
	_npf_aystar.CalculateH = heuristic_proc;
 
	_npf_aystar.EndNodeCheck = target_proc;
 
	_npf_aystar.FoundEndNode = NPFSaveTargetData;
 
	_npf_aystar.GetNeighbours = NPFFollowTrack;
 
	switch (type) {
 
		default: NOT_REACHED();
 
		case TRANSPORT_RAIL:  _npf_aystar.CalculateG = NPFRailPathCost;  break;
 
		case TRANSPORT_ROAD:  _npf_aystar.CalculateG = NPFRoadPathCost;  break;
 
		case TRANSPORT_WATER: _npf_aystar.CalculateG = NPFWaterPathCost; break;
 
	}
 

	
 
	/* Initialize Start Node(s) */
 
	start1->user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 
	start1->user_data[NPF_NODE_FLAGS] = 0;
 
	_npf_aystar.addstart(&_npf_aystar, start1, 0);
 
	if (start2) {
 
		start2->user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 
		start2->user_data[NPF_NODE_FLAGS] = 0;
 
		NPFSetFlag(start2, NPF_FLAG_REVERSE, true);
 
		_npf_aystar.addstart(&_npf_aystar, start2, reverse_penalty);
 
	}
 

	
 
	/* Initialize result */
 
	result.best_bird_dist = (uint)-1;
 
	result.best_path_dist = (uint)-1;
 
	result.best_trackdir = INVALID_TRACKDIR;
 
	_npf_aystar.user_path = &result;
 

	
 
	/* Initialize target */
 
	_npf_aystar.user_target = target;
 

	
 
	/* Initialize user_data */
 
	_npf_aystar.user_data[NPF_TYPE] = type;
 
	_npf_aystar.user_data[NPF_OWNER] = owner;
 
	_npf_aystar.user_data[NPF_RAILTYPES] = railtypes;
 

	
 
	/* GO! */
 
	r = AyStarMain_Main(&_npf_aystar);
 
	assert(r != AYSTAR_STILL_BUSY);
 

	
 
	if (result.best_bird_dist != 0) {
 
		if (target != NULL) {
 
			DEBUG(npf, 1, "Could not find route to tile 0x%X from 0x%X.", target->dest_coords, start1->tile);
 
		} else {
 
			/* Assumption: target == NULL, so we are looking for a depot */
 
			DEBUG(npf, 1, "Could not find route to a depot from tile 0x%X.", start1->tile);
 
		}
 

	
 
	}
 
	return result;
 
}
 

	
 
NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypeMask railtypes)
 
{
 
	AyStarNode start1;
 
	AyStarNode start2;
 

	
 
	start1.tile = tile1;
 
	start2.tile = tile2;
 
	/* We set this in case the target is also the start tile, we will just
 
	 * return a not found then */
 
	start1.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 
	start1.direction = trackdir1;
 
	start2.direction = trackdir2;
 
	start2.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 

	
 
	return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), target, NPFFindStationOrTile, NPFCalcStationOrTileHeuristic, type, owner, railtypes, 0);
 
}
 

	
 
NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypeMask railtypes)
 
{
 
	return NPFRouteToStationOrTileTwoWay(tile, trackdir, INVALID_TILE, 0, target, type, owner, railtypes);
 
}
 

	
 
NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, TransportType type, Owner owner, RailTypeMask railtypes, uint reverse_penalty)
 
{
 
	AyStarNode start1;
 
	AyStarNode start2;
 

	
 
	start1.tile = tile1;
 
	start2.tile = tile2;
 
	/* We set this in case the target is also the start tile, we will just
 
	 * return a not found then */
 
	start1.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 
	start1.direction = trackdir1;
 
	start2.direction = trackdir2;
 
	start2.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 

	
 
	/* perform a breadth first search. Target is NULL,
 
	 * since we are just looking for any depot...*/
 
	return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), NULL, NPFFindDepot, NPFCalcZero, type, owner, railtypes, reverse_penalty);
 
}
 

	
 
NPFFoundTargetData NPFRouteToDepotBreadthFirst(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailTypeMask railtypes)
 
{
 
	return NPFRouteToDepotBreadthFirstTwoWay(tile, trackdir, INVALID_TILE, 0, type, owner, railtypes, 0);
 
}
 

	
 
NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailTypeMask railtypes)
 
{
 
	/* Okay, what we're gonna do. First, we look at all depots, calculate
 
	 * the manhatten distance to get to each depot. We then sort them by
 
	 * distance. We start by trying to plan a route to the closest, then
 
	 * the next closest, etc. We stop when the best route we have found so
 
	 * far, is shorter than the manhattan distance. This will obviously
 
	 * always find the closest depot. It will probably be most efficient
 
	 * for ships, since the heuristic will not be to far off then. I hope.
 
	 */
 
	Queue depots;
 
	int r;
 
	NPFFoundTargetData best_result = {(uint)-1, (uint)-1, INVALID_TRACKDIR, {INVALID_TILE, 0, {0, 0}}};
 
	NPFFoundTargetData result;
 
	NPFFindStationOrTileData target;
 
	AyStarNode start;
 
	Depot* current;
 
	Depot *depot;
 

	
 
	init_InsSort(&depots);
 
	/* Okay, let's find all depots that we can use first */
 
	FOR_ALL_DEPOTS(depot) {
 
		/* Check if this is really a valid depot, it is of the needed type and
 
		 * owner */
 
		if (IsTileDepotType(depot->xy, type) && IsTileOwner(depot->xy, owner))
 
			/* If so, let's add it to the queue, sorted by distance */
 
			depots.push(&depots, depot, DistanceManhattan(tile, depot->xy));
 
	}
 

	
 
	/* Now, let's initialise the aystar */
 

	
 
	/* Initialize procs */
 
	_npf_aystar.CalculateH = NPFCalcStationOrTileHeuristic;
 
	_npf_aystar.EndNodeCheck = NPFFindStationOrTile;
 
	_npf_aystar.FoundEndNode = NPFSaveTargetData;
 
	_npf_aystar.GetNeighbours = NPFFollowTrack;
 
	switch (type) {
 
		default: NOT_REACHED();
 
		case TRANSPORT_RAIL:  _npf_aystar.CalculateG = NPFRailPathCost;  break;
 
		case TRANSPORT_ROAD:  _npf_aystar.CalculateG = NPFRoadPathCost;  break;
 
		case TRANSPORT_WATER: _npf_aystar.CalculateG = NPFWaterPathCost; break;
 
	}
 

	
 
	/* Initialize target */
 
	target.station_index = INVALID_STATION; /* We will initialize dest_coords inside the loop below */
 
	_npf_aystar.user_target = &target;
 

	
 
	/* Initialize user_data */
 
	_npf_aystar.user_data[NPF_TYPE] = type;
 
	_npf_aystar.user_data[NPF_OWNER] = owner;
 

	
 
	/* Initialize Start Node */
 
	start.tile = tile;
 
	start.direction = trackdir; /* We will initialize user_data inside the loop below */
 

	
 
	/* Initialize Result */
 
	_npf_aystar.user_path = &result;
 
	best_result.best_path_dist = (uint)-1;
 
	best_result.best_bird_dist = (uint)-1;
 

	
 
	/* Just iterate the depots in order of increasing distance */
 
	while ((current = depots.pop(&depots))) {
 
		/* Check to see if we already have a path shorter than this
 
		 * depot's manhattan distance. HACK: We call DistanceManhattan
 
		 * again, we should probably modify the queue to give us that
 
		 * value... */
 
		if ( DistanceManhattan(tile, current->xy * NPF_TILE_LENGTH) > best_result.best_path_dist)
 
			break;
 

	
 
		/* Initialize Start Node */
 
		/* We set this in case the target is also the start tile, we will just
 
		 * return a not found then */
 
		start.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 
		start.user_data[NPF_NODE_FLAGS] = 0;
 
		_npf_aystar.addstart(&_npf_aystar, &start, 0);
 

	
 
		/* Initialize result */
 
		result.best_bird_dist = (uint)-1;
 
		result.best_path_dist = (uint)-1;
 
		result.best_trackdir = INVALID_TRACKDIR;
 

	
 
		/* Initialize target */
 
		target.dest_coords = current->xy;
 

	
 
		/* GO! */
 
		r = AyStarMain_Main(&_npf_aystar);
 
		assert(r != AYSTAR_STILL_BUSY);
 

	
 
		/* This depot is closer */
 
		if (result.best_path_dist < best_result.best_path_dist)
 
			best_result = result;
 
	}
 
	if (result.best_bird_dist != 0) {
 
		DEBUG(npf, 1, "Could not find route to any depot from tile 0x%X.", tile);
 
	}
 
	return best_result;
 
}
 

	
 
void InitializeNPF(void)
 
{
 
	init_AyStar(&_npf_aystar, NPFHash, NPF_HASH_SIZE);
 
	_npf_aystar.loops_per_tick = 0;
 
	_npf_aystar.max_path_cost = 0;
 
	//_npf_aystar.max_search_nodes = 0;
 
	/* We will limit the number of nodes for now, until we have a better
 
	 * solution to really fix performance */
 
	_npf_aystar.max_search_nodes = _patches.npf_max_search_nodes;
 
}
 

	
 
void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v)
 
{
 
	/* Ships don't really reach their stations, but the tile in front. So don't
 
	 * save the station id for ships. For roadvehs we don't store it either,
 
	 * because multistop depends on vehicles actually reaching the exact
 
	 * dest_tile, not just any stop of that station.
 
	 * So only for train orders to stations we fill fstd->station_index, for all
 
	 * others only dest_coords */
 
	if (v->current_order.type == OT_GOTO_STATION && v->type == VEH_Train) {
 
		fstd->station_index = v->current_order.dest;
 
		/* Let's take the closest tile of the station as our target for trains */
 
		fstd->dest_coords = CalcClosestStationTile(v->current_order.dest, v->tile);
 
	} else {
 
		fstd->dest_coords = v->dest_tile;
 
		fstd->station_index = INVALID_STATION;
 
	}
 
}
src/oldloader.c
Show inline comments
 
deleted file
src/oldloader.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "station_map.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "town.h"
 
#include "industry.h"
 
#include "station.h"
 
#include "economy.h"
 
#include "player.h"
 
#include "engine.h"
 
#include "vehicle.h"
 
#include "signs.h"
 
#include "debug.h"
 
#include "depot.h"
 
#include "network/network.h"
 
#include "ai/ai.h"
 
#include "date.h"
 

	
 
enum {
 
	HEADER_SIZE = 49,
 
	BUFFER_SIZE = 4096,
 

	
 
	OLD_MAP_SIZE = 256 * 256
 
};
 

	
 
typedef struct LoadgameState {
 
	FILE *file;
 

	
 
	uint chunk_size;
 

	
 
	bool decoding;
 
	byte decode_char;
 

	
 
	uint buffer_count;
 
	uint buffer_cur;
 
	byte buffer[BUFFER_SIZE];
 

	
 
	uint total_read;
 
	bool failed;
 
} LoadgameState;
 

	
 
/* OldChunk-Type */
 
typedef enum OldChunkTypes {
 
	OC_SIMPLE    = 0,
 
	OC_NULL      = 1,
 
	OC_CHUNK     = 2,
 
	OC_ASSERT    = 3,
 
	/* 8 bytes allocated (256 max) */
 

	
 
	OC_VAR_I8    = 1 << 8,
 
	OC_VAR_U8    = 2 << 8,
 
	OC_VAR_I16   = 3 << 8,
 
	OC_VAR_U16   = 4 << 8,
 
	OC_VAR_I32   = 5 << 8,
 
	OC_VAR_U32   = 6 << 8,
 
	OC_VAR_I64   = 7 << 8,
 
	/* 8 bytes allocated (256 max) */
 

	
 
	OC_FILE_I8   = 1 << 16,
 
	OC_FILE_U8   = 2 << 16,
 
	OC_FILE_I16  = 3 << 16,
 
	OC_FILE_U16  = 4 << 16,
 
	OC_FILE_I32  = 5 << 16,
 
	OC_FILE_U32  = 6 << 16,
 
	/* 8 bytes allocated (256 max) */
 

	
 
	OC_INT8      = OC_VAR_I8   | OC_FILE_I8,
 
	OC_UINT8     = OC_VAR_U8   | OC_FILE_U8,
 
	OC_INT16     = OC_VAR_I16  | OC_FILE_I16,
 
	OC_UINT16    = OC_VAR_U16  | OC_FILE_U16,
 
	OC_INT32     = OC_VAR_I32  | OC_FILE_I32,
 
	OC_UINT32    = OC_VAR_U32  | OC_FILE_U32,
 

	
 
	OC_TILE      = OC_VAR_U32  | OC_FILE_U16,
 

	
 
	OC_END       = 0 ///< End of the whole chunk, all 32bits set to zero
 
} OldChunkType;
 

	
 
typedef bool OldChunkProc(LoadgameState *ls, int num);
 

	
 
typedef struct OldChunks {
 
	OldChunkType type;   ///< Type of field
 
	uint32 amount;       ///< Amount of fields
 

	
 
	void *ptr;           ///< Pointer where to save the data (may only be set if offset is 0)
 
	uint offset;         ///< Offset from basepointer (may only be set if ptr is NULL)
 
	OldChunkProc *proc;  ///< Pointer to function that is called with OC_CHUNK
 
} OldChunks;
 

	
 
/* If it fails, check lines above.. */
 
assert_compile(sizeof(TileIndex) == 4);
 

	
 
static uint32 _bump_assert_value;
 
static bool   _read_ttdpatch_flags;
 

	
 
static OldChunkType GetOldChunkType(OldChunkType type)     {return GB(type, 0, 8);}
 
static OldChunkType GetOldChunkVarType(OldChunkType type)  {return GB(type, 8, 8) << 8;}
 
static OldChunkType GetOldChunkFileType(OldChunkType type) {return GB(type, 16, 8) << 16;}
 

	
 
static inline byte CalcOldVarLen(OldChunkType type)
 
{
 
	static const byte type_mem_size[] = {0, 1, 1, 2, 2, 4, 4, 8};
 
	byte length = GB(type, 8, 8);
 
	assert(length != 0 && length < lengthof(type_mem_size));
 
	return type_mem_size[length];
 
}
 

	
 
/**
 
 *
 
 * Reads a byte from a file (do not call yourself, use ReadByte())
 
 *
 
 */
 
static byte ReadByteFromFile(LoadgameState *ls)
 
{
 
	/* To avoid slow reads, we read BUFFER_SIZE of bytes per time
 
	and just return a byte per time */
 
	if (ls->buffer_cur >= ls->buffer_count) {
 
		/* Read some new bytes from the file */
 
		int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file);
 

	
 
		/* We tried to read, but there is nothing in the file anymore.. */
 
		if (count == 0) {
 
			DEBUG(oldloader, 0, "Read past end of file, loading failed");
 
			ls->failed = true;
 
		}
 

	
 
		ls->buffer_count = count;
 
		ls->buffer_cur   = 0;
 
	}
 

	
 
	return ls->buffer[ls->buffer_cur++];
 
}
 

	
 
/**
 
 *
 
 * Reads a byte from the buffer and decompress if needed
 
 *
 
 */
 
static byte ReadByte(LoadgameState *ls)
 
{
 
	/* Old savegames have a nice compression algorithm (RLE)
 
	which means that we have a chunk, which starts with a length
 
	byte. If that byte is negative, we have to repeat the next byte
 
	that many times (+1). Else, we need to read that amount of bytes.
 
	Works pretty good if you have many zero's behind eachother */
 

	
 
	if (ls->chunk_size == 0) {
 
		/* Read new chunk */
 
		int8 new_byte = ReadByteFromFile(ls);
 

	
 
		if (new_byte < 0) {
 
			/* Repeat next char for new_byte times */
 
			ls->decoding    = true;
 
			ls->decode_char = ReadByteFromFile(ls);
 
			ls->chunk_size  = -new_byte + 1;
 
		} else {
 
			ls->decoding    = false;
 
			ls->chunk_size  = new_byte + 1;
 
		}
 
	}
 

	
 
	ls->total_read++;
 
	ls->chunk_size--;
 

	
 
	return ls->decoding ? ls->decode_char : ReadByteFromFile(ls);
 
}
 

	
 
static inline uint16 ReadUint16(LoadgameState *ls)
 
{
 
	byte x = ReadByte(ls);
 
	return x | ReadByte(ls) << 8;
 
}
 

	
 
static inline uint32 ReadUint32(LoadgameState *ls)
 
{
 
	uint16 x = ReadUint16(ls);
 
	return x | ReadUint16(ls) << 16;
 
}
 

	
 
/**
 
 *
 
 * Loads a chunk from the old savegame
 
 *
 
 */
 
static bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
 
{
 
	const OldChunks *chunk = chunks;
 
	byte *base_ptr = base;
 

	
 
	while (chunk->type != OC_END) {
 
		byte* ptr = chunk->ptr;
 
		uint i;
 

	
 
		for (i = 0; i < chunk->amount; i++) {
 
			if (ls->failed) return false;
 

	
 
			/* Handle simple types */
 
			if (GetOldChunkType(chunk->type) != 0) {
 
				switch (GetOldChunkType(chunk->type)) {
 
					/* Just read the byte and forget about it */
 
					case OC_NULL: ReadByte(ls); break;
 

	
 
					case OC_CHUNK:
 
						/* Call function, with 'i' as parameter to tell which item we
 
						 * are going to read */
 
						if (!chunk->proc(ls, i)) return false;
 
						break;
 

	
 
					case OC_ASSERT:
 
						DEBUG(oldloader, 4, "Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value);
 
						if (ls->total_read != chunk->offset + _bump_assert_value) ls->failed = true;
 
					default: break;
 
				}
 
			} else {
 
				uint32 res = 0;
 

	
 
				/* Reading from the file: bits 16 to 23 have the FILE type */
 
				switch (GetOldChunkFileType(chunk->type)) {
 
					case OC_FILE_I8:  res = (int8)ReadByte(ls); break;
 
					case OC_FILE_U8:  res = ReadByte(ls); break;
 
					case OC_FILE_I16: res = (int16)ReadUint16(ls); break;
 
					case OC_FILE_U16: res = ReadUint16(ls); break;
 
					case OC_FILE_I32: res = (int32)ReadUint32(ls); break;
 
					case OC_FILE_U32: res = ReadUint32(ls); break;
 
					default: NOT_REACHED();
 
				}
 

	
 
				/* Sanity check */
 
				assert(base_ptr != NULL || chunk->ptr != NULL);
 

	
 
				/* Writing to the var: bits 8 to 15 have the VAR type */
 
				if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset;
 

	
 
				/* Write the data */
 
				switch (GetOldChunkVarType(chunk->type)) {
 
					case OC_VAR_I8: *(int8  *)ptr = GB(res, 0, 8); break;
 
					case OC_VAR_U8: *(uint8 *)ptr = GB(res, 0, 8); break;
 
					case OC_VAR_I16:*(int16 *)ptr = GB(res, 0, 16); break;
 
					case OC_VAR_U16:*(uint16*)ptr = GB(res, 0, 16); break;
 
					case OC_VAR_I32:*(int32 *)ptr = res; break;
 
					case OC_VAR_U32:*(uint32*)ptr = res; break;
 
					case OC_VAR_I64:*(int64 *)ptr = res; break;
 
					default: NOT_REACHED();
 
				}
 

	
 
				/* Increase pointer base for arrays when looping */
 
				if (chunk->amount > 1 && chunk->ptr != NULL) ptr += CalcOldVarLen(chunk->type);
 
			}
 
		}
 

	
 
		chunk++;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 *
 
 * Initialize some data before reading
 
 *
 
 */
 
static void InitLoading(LoadgameState *ls)
 
{
 
	ls->chunk_size   = 0;
 
	ls->total_read   = 0;
 
	ls->failed       = false;
 

	
 
	ls->decoding     = false;
 
	ls->decode_char  = 0;
 

	
 
	ls->buffer_cur   = 0;
 
	ls->buffer_count = 0;
 
	memset(ls->buffer, 0, BUFFER_SIZE);
 

	
 
	_bump_assert_value = 0;
 

	
 
	_read_ttdpatch_flags = false;
 
}
 

	
 

	
 
/*
 
 * Begin -- Stuff to fix the savegames to be OpenTTD compatible
 
 */
 

	
 
extern uint32 GetOldTownName(uint32 townnameparts, byte old_town_name_type);
 

	
 
static void FixOldTowns(void)
 
{
 
	Town *town;
 

	
 
	/* Convert town-names if needed */
 
	FOR_ALL_TOWNS(town) {
 
		if (IS_INT_INSIDE(town->townnametype, 0x20C1, 0x20C3)) {
 
			town->townnametype = SPECSTR_TOWNNAME_ENGLISH + _opt.town_name;
 
			town->townnameparts = GetOldTownName(town->townnameparts, _opt.town_name);
 
		}
 
	}
 
}
 

	
 
static void FixOldStations(void)
 
{
 
	Station *st;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		/* Check if we need to swap width and height for the station */
 
		if (st->train_tile != 0 && GetRailStationAxis(st->train_tile) != AXIS_X) {
 
			swap_byte(&st->trainst_w, &st->trainst_h);
 
		}
 

	
 
		/* Check if there is a bus or truck station, and convert to new format */
 
		if (st->bus_tile_obsolete != 0) {
 
			st->bus_stops = AllocateRoadStop();
 
			st->bus_stops->xy = st->bus_tile_obsolete;
 
			st->bus_stops->used = true;
 
			st->bus_stops->status = 3;
 
			st->bus_stops->station = st->index;
 
			st->bus_stops->next = NULL;
 
			st->bus_stops->prev = NULL;
 
			st->bus_stops->num_vehicles = 0;
 
		}
 

	
 
		if (st->lorry_tile_obsolete != 0) {
 
			st->truck_stops = AllocateRoadStop();
 
			st->truck_stops->xy = st->lorry_tile_obsolete;
 
			st->truck_stops->used = true;
 
			st->truck_stops->status = 3;
 
			st->truck_stops->station = st->index;
 
			st->truck_stops->next = NULL;
 
			st->truck_stops->prev = NULL;
 
			st->truck_stops->num_vehicles = 0;
 
		}
 
	}
 
}
 

	
 
static void FixOldVehicles(void)
 
{
 
	/* Check for shared orders, and link them correctly */
 
	Vehicle* v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		Vehicle *u;
 

	
 
		FOR_ALL_VEHICLES_FROM(u, v->index + 1) {
 
			/* If a vehicle has the same orders, add the link to eachother
 
			 * in both vehicles */
 
			if (v->orders == u->orders) {
 
				v->next_shared = u;
 
				u->prev_shared = v;
 
				break;
 
			}
 
		}
 
	}
 
}
 

	
 
/*
 
 * End -- Stuff to fix the savegames to be OpenTTD compatible
 
 */
 

	
 

	
 
/* Help:
 
 *  - OCL_SVAR: load 'type' to offset 'offset' in a struct of type 'base', which must also
 
 *       be given via base in LoadChunk() as real pointer
 
 *  - OCL_VAR: load 'type' to a global var
 
 *  - OCL_END: every struct must end with this
 
 *  - OCL_NULL: read 'amount' of bytes and send them to /dev/null or something
 
 *  - OCL_CHUNK: load an other proc to load a part of the savegame, 'amount' times
 
 *  - OCL_ASSERT: to check if we are really at the place we expect to be.. because old savegames are too binary to be sure ;)
 
 */
 
#define OCL_SVAR(type, base, offset)         { type,          1, NULL,    (uint)offsetof(base, offset), NULL }
 
#define OCL_VAR(type, amount, pointer)       { type,     amount, pointer, 0,                      NULL }
 
#define OCL_END()                                   { OC_END,        0, NULL,    0,                      NULL }
 
#define OCL_NULL(amount)                            { OC_NULL,  amount, NULL,    0,                      NULL }
 
#define OCL_CHUNK(amount, proc)                     { OC_CHUNK, amount, NULL,    0,                      proc }
 
#define OCL_ASSERT(size)                            { OC_ASSERT,     1, NULL, size,                      NULL }
 

	
 
/* The savegames has some hard-coded pointers, because it always enters the same
 
    piece of memory.. we don't.. so we need to remap ;)
 
   Old Towns are 94 bytes big
 
   Old Orders are 2 bytes big */
 
#define REMAP_TOWN_IDX(x) ((x) - (0x0459154 - 0x0458EF0)) / 94
 
#define REMAP_ORDER_IDX(x) ((x) - (0x045AB08 - 0x0458EF0)) / 2
 

	
 
extern TileIndex _animated_tile_list[256];
 
extern char _name_array[512][32];
 

	
 
static byte   _old_vehicle_multiplier;
 
static uint8  _old_map3[OLD_MAP_SIZE * 2];
 
static bool   _new_ttdpatch_format;
 
static uint32 _old_town_index;
 
static uint16 _old_string_id;
 
static uint16 _old_string_id_2;
 

	
 
static void ReadTTDPatchFlags(void)
 
{
 
	int i;
 

	
 
	if (_read_ttdpatch_flags) return;
 

	
 
	_read_ttdpatch_flags = true;
 

	
 
	/* TTDPatch misuses _old_map3 for flags.. read them! */
 
	_old_vehicle_multiplier = _old_map3[0];
 
	/* Somehow.... there was an error in some savegames, so 0 becomes 1
 
	and 1 becomes 2. The rest of the values are okay */
 
	if (_old_vehicle_multiplier < 2) _old_vehicle_multiplier++;
 

	
 
	/* TTDPatch incraeses the Vehicle-part in the middle of the game,
 
	so if the multipler is anything else but 1, the assert fails..
 
	bump the assert value so it doesn't!
 
	(1 multipler == 850 vehicles
 
	1 vehicle   == 128 bytes */
 
	_bump_assert_value = (_old_vehicle_multiplier - 1) * 850 * 128;
 

	
 
	/* Check if we have a modern TTDPatch savegame (has extra data all around) */
 
	_new_ttdpatch_format = (memcmp(&_old_map3[0x1FFFA], "TTDp", 4) == 0);
 

	
 
	/* Clean the misused places */
 
	for (i = 0;       i < 17;      i++) _old_map3[i] = 0;
 
	for (i = 0x1FE00; i < 0x20000; i++) _old_map3[i] = 0;
 

	
 
	if (_new_ttdpatch_format) DEBUG(oldloader, 1, "Found TTDPatch game");
 

	
 
	DEBUG(oldloader, 1, "Vehicle-multiplier is set to %d (%d vehicles)", _old_vehicle_multiplier, _old_vehicle_multiplier * 850);
 
}
 

	
 
static const OldChunks town_chunk[] = {
 
	OCL_SVAR(   OC_TILE, Town, xy ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, population ),
 
	OCL_SVAR( OC_UINT16, Town, townnametype ),
 
	OCL_SVAR( OC_UINT32, Town, townnameparts ),
 
	OCL_SVAR(  OC_UINT8, Town, grow_counter ),
 
	OCL_NULL( 1 ),         // sort_index,        no longer in use
 
	OCL_NULL( 4 ),         // sign-coordinates,  no longer in use
 
	OCL_NULL( 2 ),         // namewidth,         no longer in use
 
	OCL_SVAR( OC_UINT16, Town, flags12 ),
 
	OCL_NULL( 10 ),        // radius,            no longer in use
 

	
 
	OCL_SVAR( OC_UINT16, Town, ratings[0] ),
 
	OCL_SVAR( OC_UINT16, Town, ratings[1] ),
 
	OCL_SVAR( OC_UINT16, Town, ratings[2] ),
 
	OCL_SVAR( OC_UINT16, Town, ratings[3] ),
 
	OCL_SVAR( OC_UINT16, Town, ratings[4] ),
 
	OCL_SVAR( OC_UINT16, Town, ratings[5] ),
 
	OCL_SVAR( OC_UINT16, Town, ratings[6] ),
 
	OCL_SVAR( OC_UINT16, Town, ratings[7] ),
 

	
 
	/* XXX - This is pretty odd.. we read 32bit, but only write 8bit.. sure there is
 
	nothing changed?? */
 
	OCL_SVAR( OC_FILE_U32 | OC_VAR_U8, Town, have_ratings ),
 
	OCL_SVAR( OC_FILE_U32 | OC_VAR_U8, Town, statues ),
 
	OCL_SVAR( OC_UINT16, Town, num_houses ),
 
	OCL_SVAR(  OC_UINT8, Town, time_until_rebuild ),
 
	OCL_SVAR(  OC_UINT8, Town, growth_rate ),
 

	
 
	OCL_SVAR( OC_UINT16, Town, new_max_pass ),
 
	OCL_SVAR( OC_UINT16, Town, new_max_mail ),
 
	OCL_SVAR( OC_UINT16, Town, new_act_pass ),
 
	OCL_SVAR( OC_UINT16, Town, new_act_mail ),
 
	OCL_SVAR( OC_UINT16, Town, max_pass ),
 
	OCL_SVAR( OC_UINT16, Town, max_mail ),
 
	OCL_SVAR( OC_UINT16, Town, act_pass ),
 
	OCL_SVAR( OC_UINT16, Town, act_mail ),
 

	
 
	OCL_SVAR(  OC_UINT8, Town, pct_pass_transported ),
 
	OCL_SVAR(  OC_UINT8, Town, pct_mail_transported ),
 

	
 
	OCL_SVAR( OC_UINT16, Town, new_act_food ),
 
	OCL_SVAR( OC_UINT16, Town, new_act_water ),
 
	OCL_SVAR( OC_UINT16, Town, act_food ),
 
	OCL_SVAR( OC_UINT16, Town, act_water ),
 

	
 
	OCL_SVAR(  OC_UINT8, Town, road_build_months ),
 
	OCL_SVAR(  OC_UINT8, Town, fund_buildings_months ),
 

	
 
	OCL_NULL( 8 ),         // some junk at the end of the record
 

	
 
	OCL_END()
 
};
 
static bool LoadOldTown(LoadgameState *ls, int num)
 
{
 
	if (!AddBlockIfNeeded(&_Town_pool, num))
 
		error("Towns: failed loading savegame: too many towns");
 

	
 
	return LoadChunk(ls, GetTown(num), town_chunk);
 
}
 

	
 
static uint16 _old_order;
 
static const OldChunks order_chunk[] = {
 
	OCL_VAR ( OC_UINT16,   1, &_old_order ),
 
	OCL_END()
 
};
 

	
 
static bool LoadOldOrder(LoadgameState *ls, int num)
 
{
 
	if (!AddBlockIfNeeded(&_Order_pool, num))
 
		error("Orders: failed loading savegame: too many orders");
 

	
 
	if (!LoadChunk(ls, NULL, order_chunk)) return false;
 

	
 
	AssignOrder(GetOrder(num), UnpackOldOrder(_old_order));
 

	
 
	/* Relink the orders to eachother (in TTD(Patch) the orders for one
 
	vehicle are behind eachother, with OT_NOTHING as indication that
 
	it is the last order */
 
	if (num > 0 && GetOrder(num)->type != OT_NOTHING)
 
		GetOrder(num - 1)->next = GetOrder(num);
 

	
 
	return true;
 
}
 

	
 
static const OldChunks depot_chunk[] = {
 
	OCL_SVAR(   OC_TILE, Depot, xy ),
 
	OCL_VAR ( OC_UINT32,   1, &_old_town_index ),
 
	OCL_END()
 
};
 

	
 
static bool LoadOldDepot(LoadgameState *ls, int num)
 
{
 
	if (!AddBlockIfNeeded(&_Depot_pool, num))
 
		error("Depots: failed loading savegame: too many depots");
 

	
 
	if (!LoadChunk(ls, GetDepot(num), depot_chunk)) return false;
 

	
 
	if (IsValidDepot(GetDepot(num))) {
 
		GetDepot(num)->town_index = REMAP_TOWN_IDX(_old_town_index);
 
	}
 

	
 
	return true;
 
}
 

	
 
static int32 _old_price;
 
static uint16 _old_price_frac;
 
static const OldChunks price_chunk[] = {
 
	OCL_VAR (  OC_INT32,   1, &_old_price ),
 
	OCL_VAR ( OC_UINT16,   1, &_old_price_frac ),
 
	OCL_END()
 
};
 

	
 
static bool LoadOldPrice(LoadgameState *ls, int num)
 
{
 
	if (!LoadChunk(ls, NULL, price_chunk)) return false;
 

	
 
	/* We use a struct to store the prices, but they are ints in a row..
 
	so just access the struct as an array of int32's */
 
	((int32*)&_price)[num] = _old_price;
 
	_price_frac[num] = _old_price_frac;
 

	
 
	return true;
 
}
 

	
 
static const OldChunks cargo_payment_rate_chunk[] = {
 
	OCL_VAR (  OC_INT32,   1, &_old_price ),
 
	OCL_VAR ( OC_UINT16,   1, &_old_price_frac ),
 

	
 
	OCL_NULL( 2 ),         // Junk
 
	OCL_END()
 
};
 

	
 
static bool LoadOldCargoPaymentRate(LoadgameState *ls, int num)
 
{
 
	if (!LoadChunk(ls, NULL, cargo_payment_rate_chunk)) return false;
 

	
 
	_cargo_payment_rates[num] = -_old_price;
 
	_cargo_payment_rates_frac[num] = _old_price_frac;
 

	
 
	return true;
 
}
 

	
 
static uint8 _old_platforms;
 
static uint _current_station_id;
 

	
 
static const OldChunks goods_chunk[] = {
 
	OCL_SVAR( OC_UINT16, GoodsEntry, waiting_acceptance ),
 
	OCL_SVAR(  OC_UINT8, GoodsEntry, days_since_pickup ),
 
	OCL_SVAR(  OC_UINT8, GoodsEntry, rating ),
 
	OCL_SVAR(  OC_FILE_U8 | OC_VAR_U16, GoodsEntry, enroute_from ),
 
	OCL_SVAR(  OC_UINT8, GoodsEntry, enroute_time ),
 
	OCL_SVAR(  OC_UINT8, GoodsEntry, last_speed ),
 
	OCL_SVAR(  OC_UINT8, GoodsEntry, last_age ),
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldGood(LoadgameState *ls, int num)
 
{
 
	Station *st = GetStation(_current_station_id);
 
	return LoadChunk(ls, &st->goods[num], goods_chunk);
 
}
 

	
 
static const OldChunks station_chunk[] = {
 
	OCL_SVAR(   OC_TILE, Station, xy ),
 
	OCL_VAR ( OC_UINT32,   1, &_old_town_index ),
 

	
 
	OCL_SVAR(   OC_TILE, Station, bus_tile_obsolete ),
 
	OCL_SVAR(   OC_TILE, Station, lorry_tile_obsolete ),
 
	OCL_SVAR(   OC_TILE, Station, train_tile ),
 
	OCL_SVAR(   OC_TILE, Station, airport_tile ),
 
	OCL_SVAR(   OC_TILE, Station, dock_tile ),
 

	
 
	OCL_VAR (  OC_UINT8,   1, &_old_platforms ),
 

	
 
	OCL_NULL( 1 ),         // sort-index, no longer in use
 
	OCL_NULL( 2 ),         // sign-width, no longer in use
 

	
 
	OCL_VAR ( OC_UINT16,   1, &_old_string_id ),
 

	
 
	OCL_NULL( 4 ),         // sign left/top, no longer in use
 

	
 
	OCL_SVAR( OC_UINT16, Station, had_vehicle_of_type ),
 

	
 
	OCL_CHUNK( 12, LoadOldGood ),
 

	
 
	OCL_SVAR(  OC_UINT8, Station, time_since_load ),
 
	OCL_SVAR(  OC_UINT8, Station, time_since_unload ),
 
	OCL_SVAR(  OC_UINT8, Station, delete_ctr ),
 
	OCL_SVAR(  OC_UINT8, Station, owner ),
 
	OCL_SVAR(  OC_UINT8, Station, facilities ),
 
	OCL_SVAR(  OC_UINT8, Station, airport_type ),
 
	OCL_NULL( 2 ),         // Bus/truck status, no longer in use
 
	OCL_SVAR(  OC_UINT8, Station, blocked_months_obsolete ),
 
	OCL_NULL( 1 ),         // Unknown
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Station, airport_flags ),
 
	OCL_NULL( 2 ),         // last_vehicle. now last_vehicle_type
 

	
 
	OCL_NULL( 4 ),         // Junk at end of chunk
 

	
 
	OCL_END()
 
};
 
static bool LoadOldStation(LoadgameState *ls, int num)
 
{
 
	Station *st;
 

	
 
	if (!AddBlockIfNeeded(&_Station_pool, num))
 
		error("Stations: failed loading savegame: too many stations");
 

	
 
	st = GetStation(num);
 
	_current_station_id = num;
 

	
 
	if (!LoadChunk(ls, st, station_chunk))
 
		return false;
 

	
 
	if (IsValidStation(st)) {
 
		if (st->train_tile) {
 
			/* Calculate the trainst_w and trainst_h */
 
			uint w = GB(_old_platforms, 3, 3);
 
			uint h = GB(_old_platforms, 0, 3);
 
			st->trainst_w = w;
 
			st->trainst_h = h;
 
		}
 

	
 
		st->town    = GetTown(REMAP_TOWN_IDX(_old_town_index));
 
		st->string_id = RemapOldStringID(_old_string_id);
 
	}
 

	
 
	return true;
 
}
 

	
 
static const OldChunks industry_chunk[] = {
 
	OCL_SVAR(   OC_TILE, Industry, xy ),
 
	OCL_VAR ( OC_UINT32,   1, &_old_town_index ),
 
	OCL_SVAR(  OC_UINT8, Industry, width ),
 
	OCL_SVAR(  OC_UINT8, Industry, height ),
 
	OCL_SVAR(  OC_UINT8, Industry, produced_cargo[0] ),
 
	OCL_SVAR(  OC_UINT8, Industry, produced_cargo[1] ),
 

	
 
	OCL_SVAR( OC_UINT16, Industry, cargo_waiting[0] ),
 
	OCL_SVAR( OC_UINT16, Industry, cargo_waiting[1] ),
 

	
 
	OCL_SVAR(  OC_UINT8, Industry, production_rate[0] ),
 
	OCL_SVAR(  OC_UINT8, Industry, production_rate[1] ),
 

	
 
	OCL_SVAR(  OC_UINT8, Industry, accepts_cargo[0] ),
 
	OCL_SVAR(  OC_UINT8, Industry, accepts_cargo[1] ),
 
	OCL_SVAR(  OC_UINT8, Industry, accepts_cargo[2] ),
 

	
 
	OCL_SVAR(  OC_UINT8, Industry, prod_level ),
 

	
 
	OCL_SVAR( OC_UINT16, Industry, last_mo_production[0] ),
 
	OCL_SVAR( OC_UINT16, Industry, last_mo_production[1] ),
 
	OCL_SVAR( OC_UINT16, Industry, last_mo_transported[0] ),
 
	OCL_SVAR( OC_UINT16, Industry, last_mo_transported[1] ),
 

	
 
	OCL_SVAR(  OC_UINT8, Industry, pct_transported[0] ),
 
	OCL_SVAR(  OC_UINT8, Industry, pct_transported[1] ),
 

	
 
	OCL_SVAR( OC_UINT16, Industry, total_production[0] ),
 
	OCL_SVAR( OC_UINT16, Industry, total_production[1] ),
 
	OCL_SVAR( OC_UINT16, Industry, total_transported[0] ),
 
	OCL_SVAR( OC_UINT16, Industry, total_transported[1] ),
 

	
 
	OCL_SVAR(  OC_UINT8, Industry, type ),
 
	OCL_SVAR(  OC_UINT8, Industry, owner ),
 
	OCL_SVAR(  OC_UINT8, Industry, random_color ),
 
	OCL_SVAR( OC_FILE_U8 | OC_VAR_I32, Industry, last_prod_year ),
 
	OCL_SVAR( OC_UINT16, Industry, counter ),
 
	OCL_SVAR(  OC_UINT8, Industry, was_cargo_delivered ),
 

	
 
	OCL_NULL( 9 ), // Random junk at the end of this chunk
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldIndustry(LoadgameState *ls, int num)
 
{
 
	Industry *i;
 

	
 
	if (!AddBlockIfNeeded(&_Industry_pool, num))
 
		error("Industries: failed loading savegame: too many industries");
 

	
 
	i = GetIndustry(num);
 
	if (!LoadChunk(ls, i, industry_chunk)) return false;
 

	
 
	if (IsValidIndustry(i)) {
 
		i->town = GetTown(REMAP_TOWN_IDX(_old_town_index));
 
	}
 

	
 
	return true;
 
}
 

	
 
static PlayerID _current_player_id;
 
static uint16 _old_inaugurated_year;
 
static int32 _old_yearly;
 

	
 
static const OldChunks player_yearly_chunk[] = {
 
	OCL_VAR(  OC_INT32,   1, &_old_yearly ),
 
	OCL_END()
 
};
 

	
 
static bool OldPlayerYearly(LoadgameState *ls, int num)
 
{
 
	int i;
 
	Player *p = GetPlayer(_current_player_id);
 

	
 
	for (i = 0; i < 13; i++) {
 
		if (!LoadChunk(ls, NULL, player_yearly_chunk)) return false;
 

	
 
		p->yearly_expenses[num][i] = _old_yearly;
 
	}
 

	
 
	return true;
 
}
 

	
 
static const OldChunks player_economy_chunk[] = {
 
	OCL_SVAR( OC_INT32, PlayerEconomyEntry, income ),
 
	OCL_SVAR( OC_INT32, PlayerEconomyEntry, expenses ),
 
	OCL_SVAR( OC_INT32, PlayerEconomyEntry, delivered_cargo ),
 
	OCL_SVAR( OC_INT32, PlayerEconomyEntry, performance_history ),
 
	OCL_SVAR( OC_FILE_I32 | OC_VAR_I64, PlayerEconomyEntry, company_value ),
 

	
 
	OCL_END()
 
};
 

	
 
static bool OldPlayerEconomy(LoadgameState *ls, int num)
 
{
 
	int i;
 
	Player *p = GetPlayer(_current_player_id);
 

	
 
	if (!LoadChunk(ls, &p->cur_economy, player_economy_chunk)) return false;
 

	
 
	/* Don't ask, but the number in TTD(Patch) are inversed to OpenTTD */
 
	p->cur_economy.income   = -p->cur_economy.income;
 
	p->cur_economy.expenses = -p->cur_economy.expenses;
 

	
 
	for (i = 0; i < 24; i++) {
 
		if (!LoadChunk(ls, &p->old_economy[i], player_economy_chunk)) return false;
 

	
 
		p->old_economy[i].income   = -p->old_economy[i].income;
 
		p->old_economy[i].expenses = -p->old_economy[i].expenses;
 
	}
 

	
 
	return true;
 
}
 

	
 
static const OldChunks player_ai_build_rec_chunk[] = {
 
	OCL_SVAR(   OC_TILE, AiBuildRec, spec_tile ),
 
	OCL_SVAR(   OC_TILE, AiBuildRec, use_tile ),
 
	OCL_SVAR(  OC_UINT8, AiBuildRec, rand_rng ),
 
	OCL_SVAR(  OC_UINT8, AiBuildRec, cur_building_rule ),
 
	OCL_SVAR(  OC_UINT8, AiBuildRec, unk6 ),
 
	OCL_SVAR(  OC_UINT8, AiBuildRec, unk7 ),
 
	OCL_SVAR(  OC_UINT8, AiBuildRec, buildcmd_a ),
 
	OCL_SVAR(  OC_UINT8, AiBuildRec, buildcmd_b ),
 
	OCL_SVAR(  OC_UINT8, AiBuildRec, direction ),
 
	OCL_SVAR(  OC_UINT8, AiBuildRec, cargo ),
 

	
 
	OCL_NULL( 8 ),  // Junk...
 

	
 
	OCL_END()
 
};
 

	
 
static bool OldLoadAIBuildRec(LoadgameState *ls, int num)
 
{
 
	Player *p = GetPlayer(_current_player_id);
 

	
 
	switch (num) {
 
		case 0: return LoadChunk(ls, &p->ai.src, player_ai_build_rec_chunk);
 
		case 1: return LoadChunk(ls, &p->ai.dst, player_ai_build_rec_chunk);
 
		case 2: return LoadChunk(ls, &p->ai.mid1, player_ai_build_rec_chunk);
 
		case 3: return LoadChunk(ls, &p->ai.mid2, player_ai_build_rec_chunk);
 
	}
 

	
 
	return false;
 
}
 
static const OldChunks player_ai_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, PlayerAI, state ),
 
	OCL_NULL( 1 ),         // Junk
 
	OCL_SVAR(  OC_UINT8, PlayerAI, state_mode ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, state_counter ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, timeout_counter ),
 

	
 
	OCL_CHUNK( 4, OldLoadAIBuildRec ),
 

	
 
	OCL_NULL( 20 ),        // More junk
 

	
 
	OCL_SVAR(  OC_UINT8, PlayerAI, cargo_type ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, num_wagons ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, build_kind ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, num_build_rec ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, num_loco_to_build ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, num_want_fullload ),
 

	
 
	OCL_NULL( 14 ),        // Oh no more junk :|
 

	
 
	OCL_NULL( 2 ),         // Loco-id, not used
 

	
 
	OCL_SVAR( OC_UINT16, PlayerAI, wagon_list[0] ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, wagon_list[1] ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, wagon_list[2] ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, wagon_list[3] ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, wagon_list[4] ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, wagon_list[5] ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, wagon_list[6] ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, wagon_list[7] ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, wagon_list[8] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[0] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[1] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[2] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[3] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[4] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[5] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[6] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[7] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[8] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[9] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[10] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[11] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[12] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[13] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[14] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[15] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[16] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[17] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[18] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, order_list_blocks[19] ),
 

	
 
	OCL_SVAR( OC_UINT16, PlayerAI, start_tile_a ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, start_tile_b ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, cur_tile_a ),
 
	OCL_SVAR( OC_UINT16, PlayerAI, cur_tile_b ),
 

	
 
	OCL_SVAR(  OC_UINT8, PlayerAI, start_dir_a ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, start_dir_b ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, cur_dir_a ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, cur_dir_b ),
 

	
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_tile_count ),
 

	
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[0] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[0] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[1] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[1] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[2] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[2] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[3] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[3] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[4] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[4] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[5] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[5] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[6] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[6] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[7] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[7] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[8] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[8] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[9] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[9] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[10] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[10] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[11] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[11] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[12] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[12] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[13] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[13] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[14] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[14] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[15] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[15] ),
 

	
 
	OCL_SVAR(  OC_UINT8, PlayerAI, railtype_to_use ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, route_type_mask ),
 

	
 
	OCL_END()
 
};
 

	
 
static bool OldPlayerAI(LoadgameState *ls, int num)
 
{
 
	Player *p = GetPlayer(_current_player_id);
 

	
 
	return LoadChunk(ls, &p->ai, player_ai_chunk);
 
}
 

	
 
static const OldChunks player_chunk[] = {
 
	OCL_VAR ( OC_UINT16,   1, &_old_string_id ),
 
	OCL_SVAR( OC_UINT32, Player, name_2 ),
 
	OCL_SVAR( OC_UINT32, Player, face ),
 
	OCL_VAR ( OC_UINT16,   1, &_old_string_id_2 ),
 
	OCL_SVAR( OC_UINT32, Player, president_name_2 ),
 

	
 
	OCL_SVAR(  OC_INT32, Player, player_money ),
 
	OCL_SVAR(  OC_INT32, Player, current_loan ),
 

	
 
	OCL_SVAR(  OC_UINT8, Player, player_color ),
 
	OCL_SVAR(  OC_UINT8, Player, player_money_fraction ),
 
	OCL_SVAR(  OC_UINT8, Player, quarters_of_bankrupcy ),
 
	OCL_SVAR(  OC_UINT8, Player, bankrupt_asked ),
 
	OCL_SVAR( OC_UINT32, Player, bankrupt_value ),
 
	OCL_SVAR( OC_UINT16, Player, bankrupt_timeout ),
 

	
 
	OCL_SVAR( OC_FILE_U32 | OC_VAR_U16, Player, cargo_types ),
 

	
 
	OCL_CHUNK( 3, OldPlayerYearly ),
 
	OCL_CHUNK( 1, OldPlayerEconomy ),
 

	
 
	OCL_VAR ( OC_UINT16,   1,    &_old_inaugurated_year ),
 
	OCL_SVAR(   OC_TILE, Player, last_build_coordinate ),
 
	OCL_SVAR(  OC_UINT8, Player, num_valid_stat_ent ),
 

	
 
	OCL_CHUNK( 1, OldPlayerAI ),
 

	
 
	OCL_SVAR(  OC_UINT8, Player, block_preview ),
 
	OCL_SVAR(  OC_UINT8, Player, ai.tick ),
 
	OCL_SVAR(  OC_UINT8, Player, avail_railtypes ),
 
	OCL_SVAR(   OC_TILE, Player, location_of_house ),
 
	OCL_SVAR(  OC_UINT8, Player, share_owners[0] ),
 
	OCL_SVAR(  OC_UINT8, Player, share_owners[1] ),
 
	OCL_SVAR(  OC_UINT8, Player, share_owners[2] ),
 
	OCL_SVAR(  OC_UINT8, Player, share_owners[3] ),
 

	
 
	OCL_NULL( 8 ), // junk at end of chunk
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldPlayer(LoadgameState *ls, int num)
 
{
 
	Player *p = GetPlayer(num);
 

	
 
	_current_player_id = num;
 

	
 
	if (!LoadChunk(ls, p, player_chunk)) return false;
 

	
 
	p->name_1 = RemapOldStringID(_old_string_id);
 
	p->president_name_1 = RemapOldStringID(_old_string_id_2);
 
	p->money64 = p->player_money;
 

	
 
	if (num == 0) {
 
		/* If the first player has no name, make sure we call it UNNAMED */
 
		if (p->name_1 == 0)
 
			p->name_1 = STR_SV_UNNAMED;
 
	} else {
 
		/* Beside some multiplayer maps (1 on 1), which we don't official support,
 
		all other players are an AI.. mark them as such */
 
		p->is_ai = true;
 
	}
 

	
 
	/* Sometimes it is better to not ask.. in old scenarios, the money
 
	was always 893288 pounds. In the newer versions this is correct,
 
	but correct for those oldies
 
	Ps: this also means that if you had exact 893288 pounds, you will go back
 
	to 10000.. this is a very VERY small chance ;) */
 
	if (p->player_money == 893288)
 
		p->money64 = p->player_money = p->current_loan = 100000;
 

	
 
	_player_colors[num] = p->player_color;
 
	p->inaugurated_year = _old_inaugurated_year;
 
	if (p->location_of_house == 0xFFFF)
 
		p->location_of_house = 0;
 

	
 
	/* State 20 for AI players is sell vehicle. Since the AI struct is not
 
	 * really figured out as of now, p->ai.cur_veh; needed for 'sell vehicle'
 
	 * is NULL and the function will crash. To fix this, just change the state
 
	 * to some harmless state, like 'loop vehicle'; 1 */
 
	if (!IsHumanPlayer(num) && p->ai.state == 20) p->ai.state = 1;
 

	
 
	if (p->is_ai && (!_networking || _network_server) && _ai.enabled)
 
		AI_StartNewAI(p->index);
 

	
 
	return true;
 
}
 

	
 
static uint32 _old_order_ptr;
 
static uint16 _old_next_ptr;
 
static uint32 _current_vehicle_id;
 

	
 
static const OldChunks vehicle_train_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, VehicleRail, track ),
 
	OCL_SVAR(  OC_UINT8, VehicleRail, force_proceed ),
 
	OCL_SVAR( OC_UINT16, VehicleRail, crash_anim_pos ),
 
	OCL_SVAR(  OC_UINT8, VehicleRail, railtype ),
 

	
 
	OCL_NULL( 5 ), // Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_road_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, VehicleRoad, state ),
 
	OCL_SVAR(  OC_UINT8, VehicleRoad, frame ),
 
	OCL_SVAR( OC_UINT16, VehicleRoad, blocked_ctr ),
 
	OCL_SVAR(  OC_UINT8, VehicleRoad, overtaking ),
 
	OCL_SVAR(  OC_UINT8, VehicleRoad, overtaking_ctr ),
 
	OCL_SVAR( OC_UINT16, VehicleRoad, crashed_ctr ),
 
	OCL_SVAR(  OC_UINT8, VehicleRoad, reverse_ctr ),
 

	
 
	OCL_NULL( 1 ), // Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_ship_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, VehicleShip, state ),
 

	
 
	OCL_NULL( 9 ), // Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_air_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, VehicleAir, pos ),
 
	OCL_SVAR(  OC_FILE_U8 | OC_VAR_U16, VehicleAir, targetairport ),
 
	OCL_SVAR( OC_UINT16, VehicleAir, crashed_counter ),
 
	OCL_SVAR(  OC_UINT8, VehicleAir, state ),
 

	
 
	OCL_NULL( 5 ), // Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_special_chunk[] = {
 
	OCL_SVAR( OC_UINT16, VehicleSpecial, unk0 ),
 
	OCL_SVAR(  OC_UINT8, VehicleSpecial, unk2 ),
 

	
 
	OCL_NULL( 7 ), // Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_disaster_chunk[] = {
 
	OCL_SVAR( OC_UINT16, VehicleDisaster, image_override ),
 
	OCL_SVAR( OC_UINT16, VehicleDisaster, unk2 ),
 

	
 
	OCL_NULL( 6 ), // Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_empty_chunk[] = {
 
	OCL_NULL( 10 ), // Junk
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldVehicleUnion(LoadgameState *ls, int num)
 
{
 
	Vehicle *v = GetVehicle(_current_vehicle_id);
 
	uint temp = ls->total_read;
 
	bool res;
 

	
 
	switch (v->type) {
 
		case VEH_Train:    res = LoadChunk(ls, &v->u.rail,     vehicle_train_chunk);    break;
 
		case VEH_Road:     res = LoadChunk(ls, &v->u.road,     vehicle_road_chunk);     break;
 
		case VEH_Ship:     res = LoadChunk(ls, &v->u.ship,     vehicle_ship_chunk);     break;
 
		case VEH_Aircraft: res = LoadChunk(ls, &v->u.air,      vehicle_air_chunk);      break;
 
		case VEH_Special:  res = LoadChunk(ls, &v->u.special,  vehicle_special_chunk);  break;
 
		case VEH_Disaster: res = LoadChunk(ls, &v->u.disaster, vehicle_disaster_chunk); break;
 
		default:           res = LoadChunk(ls, NULL,           vehicle_empty_chunk);    break;
 
	}
 

	
 
	/* This chunk size should always be 10 bytes */
 
	if (ls->total_read - temp != 10) {
 
		DEBUG(oldloader, 4, "Assert failed in Vehicle");
 
		return false;
 
	}
 

	
 
	return res;
 
}
 

	
 
static const OldChunks vehicle_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, Vehicle, type ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, subtype ),
 

	
 
	OCL_NULL( 2 ),         // Hash, calculated automatically
 
	OCL_NULL( 2 ),         // Index, calculated automatically
 

	
 
	OCL_VAR ( OC_UINT32,   1, &_old_order_ptr ),
 
	OCL_VAR ( OC_UINT16,   1, &_old_order ),
 

	
 
	OCL_SVAR(  OC_UINT8, Vehicle, num_orders ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, cur_order_index ),
 
	OCL_SVAR(   OC_TILE, Vehicle, dest_tile ),
 
	OCL_SVAR( OC_UINT16, Vehicle, load_unload_time_rem ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, date_of_last_service ),
 
	OCL_SVAR( OC_UINT16, Vehicle, service_interval ),
 
	OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, last_station_visited ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, tick_counter ),
 
	OCL_SVAR( OC_UINT16, Vehicle, max_speed ),
 

	
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Vehicle, x_pos ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Vehicle, y_pos ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, z_pos ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, direction ),
 
	OCL_SVAR(   OC_INT8, Vehicle, x_offs ),
 
	OCL_SVAR(   OC_INT8, Vehicle, y_offs ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, sprite_width ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, sprite_height ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, z_height ),
 

	
 
	OCL_SVAR(  OC_UINT8, Vehicle, owner ),
 
	OCL_SVAR(   OC_TILE, Vehicle, tile ),
 
	OCL_SVAR( OC_UINT16, Vehicle, cur_image ),
 

	
 
	OCL_NULL( 8 ),        // Vehicle sprite box, calculated automatically
 

	
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, Vehicle, vehstatus ),
 
	OCL_SVAR( OC_UINT16, Vehicle, cur_speed ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, subspeed ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, acceleration ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, progress ),
 

	
 
	OCL_SVAR(  OC_UINT8, Vehicle, cargo_type ),
 
	OCL_SVAR( OC_UINT16, Vehicle, cargo_cap ),
 
	OCL_SVAR( OC_UINT16, Vehicle, cargo_count ),
 
	OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, cargo_source ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, cargo_days ),
 

	
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, age ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, max_age ),
 
	OCL_SVAR( OC_FILE_U8 | OC_VAR_I32, Vehicle, build_year ),
 
	OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, unitnumber ),
 

	
 
	OCL_SVAR( OC_UINT16, Vehicle, engine_type ),
 

	
 
	OCL_SVAR(  OC_UINT8, Vehicle, spritenum ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, day_counter ),
 

	
 
	OCL_SVAR(  OC_UINT8, Vehicle, breakdowns_since_last_service ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, breakdown_ctr ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, breakdown_delay ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, breakdown_chance ),
 

	
 
	OCL_SVAR( OC_UINT16, Vehicle, reliability ),
 
	OCL_SVAR( OC_UINT16, Vehicle, reliability_spd_dec ),
 

	
 
	OCL_SVAR(  OC_INT32, Vehicle, profit_this_year ),
 
	OCL_SVAR(  OC_INT32, Vehicle, profit_last_year ),
 

	
 
	OCL_VAR ( OC_UINT16,   1, &_old_next_ptr ),
 

	
 
	OCL_SVAR( OC_UINT32, Vehicle, value ),
 

	
 
	OCL_VAR ( OC_UINT16,   1, &_old_string_id ),
 

	
 
	OCL_CHUNK( 1, LoadOldVehicleUnion ),
 

	
 
	OCL_NULL( 20 ), // Junk at end of struct (TTDPatch has some data in it)
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldVehicle(LoadgameState *ls, int num)
 
{
 
	uint i;
 

	
 
	/* Read the TTDPatch flags, because we need some info from it */
 
	ReadTTDPatchFlags();
 

	
 
	for (i = 0; i < _old_vehicle_multiplier; i++) {
 
		Vehicle *v;
 

	
 
		_current_vehicle_id = num * _old_vehicle_multiplier + i;
 

	
 
		if (!AddBlockIfNeeded(&_Vehicle_pool, _current_vehicle_id))
 
			error("Vehicles: failed loading savegame: too many vehicles");
 

	
 
		v = GetVehicle(_current_vehicle_id);
 
		if (!LoadChunk(ls, v, vehicle_chunk)) return false;
 

	
 
		/* This should be consistent, else we have a big problem... */
 
		if (v->index != _current_vehicle_id) {
 
			DEBUG(oldloader, 0, "Loading failed - vehicle-array is invalid");
 
			return false;
 
		}
 

	
 
		if (_old_order_ptr != 0 && _old_order_ptr != 0xFFFFFFFF) {
 
			v->orders = GetOrder(REMAP_ORDER_IDX(_old_order_ptr));
 
		}
 
		AssignOrder(&v->current_order, UnpackOldOrder(_old_order));
 

	
 
		/* For some reason we need to correct for this */
 
		switch (v->spritenum) {
 
			case 0xfd: break;
 
			case 0xff: v->spritenum = 0xfe; break;
 
			default:   v->spritenum >>= 1; break;
 
		}
 

	
 
		if (_old_next_ptr != 0xFFFF)
 
			v->next = GetVehicle(_old_next_ptr);
 

	
 
		v->string_id = RemapOldStringID(_old_string_id);
 

	
 
		/* Vehicle-subtype is different in TTD(Patch) */
 
		if (v->type == VEH_Special) v->subtype = v->subtype >> 1;
 
	}
 

	
 
	return true;
 
}
 

	
 
static const OldChunks sign_chunk[] = {
 
	OCL_SVAR( OC_UINT16, Sign, str ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32,Sign, x ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32,Sign, y ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I8, Sign, z ),
 

	
 
	OCL_NULL( 6 ),         // Width of sign, no longer in use
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldSign(LoadgameState *ls, int num)
 
{
 
	if (!AddBlockIfNeeded(&_Sign_pool, num))
 
		error("Signs: failed loading savegame: too many signs");
 

	
 
	return LoadChunk(ls, GetSign(num), sign_chunk);
 
}
 

	
 
static const OldChunks engine_chunk[] = {
 
	OCL_SVAR( OC_UINT16, Engine, player_avail ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Engine, intro_date ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Engine, age ),
 
	OCL_SVAR( OC_UINT16, Engine, reliability ),
 
	OCL_SVAR( OC_UINT16, Engine, reliability_spd_dec ),
 
	OCL_SVAR( OC_UINT16, Engine, reliability_start ),
 
	OCL_SVAR( OC_UINT16, Engine, reliability_max ),
 
	OCL_SVAR( OC_UINT16, Engine, reliability_final ),
 
	OCL_SVAR( OC_UINT16, Engine, duration_phase_1 ),
 
	OCL_SVAR( OC_UINT16, Engine, duration_phase_2 ),
 
	OCL_SVAR( OC_UINT16, Engine, duration_phase_3 ),
 

	
 
	OCL_SVAR(  OC_UINT8, Engine, lifelength ),
 
	OCL_SVAR(  OC_UINT8, Engine, flags ),
 
	OCL_SVAR(  OC_UINT8, Engine, preview_player ),
 
	OCL_SVAR(  OC_UINT8, Engine, preview_wait ),
 
	OCL_SVAR(  OC_UINT8, Engine, railtype ),
 

	
 
	OCL_NULL( 1 ),         // Junk
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldEngine(LoadgameState *ls, int num)
 
{
 
	if (!LoadChunk(ls, GetEngine(num), engine_chunk)) return false;
 

	
 
	/* Make sure wagons are marked as do-not-age */
 
	if ((num >= 27 && num < 54) || (num >= 57 && num < 84) || (num >= 89 && num < 116))
 
		GetEngine(num)->age = 0xFFFF;
 

	
 
	return true;
 
}
 

	
 
static const OldChunks subsidy_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, Subsidy, cargo_type ),
 
	OCL_SVAR(  OC_UINT8, Subsidy, age ),
 
	OCL_SVAR(  OC_FILE_U8 | OC_VAR_U16, Subsidy, from ),
 
	OCL_SVAR(  OC_FILE_U8 | OC_VAR_U16, Subsidy, to ),
 

	
 
	OCL_END()
 
};
 

	
 
static inline bool LoadOldSubsidy(LoadgameState *ls, int num)
 
{
 
	return LoadChunk(ls, &_subsidies[num], subsidy_chunk);
 
}
 

	
 
static const OldChunks game_difficulty_chunk[] = {
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, max_no_competitors ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, competitor_start_time ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, number_towns ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, number_industries ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, max_loan ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, initial_interest ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, vehicle_costs ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, competitor_speed ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, competitor_intelligence ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, vehicle_breakdowns ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, subsidy_multiplier ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, construction_cost ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, terrain_type ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, quantity_sea_lakes ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, economy ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, line_reverse_mode ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, GameDifficulty, disasters ),
 
	OCL_END()
 
};
 

	
 
static inline bool LoadOldGameDifficulty(LoadgameState *ls, int num)
 
{
 
	return LoadChunk(ls, &_opt.diff, game_difficulty_chunk);
 
}
 

	
 

	
 
static bool LoadOldMapPart1(LoadgameState *ls, int num)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < OLD_MAP_SIZE; i++) {
 
		_m[i].m1 = ReadByte(ls);
 
	}
 
	for (i = 0; i < OLD_MAP_SIZE; i++) {
 
		_m[i].m2 = ReadByte(ls);
 
	}
 
	for (i = 0; i < OLD_MAP_SIZE; i++) {
 
		_old_map3[i * 2] = ReadByte(ls);
 
		_old_map3[i * 2 + 1] = ReadByte(ls);
 
	}
 
	for (i = 0; i < OLD_MAP_SIZE / 4; i++) {
 
		byte b = ReadByte(ls);
 
		_m[i * 4 + 0].extra = GB(b, 0, 2);
 
		_m[i * 4 + 1].extra = GB(b, 2, 2);
 
		_m[i * 4 + 2].extra = GB(b, 4, 2);
 
		_m[i * 4 + 3].extra = GB(b, 6, 2);
 
	}
 

	
 
	return !ls->failed;
 
}
 

	
 
static bool LoadOldMapPart2(LoadgameState *ls, int num)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < OLD_MAP_SIZE; i++) {
 
		_m[i].type_height = ReadByte(ls);
 
	}
 
	for (i = 0; i < OLD_MAP_SIZE; i++) {
 
		_m[i].m5 = ReadByte(ls);
 
	}
 

	
 
	return !ls->failed;
 
}
 

	
 
static uint32 _old_cur_town_ctr;
 
static const OldChunks main_chunk[] = {
 
	OCL_ASSERT( 0 ),
 
	OCL_VAR ( OC_FILE_U16 | OC_VAR_U32, 1, &_date ),
 
	OCL_VAR ( OC_UINT16,   1, &_date_fract ),
 
	OCL_NULL( 600 ),            // TextEffects
 
	OCL_VAR ( OC_UINT32,   2, &_random_seeds[0] ),
 

	
 
	OCL_ASSERT( 0x264 ),
 
	OCL_CHUNK(  70, LoadOldTown ),
 
	OCL_ASSERT( 0x1C18 ),
 
	OCL_CHUNK(5000, LoadOldOrder ),
 
	OCL_ASSERT( 0x4328 ),
 

	
 
	OCL_VAR (   OC_TILE, 256, &_animated_tile_list[0] ),
 
	OCL_NULL( 4 ),              // old end-of-order-list-pointer, no longer in use
 

	
 
	OCL_CHUNK( 255, LoadOldDepot ),
 
	OCL_ASSERT( 0x4B26 ),
 

	
 
	OCL_VAR ( OC_UINT32,   1, &_old_cur_town_ctr ),
 
	OCL_NULL( 2 ),              // timer_counter, no longer in use
 
	OCL_NULL( 2 ),              // land_code,     no longer in use
 

	
 
	OCL_VAR ( OC_FILE_U16 | OC_VAR_U8, 1, &_age_cargo_skip_counter ),
 
	OCL_VAR ( OC_UINT16,   1, &_tick_counter ),
 
	OCL_VAR (   OC_TILE,   1, &_cur_tileloop_tile ),
 

	
 
	OCL_CHUNK( 49, LoadOldPrice ),
 
	OCL_CHUNK( 12, LoadOldCargoPaymentRate ),
 

	
 
	OCL_ASSERT( 0x4CBA ),
 

	
 
	OCL_CHUNK( 1, LoadOldMapPart1 ),
 

	
 
	OCL_ASSERT( 0x48CBA ),
 

	
 
	OCL_CHUNK(250, LoadOldStation ),
 
	OCL_CHUNK( 90, LoadOldIndustry ),
 
	OCL_CHUNK(  8, LoadOldPlayer ),
 

	
 
	OCL_ASSERT( 0x547F2 ),
 

	
 
	OCL_CHUNK( 850, LoadOldVehicle ),
 

	
 
	OCL_ASSERT( 0x6F0F2 ),
 

	
 
	OCL_VAR (  OC_UINT8, 32 * 500, &_name_array[0] ),
 

	
 
	OCL_NULL( 0x2000 ),            // Old hash-table, no longer in use
 

	
 
	OCL_CHUNK( 40, LoadOldSign ),
 
	OCL_CHUNK(256, LoadOldEngine ),
 

	
 
	OCL_VAR ( OC_UINT16,    1, &_vehicle_id_ctr_day ),
 

	
 
	OCL_CHUNK(  8, LoadOldSubsidy ),
 

	
 
	OCL_VAR ( OC_FILE_U16 | OC_VAR_U32,   1, &_next_competitor_start ),
 
	OCL_VAR ( OC_FILE_I16 | OC_VAR_I32,   1, &_saved_scrollpos_x ),
 
	OCL_VAR ( OC_FILE_I16 | OC_VAR_I32,   1, &_saved_scrollpos_y ),
 
	OCL_VAR ( OC_FILE_U16 | OC_VAR_U8,    1, &_saved_scrollpos_zoom ),
 

	
 
	OCL_VAR ( OC_UINT32,    1, &_economy.max_loan ),
 
	OCL_VAR ( OC_UINT32,    1, &_economy.max_loan_unround ),
 
	OCL_VAR ( OC_FILE_U16 | OC_VAR_U32,    1, &_economy.fluct ),
 

	
 
	OCL_VAR ( OC_UINT16,    1, &_disaster_delay ),
 

	
 
	OCL_NULL( 144 ),             // cargo-stuff, calculated in InitializeLandscapeVariables
 

	
 
	OCL_VAR ( OC_UINT16,  256, &_engine_name_strings[0] ),
 

	
 
	OCL_NULL( 144 ),             // AI cargo-stuff, calculated in InitializeLandscapeVariables
 
	OCL_NULL( 2 ),               // Company indexes of players, no longer in use
 

	
 
	OCL_VAR ( OC_FILE_U8 | OC_VAR_U16,    1, &_station_tick_ctr ),
 

	
 
	OCL_VAR (  OC_UINT8,    1, &_opt.currency ),
 
	OCL_VAR (  OC_UINT8,    1, &_opt.units ),
 
	OCL_VAR ( OC_FILE_U8 | OC_VAR_U32,    1, &_cur_player_tick_index ),
 

	
 
	OCL_NULL( 2 ),               // Date stuff, calculated automatically
 
	OCL_NULL( 8 ),               // Player colors, calculated automatically
 

	
 
	OCL_VAR (  OC_UINT8,    1, &_economy.infl_amount ),
 
	OCL_VAR (  OC_UINT8,    1, &_economy.infl_amount_pr ),
 
	OCL_VAR (  OC_UINT8,    1, &_economy.interest_rate ),
 
	OCL_VAR (  OC_UINT8,    1, &_avail_aircraft ),
 
	OCL_VAR (  OC_UINT8,    1, &_opt.road_side ),
 
	OCL_VAR (  OC_UINT8,    1, &_opt.town_name ),
 

	
 
	OCL_CHUNK( 1, LoadOldGameDifficulty ),
 

	
 
	OCL_ASSERT( 0x77130 ),
 

	
 
	OCL_VAR (  OC_UINT8,    1, &_opt.diff_level ),
 
	OCL_VAR (  OC_UINT8,    1, &_opt.landscape ),
 
	OCL_VAR (  OC_UINT8,    1, &_trees_tick_ctr ),
 

	
 
	OCL_NULL( 1 ),               // Custom vehicle types yes/no, no longer used
 
	OCL_VAR (  OC_UINT8,    1, &_opt.snow_line ),
 

	
 
	OCL_NULL( 32 ),              // new_industry_randtable, no longer used (because of new design)
 
	OCL_NULL( 36 ),              // cargo-stuff, calculated in InitializeLandscapeVariables
 

	
 
	OCL_ASSERT( 0x77179 ),
 

	
 
	OCL_CHUNK( 1, LoadOldMapPart2 ),
 

	
 
	OCL_ASSERT( 0x97179 ),
 

	
 
	/* Below any (if available) extra chunks from TTDPatch can follow */
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldMain(LoadgameState *ls)
 
{
 
	int i;
 

	
 
	/* The first 49 is the name of the game + checksum, skip it */
 
	fseek(ls->file, HEADER_SIZE, SEEK_SET);
 

	
 
	DEBUG(oldloader, 4, "Reading main chunk...");
 
	/* Load the biggest chunk */
 
	if (!LoadChunk(ls, NULL, main_chunk)) {
 
		DEBUG(oldloader, 0, "Loading failed");
 
		return false;
 
	}
 
	DEBUG(oldloader, 4, "Done, converting game data...");
 

	
 
	/* Fix some general stuff */
 
	_opt.landscape = _opt.landscape & 0xF;
 

	
 
	/* Remap some pointers */
 
	_cur_town_ctr      = REMAP_TOWN_IDX(_old_cur_town_ctr);
 

	
 
	/* _old_map3 is changed in _map3_lo and _map3_hi */
 
	for (i = 0; i < OLD_MAP_SIZE; i++) {
 
		_m[i].m3 = _old_map3[i * 2];
 
		_m[i].m4 = _old_map3[i * 2 + 1];
 
	}
 

	
 
	for (i = 0; i < OLD_MAP_SIZE; i ++) {
 
		if (IsTileType(i, MP_RAILWAY)) {
 
			/* We save presignals different from TTDPatch, convert them */
 
			if (GetRailTileType(i) == RAIL_TILE_SIGNALS) {
 
				/* This byte is always zero in TTD for this type of tile */
 
				if (_m[i].m4) /* Convert the presignals to our own format */
 
					_m[i].m4 = (_m[i].m4 >> 1) & 7;
 
			}
 
			/* TTDPatch stores PBS things in L6 and all elsewhere; so we'll just
 
			 * clear it for ourselves and let OTTD's rebuild PBS itself */
 
			_m[i].m4 &= 0xF; /* Only keep the lower four bits; upper four is PBS */
 
		}
 
	}
 

	
 
	/* Fix the game to be compatible with OpenTTD */
 
	FixOldTowns();
 
	FixOldStations();
 
	FixOldVehicles();
 

	
 
	AddTypeToEngines();
 

	
 
	/* We have a new difficulty setting */
 
	_opt.diff.town_council_tolerance = clamp(_opt.diff_level, 0, 2);
 

	
 
	DEBUG(oldloader, 4, "Finished converting game data");
 
	DEBUG(oldloader, 1, "TTD(Patch) savegame successfully converted");
 

	
 
	return true;
 
}
 

	
 
bool LoadOldSaveGame(const char *file)
 
{
 
	LoadgameState ls;
 

	
 
	DEBUG(oldloader, 4, "Trying to load a TTD(Patch) savegame");
 

	
 
	InitLoading(&ls);
 

	
 
	/* Open file */
 
	ls.file = fopen(file, "rb");
 

	
 
	if (ls.file == NULL) {
 
		DEBUG(oldloader, 0, "Cannot open file '%s'", file);
 
		return false;
 
	}
 

	
 
	/* Load the main chunk */
 
	if (!LoadOldMain(&ls)) return false;
 

	
 
	fclose(ls.file);
 

	
 
	_pause = 2;
 

	
 
	return true;
 
}
 

	
 
void GetOldSaveGameName(char *title, const char *path, const char *file)
 
{
 
	char filename[MAX_PATH];
 
	FILE *f;
 

	
 
	snprintf(filename, lengthof(filename), "%s" PATHSEP "%s", path, file);
 
	f = fopen(filename, "rb");
 
	title[0] = '\0';
 
	title[48] = '\0';
 

	
 
	if (f == NULL) return;
 

	
 
	if (fread(title, 1, 48, f) != 48) snprintf(title, 48, "Corrupt file");
 

	
 
	fclose(f);
 
}
src/oldpool.c
Show inline comments
 
deleted file
src/oldpool.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "oldpool.h"
 

	
 
/**
 
 * Clean a pool in a safe way (does free all blocks)
 
 */
 
void CleanPool(OldMemoryPool *pool)
 
{
 
	uint i;
 

	
 
	DEBUG(misc, 4, "[Pool] (%s) cleaning pool..", pool->name);
 

	
 
	/* Free all blocks */
 
	for (i = 0; i < pool->current_blocks; i++) {
 
		if (pool->clean_block_proc != NULL) {
 
			pool->clean_block_proc(i * (1 << pool->block_size_bits), (i + 1) * (1 << pool->block_size_bits) - 1);
 
		}
 
		free(pool->blocks[i]);
 
	}
 

	
 
	/* Free the block itself */
 
	free(pool->blocks);
 

	
 
	/* Clear up some critical data */
 
	pool->total_items = 0;
 
	pool->current_blocks = 0;
 
	pool->blocks = NULL;
 
}
 

	
 
/**
 
 * This function tries to increase the size of array by adding
 
 *  1 block too it
 
 *
 
 * @return Returns false if the pool could not be increased
 
 */
 
bool AddBlockToPool(OldMemoryPool *pool)
 
{
 
	/* Is the pool at his max? */
 
	if (pool->max_blocks == pool->current_blocks)
 
		return false;
 

	
 
	pool->total_items = (pool->current_blocks + 1) * (1 << pool->block_size_bits);
 

	
 
	DEBUG(misc, 4, "[Pool] (%s) increasing size of pool to %d items (%d bytes)", pool->name, pool->total_items, pool->total_items * pool->item_size);
 

	
 
	/* Increase the poolsize */
 
	pool->blocks = realloc(pool->blocks, sizeof(pool->blocks[0]) * (pool->current_blocks + 1));
 
	if (pool->blocks == NULL) error("Pool: (%s) could not allocate memory for blocks", pool->name);
 

	
 
	/* Allocate memory to the new block item */
 
	pool->blocks[pool->current_blocks] = malloc(pool->item_size * (1 << pool->block_size_bits));
 
	if (pool->blocks[pool->current_blocks] == NULL)
 
		error("Pool: (%s) could not allocate memory for blocks", pool->name);
 

	
 
	/* Clean the content of the new block */
 
	memset(pool->blocks[pool->current_blocks], 0, pool->item_size * (1 << pool->block_size_bits));
 

	
 
	/* Call a custom function if defined (e.g. to fill indexes) */
 
	if (pool->new_block_proc != NULL)
 
		pool->new_block_proc(pool->current_blocks * (1 << pool->block_size_bits));
 

	
 
	/* We have a new block */
 
	pool->current_blocks++;
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Adds blocks to the pool if needed (and possible) till index fits inside the pool
 
 *
 
 * @return Returns false if adding failed
 
 */
 
bool AddBlockIfNeeded(OldMemoryPool *pool, uint index)
 
{
 
	while (index >= pool->total_items) {
 
		if (!AddBlockToPool(pool))
 
			return false;
 
	}
 

	
 
	return true;
 
}
src/openttd.c
Show inline comments
 
deleted file
src/openttd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "string.h"
 
#include "table/strings.h"
 
#include "debug.h"
 
#include "driver.h"
 
#include "saveload.h"
 
#include "strings.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "void_map.h"
 

	
 
#define VARDEF
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "functions.h"
 
#include "mixer.h"
 
#include "spritecache.h"
 
#include "strings.h"
 
#include "gfx.h"
 
#include "gfxinit.h"
 
#include "gui.h"
 
#include "station.h"
 
#include "station_map.h"
 
#include "town_map.h"
 
#include "tunnel_map.h"
 
#include "vehicle.h"
 
#include "viewport.h"
 
#include "window.h"
 
#include "player.h"
 
#include "command.h"
 
#include "town.h"
 
#include "industry.h"
 
#include "news.h"
 
#include "engine.h"
 
#include "sound.h"
 
#include "economy.h"
 
#include "fileio.h"
 
#include "hal.h"
 
#include "airport.h"
 
#include "console.h"
 
#include "screenshot.h"
 
#include "network/network.h"
 
#include "signs.h"
 
#include "depot.h"
 
#include "waypoint.h"
 
#include "ai/ai.h"
 
#include "train.h"
 
#include "yapf/yapf.h"
 
#include "settings.h"
 
#include "genworld.h"
 
#include "date.h"
 
#include "clear_map.h"
 
#include "fontcache.h"
 
#include "newgrf_config.h"
 

	
 
#include "bridge_map.h"
 
#include "clear_map.h"
 
#include "rail_map.h"
 
#include "road_map.h"
 
#include "water_map.h"
 
#include "industry_map.h"
 

	
 
#include <stdarg.h>
 

	
 
void CallLandscapeTick(void);
 
void IncreaseDate(void);
 
void DoPaletteAnimations(void);
 
void MusicLoop(void);
 
void ResetMusic(void);
 
void InitializeStations(void);
 
void DeleteAllPlayerStations(void);
 

	
 
extern void SetDifficultyLevel(int mode, GameOptions *gm_opt);
 
extern void DoStartupNewPlayer(bool is_ai);
 
extern void ShowOSErrorBox(const char *buf);
 

	
 
/* TODO: usrerror() for errors which are not of an internal nature but
 
 * caused by the user, i.e. missing files or fatal configuration errors.
 
 * Post-0.4.0 since Celestar doesn't want this in SVN before. --pasky */
 

	
 
void CDECL error(const char *s, ...)
 
{
 
	va_list va;
 
	char buf[512];
 

	
 
	va_start(va, s);
 
	vsnprintf(buf, lengthof(buf), s, va);
 
	va_end(va);
 

	
 
	ShowOSErrorBox(buf);
 
	if (_video_driver != NULL) _video_driver->stop();
 

	
 
	assert(0);
 
	exit(1);
 
}
 

	
 
void CDECL ShowInfoF(const char *str, ...)
 
{
 
	va_list va;
 
	char buf[1024];
 
	va_start(va, str);
 
	vsnprintf(buf, lengthof(buf), str, va);
 
	va_end(va);
 
	ShowInfo(buf);
 
}
 

	
 

	
 
void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
 
{
 
	FILE *in;
 
	byte *mem;
 
	size_t len;
 

	
 
	in = fopen(filename, "rb");
 
	if (in == NULL) return NULL;
 

	
 
	fseek(in, 0, SEEK_END);
 
	len = ftell(in);
 
	fseek(in, 0, SEEK_SET);
 
	if (len > maxsize || (mem = malloc(len + 1)) == NULL) {
 
		fclose(in);
 
		return NULL;
 
	}
 
	mem[len] = 0;
 
	if (fread(mem, len, 1, in) != 1) {
 
		fclose(in);
 
		free(mem);
 
		return NULL;
 
	}
 
	fclose(in);
 

	
 
	*lenp = len;
 
	return mem;
 
}
 

	
 
static void showhelp(void)
 
{
 
	extern const char _openttd_revision[];
 
	char buf[4096], *p;
 

	
 
	p = buf;
 

	
 
	p += snprintf(p, lengthof(buf), "OpenTTD %s\n", _openttd_revision);
 
	p = strecpy(p,
 
		"\n"
 
		"\n"
 
		"Command line options:\n"
 
		"  -v drv              = Set video driver (see below)\n"
 
		"  -s drv              = Set sound driver (see below)\n"
 
		"  -m drv              = Set music driver (see below)\n"
 
		"  -r res              = Set resolution (for instance 800x600)\n"
 
		"  -h                  = Display this help text\n"
 
		"  -t year             = Set starting year\n"
 
		"  -d [[fac=]lvl[,...]]= Debug mode\n"
 
		"  -e                  = Start Editor\n"
 
		"  -g [savegame]       = Start new/save game immediately\n"
 
		"  -G seed             = Set random seed\n"
 
		"  -n [ip:port#player] = Start networkgame\n"
 
		"  -D [ip][:port]      = Start dedicated server\n"
 
#if !defined(__MORPHOS__) && !defined(__AMIGA__) && !defined(WIN32)
 
		"  -f                  = Fork into the background (dedicated only)\n"
 
#endif
 
		"  -i                  = Force to use the DOS palette\n"
 
		"                          (use this if you see a lot of pink)\n"
 
		"  -c config_file      = Use 'config_file' instead of 'openttd.cfg'\n"
 
		"  -x                  = Do not automatically save to config file on exit\n",
 
		lastof(buf)
 
	);
 

	
 
	p = GetDriverList(p, lastof(buf));
 

	
 
	ShowInfo(buf);
 
}
 

	
 

	
 
typedef struct {
 
	char *opt;
 
	int numleft;
 
	char **argv;
 
	const char *options;
 
	char *cont;
 
} MyGetOptData;
 

	
 
static void MyGetOptInit(MyGetOptData *md, int argc, char **argv, const char *options)
 
{
 
	md->cont = NULL;
 
	md->numleft = argc;
 
	md->argv = argv;
 
	md->options = options;
 
}
 

	
 
static int MyGetOpt(MyGetOptData *md)
 
{
 
	char *s,*r,*t;
 

	
 
	s = md->cont;
 
	if (s != NULL)
 
		goto md_continue_here;
 

	
 
	for (;;) {
 
		if (--md->numleft < 0) return -1;
 

	
 
		s = *md->argv++;
 
		if (*s == '-') {
 
md_continue_here:;
 
			s++;
 
			if (*s != 0) {
 
				// Found argument, try to locate it in options.
 
				if (*s == ':' || (r = strchr(md->options, *s)) == NULL) {
 
					// ERROR!
 
					return -2;
 
				}
 
				if (r[1] == ':') {
 
					// Item wants an argument. Check if the argument follows, or if it comes as a separate arg.
 
					if (!*(t = s + 1)) {
 
						// It comes as a separate arg. Check if out of args?
 
						if (--md->numleft < 0 || *(t = *md->argv) == '-') {
 
							// Check if item is optional?
 
							if (r[2] != ':')
 
								return -2;
 
							md->numleft++;
 
							t = NULL;
 
						} else {
 
							md->argv++;
 
						}
 
					}
 
					md->opt = t;
 
					md->cont = NULL;
 
					return *s;
 
				}
 
				md->opt = NULL;
 
				md->cont = s;
 
				return *s;
 
			}
 
		} else {
 
			// This is currently not supported.
 
			return -2;
 
		}
 
	}
 
}
 

	
 

	
 
static void ParseResolution(int res[2], const char *s)
 
{
 
	char *t = strchr(s, 'x');
 
	if (t == NULL) {
 
		ShowInfoF("Invalid resolution '%s'", s);
 
		return;
 
	}
 

	
 
	res[0] = clamp(strtoul(s, NULL, 0), 64, MAX_SCREEN_WIDTH);
 
	res[1] = clamp(strtoul(t + 1, NULL, 0), 64, MAX_SCREEN_HEIGHT);
 
}
 

	
 
static void InitializeDynamicVariables(void)
 
{
 
	/* Dynamic stuff needs to be initialized somewhere... */
 
	_town_sort     = NULL;
 
	_industry_sort = NULL;
 
}
 

	
 
static void UnInitializeDynamicVariables(void)
 
{
 
	/* Dynamic stuff needs to be free'd somewhere... */
 
	CleanPool(&_Town_pool);
 
	CleanPool(&_Industry_pool);
 
	CleanPool(&_Station_pool);
 
	CleanPool(&_Vehicle_pool);
 
	CleanPool(&_Sign_pool);
 
	CleanPool(&_Order_pool);
 

	
 
	free((void*)_town_sort);
 
	free((void*)_industry_sort);
 
}
 

	
 
static void UnInitializeGame(void)
 
{
 
	UnInitWindowSystem();
 

	
 
	free(_config_file);
 
}
 

	
 
static void LoadIntroGame(void)
 
{
 
	char filename[256];
 

	
 
	_game_mode = GM_MENU;
 
	CLRBITS(_display_opt, DO_TRANS_BUILDINGS); // don't make buildings transparent in intro
 
	_opt_ptr = &_opt_newgame;
 
	ResetGRFConfig(false);
 

	
 
	// Setup main window
 
	ResetWindowSystem();
 
	SetupColorsAndInitialWindow();
 

	
 
	// Generate a world.
 
	snprintf(filename, lengthof(filename), "%sopntitle.dat",  _paths.data_dir);
 
#if defined SECOND_DATA_DIR
 
	if (SaveOrLoad(filename, SL_LOAD) != SL_OK) {
 
		snprintf(filename, lengthof(filename), "%sopntitle.dat",  _paths.second_data_dir);
 
	}
 
#endif
 
	if (SaveOrLoad(filename, SL_LOAD) != SL_OK) {
 
		GenerateWorld(GW_EMPTY, 64, 64); // if failed loading, make empty world.
 
		WaitTillGeneratedWorld();
 
	}
 

	
 
	_pause = 0;
 
	SetLocalPlayer(0);
 
	/* Make sure you can't scroll in the menu */
 
	_scrolling_viewport = 0;
 
	_cursor.fix_at = false;
 
	MarkWholeScreenDirty();
 

	
 
	// Play main theme
 
	if (_music_driver->is_song_playing()) ResetMusic();
 
}
 

	
 
#if defined(UNIX) && !defined(__MORPHOS__)
 
extern void DedicatedFork(void);
 
#endif
 

	
 
int ttd_main(int argc, char *argv[])
 
{
 
	MyGetOptData mgo;
 
	int i;
 
	const char *optformat;
 
	char musicdriver[16], sounddriver[16], videodriver[16];
 
	int resolution[2] = {0,0};
 
	Year startyear = INVALID_YEAR;
 
	uint generation_seed = GENERATE_NEW_SEED;
 
	bool dedicated = false;
 
	bool network   = false;
 
	bool save_config = true;
 
	char *network_conn = NULL;
 
	char *dedicated_host = NULL;
 
	uint16 dedicated_port = 0;
 

	
 
	musicdriver[0] = sounddriver[0] = videodriver[0] = 0;
 

	
 
	_game_mode = GM_MENU;
 
	_switch_mode = SM_MENU;
 
	_switch_mode_errorstr = INVALID_STRING_ID;
 
	_dedicated_forks = false;
 
	_config_file = NULL;
 

	
 
	// The last param of the following function means this:
 
	//   a letter means: it accepts that param (e.g.: -h)
 
	//   a ':' behind it means: it need a param (e.g.: -m<driver>)
 
	//   a '::' behind it means: it can optional have a param (e.g.: -d<debug>)
 
	optformat = "m:s:v:hD::n::eit:d::r:g::G:c:x"
 
#if !defined(__MORPHOS__) && !defined(__AMIGA__) && !defined(WIN32)
 
		"f"
 
#endif
 
	;
 

	
 
	MyGetOptInit(&mgo, argc-1, argv+1, optformat);
 
	while ((i = MyGetOpt(&mgo)) != -1) {
 
		switch (i) {
 
		case 'm': ttd_strlcpy(musicdriver, mgo.opt, sizeof(musicdriver)); break;
 
		case 's': ttd_strlcpy(sounddriver, mgo.opt, sizeof(sounddriver)); break;
 
		case 'v': ttd_strlcpy(videodriver, mgo.opt, sizeof(videodriver)); break;
 
		case 'D':
 
			strcpy(musicdriver, "null");
 
			strcpy(sounddriver, "null");
 
			strcpy(videodriver, "dedicated");
 
			dedicated = true;
 
			if (mgo.opt != NULL)
 
			{
 
				/* Use the existing method for parsing (openttd -n).
 
				 * However, we do ignore the #player part. */
 
				const char *temp = NULL;
 
				const char *port = NULL;
 
				ParseConnectionString(&temp, &port, mgo.opt);
 
				if (*mgo.opt != '\0') dedicated_host = mgo.opt;
 
				if (port != NULL) dedicated_port = atoi(port);
 
			}
 
			break;
 
		case 'f': _dedicated_forks = true; break;
 
		case 'n':
 
			network = true;
 
			network_conn = mgo.opt; // optional IP parameter, NULL if unset
 
			break;
 
		case 'r': ParseResolution(resolution, mgo.opt); break;
 
		case 't': startyear = atoi(mgo.opt); break;
 
		case 'd': {
 
#if defined(WIN32)
 
				CreateConsole();
 
#endif
 
				if (mgo.opt != NULL) SetDebugString(mgo.opt);
 
			} break;
 
		case 'e': _switch_mode = SM_EDITOR; break;
 
		case 'i': _use_dos_palette = true; break;
 
		case 'g':
 
			if (mgo.opt != NULL) {
 
				strcpy(_file_to_saveload.name, mgo.opt);
 
				_switch_mode = SM_LOAD;
 
			} else {
 
				_switch_mode = SM_NEWGAME;
 
			}
 
			break;
 
		case 'G': generation_seed = atoi(mgo.opt); break;
 
		case 'c': _config_file = strdup(mgo.opt); break;
 
		case 'x': save_config = false; break;
 
		case -2:
 
		case 'h':
 
			showhelp();
 
			return 0;
 
		}
 
	}
 

	
 
	DeterminePaths();
 
	CheckExternalFiles();
 

	
 
#if defined(UNIX) && !defined(__MORPHOS__)
 
	// We must fork here, or we'll end up without some resources we need (like sockets)
 
	if (_dedicated_forks)
 
		DedicatedFork();
 
#endif
 

	
 
	LoadFromConfig();
 
	CheckConfig();
 
	LoadFromHighScore();
 

	
 
	// override config?
 
	if (musicdriver[0]) ttd_strlcpy(_ini_musicdriver, musicdriver, sizeof(_ini_musicdriver));
 
	if (sounddriver[0]) ttd_strlcpy(_ini_sounddriver, sounddriver, sizeof(_ini_sounddriver));
 
	if (videodriver[0]) ttd_strlcpy(_ini_videodriver, videodriver, sizeof(_ini_videodriver));
 
	if (resolution[0]) { _cur_resolution[0] = resolution[0]; _cur_resolution[1] = resolution[1]; }
 
	if (startyear != INVALID_YEAR) _patches_newgame.starting_year = startyear;
 
	if (generation_seed != GENERATE_NEW_SEED) _patches_newgame.generation_seed = generation_seed;
 

	
 
	if (dedicated_host) snprintf(_network_server_bind_ip_host, NETWORK_HOSTNAME_LENGTH, "%s", dedicated_host);
 
	if (dedicated_port) _network_server_port = dedicated_port;
 
	if (_dedicated_forks && !dedicated) _dedicated_forks = false;
 

	
 
	// enumerate language files
 
	InitializeLanguagePacks();
 

	
 
	// initialize screenshot formats
 
	InitializeScreenshotFormats();
 

	
 
	// initialize airport state machines
 
	InitializeAirports();
 

	
 
	/* initialize all variables that are allocated dynamically */
 
	InitializeDynamicVariables();
 

	
 
	/* start the AI */
 
	AI_Initialize();
 

	
 
	// Sample catalogue
 
	DEBUG(misc, 1, "Loading sound effects...");
 
	MxInitialize(11025);
 
	SoundInitialize("sample.cat");
 

	
 
	/* Initialize FreeType */
 
	InitFreeType();
 

	
 
	// This must be done early, since functions use the InvalidateWindow* calls
 
	InitWindowSystem();
 

	
 
	/* Initialize game palette */
 
	GfxInitPalettes();
 

	
 
	DEBUG(driver, 1, "Loading drivers...");
 
	LoadDriver(SOUND_DRIVER, _ini_sounddriver);
 
	LoadDriver(MUSIC_DRIVER, _ini_musicdriver);
 
	LoadDriver(VIDEO_DRIVER, _ini_videodriver); // load video last, to prevent an empty window while sound and music loads
 
	_savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
 

	
 
	// restore saved music volume
 
	_music_driver->set_volume(msf.music_vol);
 

	
 
	NetworkStartUp(); // initialize network-core
 

	
 
	ScanNewGRFFiles();
 

	
 
	_opt_ptr = &_opt_newgame;
 
	ResetGRFConfig(false);
 

	
 
	/* XXX - ugly hack, if diff_level is 9, it means we got no setting from the config file */
 
	if (_opt_newgame.diff_level == 9) SetDifficultyLevel(0, &_opt_newgame);
 

	
 
	/* Make sure _patches is filled with _patches_newgame if we switch to a game directly */
 
	if (_switch_mode != SM_NONE) {
 
		_opt = _opt_newgame;
 
		UpdatePatches();
 
	}
 

	
 
	// initialize the ingame console
 
	IConsoleInit();
 
	_cursor.in_window = true;
 
	InitializeGUI();
 
	IConsoleCmdExec("exec scripts/autoexec.scr 0");
 

	
 
	GenerateWorld(GW_EMPTY, 64, 64); // Make the viewport initialization happy
 
	WaitTillGeneratedWorld();
 

	
 
#ifdef ENABLE_NETWORK
 
	if (network && _network_available) {
 
		if (network_conn != NULL) {
 
			const char *port = NULL;
 
			const char *player = NULL;
 
			uint16 rport;
 

	
 
			rport = NETWORK_DEFAULT_PORT;
 
			_network_playas = PLAYER_NEW_COMPANY;
 

	
 
			ParseConnectionString(&player, &port, network_conn);
 

	
 
			if (player != NULL) {
 
				_network_playas = atoi(player);
 

	
 
				if (_network_playas != PLAYER_SPECTATOR) {
 
					_network_playas--;
 
					if (!IsValidPlayer(_network_playas)) return false;
 
				}
 
			}
 
			if (port != NULL) rport = atoi(port);
 

	
 
			LoadIntroGame();
 
			_switch_mode = SM_NONE;
 
			NetworkClientConnectGame(network_conn, rport);
 
		}
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	_video_driver->main_loop();
 

	
 
	WaitTillSaved();
 
	IConsoleFree();
 

	
 
	if (_network_available) NetworkShutDown(); // Shut down the network and close any open connections
 

	
 
	_video_driver->stop();
 
	_music_driver->stop();
 
	_sound_driver->stop();
 

	
 
	/* only save config if we have to */
 
	if (save_config) {
 
		SaveToConfig();
 
		SaveToHighScore();
 
	}
 

	
 
	// uninitialize airport state machines
 
	UnInitializeAirports();
 

	
 
	/* uninitialize variables that are allocated dynamic */
 
	UnInitializeDynamicVariables();
 

	
 
	/* stop the AI */
 
	AI_Uninitialize();
 

	
 
	/* Close all and any open filehandles */
 
	FioCloseAll();
 
	UnInitializeGame();
 

	
 
	return 0;
 
}
 

	
 
void HandleExitGameRequest(void)
 
{
 
	if (_game_mode == GM_MENU) { // do not ask to quit on the main screen
 
		_exit_game = true;
 
	} else if (_patches.autosave_on_exit) {
 
		DoExitSave();
 
		_exit_game = true;
 
	} else {
 
		AskExitGame();
 
	}
 
}
 

	
 

	
 
/** Mutex so that only one thread can communicate with the main program
 
 * at any given time */
 
static ThreadMsg _message = MSG_OTTD_NO_MESSAGE;
 

	
 
static inline void OTTD_ReleaseMutex(void) {_message = MSG_OTTD_NO_MESSAGE;}
 
static inline ThreadMsg OTTD_PollThreadEvent(void) {return _message;}
 

	
 
/** Called by running thread to execute some action in the main game.
 
 * It will stall as long as the mutex is not freed (handled) by the game */
 
void OTTD_SendThreadMessage(ThreadMsg msg)
 
{
 
	if (_exit_game) return;
 
	while (_message != MSG_OTTD_NO_MESSAGE) CSleep(10);
 

	
 
	_message = msg;
 
}
 

	
 

	
 
/** Handle the user-messages sent to us
 
 * @param message message sent
 
 */
 
static void ProcessSentMessage(ThreadMsg message)
 
{
 
	switch (message) {
 
		case MSG_OTTD_SAVETHREAD_DONE:  SaveFileDone(); break;
 
		case MSG_OTTD_SAVETHREAD_ERROR: SaveFileError(); break;
 
		default: NOT_REACHED();
 
	}
 

	
 
	OTTD_ReleaseMutex(); // release mutex so that other threads, messages can be handled
 
}
 

	
 
static void ShowScreenshotResult(bool b)
 
{
 
	if (b) {
 
		SetDParamStr(0, _screenshot_name);
 
		ShowErrorMessage(INVALID_STRING_ID, STR_031B_SCREENSHOT_SUCCESSFULLY, 0, 0);
 
	} else {
 
		ShowErrorMessage(INVALID_STRING_ID, STR_031C_SCREENSHOT_FAILED, 0, 0);
 
	}
 

	
 
}
 

	
 
static void MakeNewGameDone(void)
 
{
 
	/* In a dedicated server, the server does not play */
 
	if (_network_dedicated) {
 
		SetLocalPlayer(PLAYER_SPECTATOR);
 
		return;
 
	}
 

	
 
	/* Create a single player */
 
	DoStartupNewPlayer(false);
 

	
 
	SetLocalPlayer(0);
 
	_current_player = _local_player;
 
	DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_SET_AUTOREPLACE);
 

	
 
	SettingsDisableElrail(_patches.disable_elrails);
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
static void MakeNewGame(bool from_heightmap)
 
{
 
	_game_mode = GM_NORMAL;
 

	
 
	ResetGRFConfig(true);
 

	
 
	GenerateWorldSetCallback(&MakeNewGameDone);
 
	GenerateWorld(from_heightmap ? GW_HEIGHTMAP : GW_NEWGAME, 1 << _patches.map_x, 1 << _patches.map_y);
 
}
 

	
 
static void MakeNewEditorWorldDone(void)
 
{
 
	SetLocalPlayer(OWNER_NONE);
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
static void MakeNewEditorWorld(void)
 
{
 
	_game_mode = GM_EDITOR;
 

	
 
	ResetGRFConfig(true);
 

	
 
	GenerateWorldSetCallback(&MakeNewEditorWorldDone);
 
	GenerateWorld(GW_EMPTY, 1 << _patches.map_x, 1 << _patches.map_y);
 
}
 

	
 
void StartupPlayers(void);
 
void StartupDisasters(void);
 
extern void StartupEconomy(void);
 

	
 
/**
 
 * Start Scenario starts a new game based on a scenario.
 
 * Eg 'New Game' --> select a preset scenario
 
 * This starts a scenario based on your current difficulty settings
 
 */
 
static void StartScenario(void)
 
{
 
	_game_mode = GM_NORMAL;
 

	
 
	// invalid type
 
	if (_file_to_saveload.mode == SL_INVALID) {
 
		DEBUG(sl, 0, "Savegame is obsolete or invalid format: '%s'", _file_to_saveload.name);
 
		ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
 
		_game_mode = GM_MENU;
 
		return;
 
	}
 

	
 
	// Reinitialize windows
 
	ResetWindowSystem();
 

	
 
	SetupColorsAndInitialWindow();
 

	
 
	ResetGRFConfig(true);
 

	
 
	// Load game
 
	if (SaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode) != SL_OK) {
 
		LoadIntroGame();
 
		ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
 
	}
 

	
 
	_opt_ptr = &_opt;
 
	_opt_ptr->diff = _opt_newgame.diff;
 
	_opt.diff_level = _opt_newgame.diff_level;
 

	
 
	// Inititalize data
 
	StartupEconomy();
 
	StartupPlayers();
 
	StartupEngines();
 
	StartupDisasters();
 

	
 
	SetLocalPlayer(0);
 
	_current_player = _local_player;
 
	DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_SET_AUTOREPLACE);
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
bool SafeSaveOrLoad(const char *filename, int mode, int newgm)
 
{
 
	byte ogm = _game_mode;
 

	
 
	_game_mode = newgm;
 
	switch (SaveOrLoad(filename, mode)) {
 
		case SL_OK: return true;
 

	
 
		case SL_REINIT:
 
			switch (ogm) {
 
				case GM_MENU:   LoadIntroGame();      break;
 
				case GM_EDITOR: MakeNewEditorWorld(); break;
 
				default:        MakeNewGame(false);   break;
 
			}
 
			return false;
 

	
 
		default:
 
			_game_mode = ogm;
 
			return false;
 
	}
 
}
 

	
 
void SwitchMode(int new_mode)
 
{
 
#ifdef ENABLE_NETWORK
 
	// If we are saving something, the network stays in his current state
 
	if (new_mode != SM_SAVE) {
 
		// If the network is active, make it not-active
 
		if (_networking) {
 
			if (_network_server && (new_mode == SM_LOAD || new_mode == SM_NEWGAME)) {
 
				NetworkReboot();
 
				NetworkUDPStop();
 
			} else {
 
				NetworkDisconnect();
 
				NetworkUDPStop();
 
			}
 
		}
 

	
 
		// If we are a server, we restart the server
 
		if (_is_network_server) {
 
			// But not if we are going to the menu
 
			if (new_mode != SM_MENU) {
 
				NetworkServerStart();
 
			} else {
 
				// This client no longer wants to be a network-server
 
				_is_network_server = false;
 
			}
 
		}
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	switch (new_mode) {
 
	case SM_EDITOR: /* Switch to scenario editor */
 
		MakeNewEditorWorld();
 
		break;
 

	
 
	case SM_NEWGAME: /* New Game --> 'Random game' */
 
#ifdef ENABLE_NETWORK
 
		if (_network_server) {
 
			snprintf(_network_game_info.map_name, lengthof(_network_game_info.map_name), "Random Map");
 
		}
 
#endif /* ENABLE_NETWORK */
 
		MakeNewGame(false);
 
		break;
 

	
 
	case SM_START_SCENARIO: /* New Game --> Choose one of the preset scenarios */
 
#ifdef ENABLE_NETWORK
 
		if (_network_server) {
 
			snprintf(_network_game_info.map_name, lengthof(_network_game_info.map_name), "%s (Loaded scenario)", _file_to_saveload.title);
 
		}
 
#endif /* ENABLE_NETWORK */
 
		StartScenario();
 
		break;
 

	
 
	case SM_LOAD: { /* Load game, Play Scenario */
 
		_opt_ptr = &_opt;
 
		ResetGRFConfig(true);
 

	
 
		if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) {
 
			LoadIntroGame();
 
			ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
 
		} else {
 
			/* Update the local player for a loaded game. It is either always
 
			 * player #1 (eg 0) or in the case of a dedicated server a spectator */
 
			SetLocalPlayer(_network_dedicated ? PLAYER_SPECTATOR : 0);
 
			DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // decrease pause counter (was increased from opening load dialog)
 
#ifdef ENABLE_NETWORK
 
			if (_network_server) {
 
				snprintf(_network_game_info.map_name, lengthof(_network_game_info.map_name), "%s (Loaded game)", _file_to_saveload.title);
 
			}
 
#endif /* ENABLE_NETWORK */
 
		}
 
		break;
 
	}
 

	
 
	case SM_START_HEIGHTMAP: /* Load a heightmap and start a new game from it */
 
#ifdef ENABLE_NETWORK
 
		if (_network_server) {
 
			snprintf(_network_game_info.map_name, lengthof(_network_game_info.map_name), "%s (Heightmap)", _file_to_saveload.title);
 
		}
 
#endif /* ENABLE_NETWORK */
 
		MakeNewGame(true);
 
		break;
 

	
 
	case SM_LOAD_HEIGHTMAP: /* Load heightmap from scenario editor */
 
		SetLocalPlayer(OWNER_NONE);
 

	
 
		GenerateWorld(GW_HEIGHTMAP, 1 << _patches.map_x, 1 << _patches.map_y);
 
		MarkWholeScreenDirty();
 
		break;
 

	
 
	case SM_LOAD_SCENARIO: { /* Load scenario from scenario editor */
 
		if (SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_EDITOR)) {
 
			Player *p;
 

	
 
			_opt_ptr = &_opt;
 

	
 
			SetLocalPlayer(OWNER_NONE);
 
			_generating_world = true;
 
			/* Delete all players */
 
			FOR_ALL_PLAYERS(p) {
 
				if (p->is_active) {
 
					ChangeOwnershipOfPlayerItems(p->index, PLAYER_SPECTATOR);
 
					p->is_active = false;
 
				}
 
			}
 
			_generating_world = false;
 
			_patches_newgame.starting_year = _cur_year;
 
			// delete all stations owned by a player
 
			DeleteAllPlayerStations();
 
		} else {
 
			ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
 
		}
 
		break;
 
	}
 

	
 
	case SM_MENU: /* Switch to game intro menu */
 
		LoadIntroGame();
 
		break;
 

	
 
	case SM_SAVE: /* Save game */
 
		if (SaveOrLoad(_file_to_saveload.name, SL_SAVE) != SL_OK) {
 
			ShowErrorMessage(INVALID_STRING_ID, STR_4007_GAME_SAVE_FAILED, 0, 0);
 
		} else {
 
			DeleteWindowById(WC_SAVELOAD, 0);
 
		}
 
		break;
 

	
 
	case SM_GENRANDLAND: /* Generate random land within scenario editor */
 
		SetLocalPlayer(OWNER_NONE);
 
		GenerateWorld(GW_RANDOM, 1 << _patches.map_x, 1 << _patches.map_y);
 
		// XXX: set date
 
		MarkWholeScreenDirty();
 
		break;
 
	}
 

	
 
	if (_switch_mode_errorstr != INVALID_STRING_ID) {
 
		ShowErrorMessage(INVALID_STRING_ID, _switch_mode_errorstr, 0, 0);
 
	}
 
}
 

	
 

	
 
// State controlling game loop.
 
// The state must not be changed from anywhere
 
// but here.
 
// That check is enforced in DoCommand.
 
void StateGameLoop(void)
 
{
 
	// dont execute the state loop during pause
 
	if (_pause) return;
 
	if (IsGeneratingWorld()) return;
 

	
 
	if (_game_mode == GM_EDITOR) {
 
		RunTileLoop();
 
		CallVehicleTicks();
 
		CallLandscapeTick();
 
		CallWindowTickEvent();
 
		NewsLoop();
 
	} else {
 
		// All these actions has to be done from OWNER_NONE
 
		//  for multiplayer compatibility
 
		PlayerID p = _current_player;
 
		_current_player = OWNER_NONE;
 

	
 
		AnimateAnimatedTiles();
 
		IncreaseDate();
 
		RunTileLoop();
 
		CallVehicleTicks();
 
		CallLandscapeTick();
 

	
 
		AI_RunGameLoop();
 

	
 
		CallWindowTickEvent();
 
		NewsLoop();
 
		_current_player = p;
 
	}
 
}
 

	
 
static void DoAutosave(void)
 
{
 
	char buf[200];
 

	
 
	if (_patches.keep_all_autosave && _local_player != PLAYER_SPECTATOR) {
 
		const Player *p = GetPlayer(_local_player);
 
		char* s = buf;
 

	
 
		s += snprintf(buf, lengthof(buf), "%s%s", _paths.autosave_dir, PATHSEP);
 

	
 
		SetDParam(0, p->name_1);
 
		SetDParam(1, p->name_2);
 
		SetDParam(2, _date);
 
		s = GetString(s, STR_4004, lastof(buf));
 
		strecpy(s, ".sav", lastof(buf));
 
	} else { /* generate a savegame name and number according to _patches.max_num_autosaves */
 
		snprintf(buf, lengthof(buf), "%s%sautosave%d.sav", _paths.autosave_dir, PATHSEP, _autosave_ctr);
 

	
 
		_autosave_ctr++;
 
		if (_autosave_ctr >= _patches.max_num_autosaves) {
 
			// we reached the limit for numbers of autosaves. We will start over
 
			_autosave_ctr = 0;
 
		}
 
	}
 

	
 
	DEBUG(sl, 2, "Autosaving to '%s'", buf);
 
	if (SaveOrLoad(buf, SL_SAVE) != SL_OK)
 
		ShowErrorMessage(INVALID_STRING_ID, STR_AUTOSAVE_FAILED, 0, 0);
 
}
 

	
 
static void ScrollMainViewport(int x, int y)
 
{
 
	if (_game_mode != GM_MENU) {
 
		Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
 
		assert(w);
 

	
 
		WP(w,vp_d).scrollpos_x += x << w->viewport->zoom;
 
		WP(w,vp_d).scrollpos_y += y << w->viewport->zoom;
 
	}
 
}
 

	
 
static const int8 scrollamt[16][2] = {
 
	{ 0,  0},
 
	{-2,  0}, //  1 : left
 
	{ 0, -2}, //  2 : up
 
	{-2, -1}, //  3 : left + up
 
	{ 2,  0}, //  4 : right
 
	{ 0,  0}, //  5 : left + right
 
	{ 2, -1}, //  6 : right + up
 
	{ 0, -2}, //  7 : left + right + up = up
 
	{ 0  ,2}, //  8 : down
 
	{-2  ,1}, //  9 : down+left
 
	{ 0,  0}, // 10 : impossible
 
	{-2,  0}, // 11 : left + up + down = left
 
	{ 2,  1}, // 12 : down+right
 
	{ 0,  2}, // 13 : left + right + down = down
 
	{ 0, -2}, // 14 : left + right + up = up
 
	{ 0,  0}, // 15 : impossible
 
};
 

	
 
static void HandleKeyScrolling(void)
 
{
 
	if (_dirkeys && !_no_scroll) {
 
		int factor = _shift_pressed ? 50 : 10;
 
		ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
 
	}
 
}
 

	
 
void GameLoop(void)
 
{
 
	ThreadMsg message;
 

	
 
	if ((message = OTTD_PollThreadEvent()) != 0) ProcessSentMessage(message);
 

	
 
	// autosave game?
 
	if (_do_autosave) {
 
		_do_autosave = false;
 
		DoAutosave();
 
		RedrawAutosave();
 
	}
 

	
 
	// handle scrolling of the main window
 
	HandleKeyScrolling();
 

	
 
	// make a screenshot?
 
	if (IsScreenshotRequested()) ShowScreenshotResult(MakeScreenshot());
 

	
 
	// switch game mode?
 
	if (_switch_mode != SM_NONE) {
 
		SwitchMode(_switch_mode);
 
		_switch_mode = SM_NONE;
 
	}
 

	
 
	IncreaseSpriteLRU();
 
	InteractiveRandom();
 

	
 
	if (_scroller_click_timeout > 3) {
 
		_scroller_click_timeout -= 3;
 
	} else {
 
		_scroller_click_timeout = 0;
 
	}
 

	
 
	_caret_timer += 3;
 
	_timer_counter += 8;
 
	CursorTick();
 

	
 
#ifdef ENABLE_NETWORK
 
	// Check for UDP stuff
 
	if (_network_available) NetworkUDPGameLoop();
 

	
 
	if (_networking && !IsGeneratingWorld()) {
 
		// Multiplayer
 
		NetworkGameLoop();
 
	} else {
 
		if (_network_reconnect > 0 && --_network_reconnect == 0) {
 
			// This means that we want to reconnect to the last host
 
			// We do this here, because it means that the network is really closed
 
			NetworkClientConnectGame(_network_last_host, _network_last_port);
 
		}
 
		// Singleplayer
 
		StateGameLoop();
 
	}
 
#else
 
	StateGameLoop();
 
#endif /* ENABLE_NETWORK */
 

	
 
	if (!_pause && _display_opt & DO_FULL_ANIMATION) DoPaletteAnimations();
 

	
 
	if (!_pause || _cheats.build_in_pause.value) MoveAllTextEffects();
 

	
 
	InputLoop();
 

	
 
	MusicLoop();
 
}
 

	
 
void BeforeSaveGame(void)
 
{
 
	const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
 

	
 
	if (w != NULL) {
 
		_saved_scrollpos_x = WP(w, const vp_d).scrollpos_x;
 
		_saved_scrollpos_y = WP(w, const vp_d).scrollpos_y;
 
		_saved_scrollpos_zoom = w->viewport->zoom;
 
	}
 
}
 

	
 
static void ConvertTownOwner(void)
 
{
 
	TileIndex tile;
 

	
 
	for (tile = 0; tile != MapSize(); tile++) {
 
		switch (GetTileType(tile)) {
 
			case MP_STREET:
 
				if (IsLevelCrossing(tile) && GetCrossingRoadOwner(tile) & 0x80) {
 
					SetCrossingRoadOwner(tile, OWNER_TOWN);
 
				}
 
				/* FALLTHROUGH */
 

	
 
			case MP_TUNNELBRIDGE:
 
				if (GetTileOwner(tile) & 0x80) SetTileOwner(tile, OWNER_TOWN);
 
				break;
 

	
 
			default: break;
 
		}
 
	}
 
}
 

	
 
// before savegame version 4, the name of the company determined if it existed
 
static void CheckIsPlayerActive(void)
 
{
 
	Player *p;
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->name_1 != 0) p->is_active = true;
 
	}
 
}
 

	
 
// since savegame version 4.1, exclusive transport rights are stored at towns
 
static void UpdateExclusiveRights(void)
 
{
 
	Town *t;
 

	
 
	FOR_ALL_TOWNS(t) {
 
		t->exclusivity = (byte)-1;
 
	}
 

	
 
	/* FIXME old exclusive rights status is not being imported (stored in s->blocked_months_obsolete)
 
	 *   could be implemented this way:
 
	 * 1.) Go through all stations
 
	 *     Build an array town_blocked[ town_id ][ player_id ]
 
	 *     that stores if at least one station in that town is blocked for a player
 
	 * 2.) Go through that array, if you find a town that is not blocked for
 
	 *     one player, but for all others, then give him exclusivity.
 
	 */
 
}
 

	
 
static const byte convert_currency[] = {
 
	 0,  1, 12,  8,  3,
 
	10, 14, 19,  4,  5,
 
	 9, 11, 13,  6, 17,
 
	16, 22, 21,  7, 15,
 
	18,  2, 20, };
 

	
 
// since savegame version 4.2 the currencies are arranged differently
 
static void UpdateCurrencies(void)
 
{
 
	_opt.currency = convert_currency[_opt.currency];
 
}
 

	
 
/* Up to revision 1413 the invisible tiles at the southern border have not been
 
 * MP_VOID, even though they should have. This is fixed by this function
 
 */
 
static void UpdateVoidTiles(void)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < MapMaxY(); ++i) MakeVoid(i * MapSizeX() + MapMaxX());
 
	for (i = 0; i < MapSizeX(); ++i) MakeVoid(MapSizeX() * MapMaxY() + i);
 
}
 

	
 
// since savegame version 6.0 each sign has an "owner", signs without owner (from old games are set to 255)
 
static void UpdateSignOwner(void)
 
{
 
	Sign *si;
 

	
 
	FOR_ALL_SIGNS(si) si->owner = OWNER_NONE;
 
}
 

	
 
extern void UpdateOldAircraft( void );
 
extern void UpdateOilRig( void );
 

	
 

	
 
static inline RailType UpdateRailType(RailType rt, RailType min)
 
{
 
	return rt >= min ? (RailType)(rt + 1): rt;
 
}
 

	
 
bool AfterLoadGame(void)
 
{
 
	Window *w;
 
	ViewPort *vp;
 
	Player *p;
 

	
 
	// in version 2.1 of the savegame, town owner was unified.
 
	if (CheckSavegameVersionOldStyle(2, 1)) ConvertTownOwner();
 

	
 
	// from version 4.1 of the savegame, exclusive rights are stored at towns
 
	if (CheckSavegameVersionOldStyle(4, 1)) UpdateExclusiveRights();
 

	
 
	// from version 4.2 of the savegame, currencies are in a different order
 
	if (CheckSavegameVersionOldStyle(4, 2)) UpdateCurrencies();
 

	
 
	// from version 6.1 of the savegame, signs have an "owner"
 
	if (CheckSavegameVersionOldStyle(6, 1)) UpdateSignOwner();
 

	
 
	/* In old version there seems to be a problem that water is owned by
 
	    OWNER_NONE, not OWNER_WATER.. I can't replicate it for the current
 
	    (4.3) version, so I just check when versions are older, and then
 
	    walk through the whole map.. */
 
	if (CheckSavegameVersionOldStyle(4, 3)) {
 
		TileIndex tile = TileXY(0, 0);
 
		uint w = MapSizeX();
 
		uint h = MapSizeY();
 

	
 
		BEGIN_TILE_LOOP(tile_cur, w, h, tile)
 
			if (IsTileType(tile_cur, MP_WATER) && GetTileOwner(tile_cur) >= MAX_PLAYERS)
 
				SetTileOwner(tile_cur, OWNER_WATER);
 
		END_TILE_LOOP(tile_cur, w, h, tile)
 
	}
 

	
 
	// convert road side to my format.
 
	if (_opt.road_side) _opt.road_side = 1;
 

	
 
	/* Check all NewGRFs are present */
 
	if (!IsGoodGRFConfigList()) return false;
 

	
 
	/* Update current year
 
	 * must be done before loading sprites as some newgrfs check it */
 
	SetDate(_date);
 

	
 
	// Load the sprites
 
	GfxLoadSprites();
 
	LoadStringWidthTable();
 

	
 
	/* Connect front and rear engines of multiheaded trains and converts
 
	 * subtype to the new format */
 
	if (CheckSavegameVersionOldStyle(17, 1)) ConvertOldMultiheadToNew();
 

	
 
	/* Connect front and rear engines of multiheaded trains */
 
	ConnectMultiheadedTrains();
 

	
 
	// reinit the landscape variables (landscape might have changed)
 
	InitializeLandscapeVariables(true);
 

	
 
	// Update all vehicles
 
	AfterLoadVehicles();
 

	
 
	// Update all waypoints
 
	if (CheckSavegameVersion(12)) FixOldWaypoints();
 

	
 
	UpdateAllWaypointSigns();
 

	
 
	// in version 2.2 of the savegame, we have new airports
 
	if (CheckSavegameVersionOldStyle(2, 2)) UpdateOldAircraft();
 

	
 
	UpdateAllStationVirtCoord();
 

	
 
	// Setup town coords
 
	AfterLoadTown();
 
	UpdateAllSignVirtCoords();
 

	
 
	// make sure there is a town in the game
 
	if (_game_mode == GM_NORMAL && !ClosestTownFromTile(0, (uint)-1)) {
 
		_error_message = STR_NO_TOWN_IN_SCENARIO;
 
		return false;
 
	}
 

	
 
	// Initialize windows
 
	ResetWindowSystem();
 
	SetupColorsAndInitialWindow();
 

	
 
	w = FindWindowById(WC_MAIN_WINDOW, 0);
 

	
 
	WP(w,vp_d).scrollpos_x = _saved_scrollpos_x;
 
	WP(w,vp_d).scrollpos_y = _saved_scrollpos_y;
 

	
 
	vp = w->viewport;
 
	vp->zoom = _saved_scrollpos_zoom;
 
	vp->virtual_width = vp->width << vp->zoom;
 
	vp->virtual_height = vp->height << vp->zoom;
 

	
 
	// in version 4.1 of the savegame, is_active was introduced to determine
 
	// if a player does exist, rather then checking name_1
 
	if (CheckSavegameVersionOldStyle(4, 1)) CheckIsPlayerActive();
 

	
 
	// the void tiles on the southern border used to belong to a wrong class (pre 4.3).
 
	if (CheckSavegameVersionOldStyle(4, 3)) UpdateVoidTiles();
 

	
 
	// If Load Scenario / New (Scenario) Game is used,
 
	//  a player does not exist yet. So create one here.
 
	// 1 exeption: network-games. Those can have 0 players
 
	//   But this exeption is not true for network_servers!
 
	if (!_players[0].is_active && (!_networking || (_networking && _network_server)))
 
		DoStartupNewPlayer(false);
 

	
 
	DoZoomInOutWindow(ZOOM_NONE, w); // update button status
 
	MarkWholeScreenDirty();
 

	
 
	// In 5.1, Oilrigs have been moved (again)
 
	if (CheckSavegameVersionOldStyle(5, 1)) UpdateOilRig();
 

	
 
	/* In version 6.1 we put the town index in the map-array. To do this, we need
 
	 *  to use m2 (16bit big), so we need to clean m2, and that is where this is
 
	 *  all about ;) */
 
	if (CheckSavegameVersionOldStyle(6, 1)) {
 
		BEGIN_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0) {
 
			switch (GetTileType(tile)) {
 
				case MP_HOUSE:
 
					_m[tile].m4 = _m[tile].m2;
 
					SetTownIndex(tile, CalcClosestTownFromTile(tile, (uint)-1)->index);
 
					break;
 

	
 
				case MP_STREET:
 
					_m[tile].m4 |= (_m[tile].m2 << 4);
 
					if (IsTileOwner(tile, OWNER_TOWN)) {
 
						SetTownIndex(tile, CalcClosestTownFromTile(tile, (uint)-1)->index);
 
					} else {
 
						SetTownIndex(tile, 0);
 
					}
 
					break;
 

	
 
				default: break;
 
			}
 
		} END_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0);
 
	}
 

	
 
	/* From version 9.0, we update the max passengers of a town (was sometimes negative
 
	 *  before that. */
 
	if (CheckSavegameVersion(9)) {
 
		Town *t;
 
		FOR_ALL_TOWNS(t) UpdateTownMaxPass(t);
 
	}
 

	
 
	/* From version 16.0, we included autorenew on engines, which are now saved, but
 
	 *  of course, we do need to initialize them for older savegames. */
 
	if (CheckSavegameVersion(16)) {
 
		FOR_ALL_PLAYERS(p) {
 
			p->engine_renew_list   = NULL;
 
			p->engine_renew        = false;
 
			p->engine_renew_months = -6;
 
			p->engine_renew_money  = 100000;
 
		}
 

	
 
		/* When loading a game, _local_player is not yet set to the correct value.
 
		 * However, in a dedicated server we are a spectator, so nothing needs to
 
		 * happen. In case we are not a dedicated server, the local player always
 
		 * becomes player 0, unless we are in the scenario editor where all the
 
		 * players are 'invalid'.
 
		 */
 
		if (!_network_dedicated && IsValidPlayer(0)) {
 
			p = GetPlayer(0);
 
			p->engine_renew        = _patches.autorenew;
 
			p->engine_renew_months = _patches.autorenew_months;
 
			p->engine_renew_money  = _patches.autorenew_money;
 
		}
 
	}
 

	
 
	if (CheckSavegameVersion(42)) {
 
		TileIndex map_end = MapSize();
 
		TileIndex tile;
 
		Vehicle* v;
 

	
 
		for (tile = 0; tile != map_end; tile++) {
 
			if (MayHaveBridgeAbove(tile)) ClearBridgeMiddle(tile);
 
			if (IsBridgeTile(tile)) {
 
				if (HASBIT(_m[tile].m5, 6)) { // middle part
 
					Axis axis = (Axis)GB(_m[tile].m5, 0, 1);
 

	
 
					if (HASBIT(_m[tile].m5, 5)) { // transport route under bridge?
 
						if (GB(_m[tile].m5, 3, 2) == TRANSPORT_RAIL) {
 
							MakeRailNormal(
 
								tile,
 
								GetTileOwner(tile),
 
								axis == AXIS_X ? TRACK_BIT_Y : TRACK_BIT_X,
 
								GetRailType(tile)
 
							);
 
						} else {
 
							TownID town = IsTileOwner(tile, OWNER_TOWN) ? ClosestTownFromTile(tile, (uint)-1)->index : 0;
 

	
 
							MakeRoadNormal(
 
								tile,
 
								GetTileOwner(tile),
 
								axis == AXIS_X ? ROAD_Y : ROAD_X,
 
								town
 
							);
 
						}
 
					} else {
 
						if (GB(_m[tile].m5, 3, 2) == 0) {
 
							MakeClear(tile, CLEAR_GRASS, 3);
 
						} else {
 
							MakeCanal(tile, GetTileOwner(tile));
 
						}
 
					}
 
					SetBridgeMiddle(tile, axis);
 
				} else { // ramp
 
					Axis axis = (Axis)GB(_m[tile].m5, 0, 1);
 
					uint north_south = GB(_m[tile].m5, 5, 1);
 
					DiagDirection dir = ReverseDiagDir(XYNSToDiagDir(axis, north_south));
 
					TransportType type = (TransportType)GB(_m[tile].m5, 1, 2);
 

	
 
					_m[tile].m5 = 1 << 7 | type << 2 | dir;
 
				}
 
			}
 
		}
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->type != VEH_Train && v->type != VEH_Road) continue;
 
			if (IsBridgeTile(v->tile)) {
 
				DiagDirection dir = GetBridgeRampDirection(v->tile);
 

	
 
				if (dir != DirToDiagDir(v->direction)) continue;
 
				switch (dir) {
 
					default: NOT_REACHED();
 
					case DIAGDIR_NE: if ((v->x_pos & 0xF) !=  0)            continue; break;
 
					case DIAGDIR_SE: if ((v->y_pos & 0xF) != TILE_SIZE - 1) continue; break;
 
					case DIAGDIR_SW: if ((v->x_pos & 0xF) != TILE_SIZE - 1) continue; break;
 
					case DIAGDIR_NW: if ((v->y_pos & 0xF) !=  0)            continue; break;
 
				}
 
			} else if (v->z_pos > GetSlopeZ(v->x_pos, v->y_pos)) {
 
				v->tile = GetNorthernBridgeEnd(v->tile);
 
			} else {
 
				continue;
 
			}
 
			if (v->type == VEH_Train) {
 
				v->u.rail.track = 0x40;
 
			} else {
 
				v->u.road.state = 0xFF;
 
			}
 
		}
 
	}
 

	
 
	/* Elrails got added in rev 24 */
 
	if (CheckSavegameVersion(24)) {
 
		Vehicle *v;
 
		uint i;
 
		TileIndex t;
 
		RailType min_rail = RAILTYPE_ELECTRIC;
 

	
 
		for (i = 0; i < lengthof(_engines); i++) {
 
			Engine *e = GetEngine(i);
 
			if (e->type == VEH_Train &&
 
					(e->railtype != RAILTYPE_RAIL || RailVehInfo(i)->engclass == 2)) {
 
				e->railtype++;
 
			}
 
		}
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->type == VEH_Train) {
 
				RailType rt = GetEngine(v->engine_type)->railtype;
 

	
 
				v->u.rail.railtype = rt;
 
				if (rt == RAILTYPE_ELECTRIC) min_rail = RAILTYPE_RAIL;
 
			}
 
		}
 

	
 
		/* .. so we convert the entire map from normal to elrail (so maintain "fairness") */
 
		for (t = 0; t < MapSize(); t++) {
 
			switch (GetTileType(t)) {
 
				case MP_RAILWAY:
 
					SetRailType(t, UpdateRailType(GetRailType(t), min_rail));
 
					break;
 

	
 
				case MP_STREET:
 
					if (IsLevelCrossing(t)) {
 
						SetRailTypeCrossing(t, UpdateRailType(GetRailTypeCrossing(t), min_rail));
 
					}
 
					break;
 

	
 
				case MP_STATION:
 
					if (IsRailwayStation(t)) {
 
						SetRailType(t, UpdateRailType(GetRailType(t), min_rail));
 
					}
 
					break;
 

	
 
				case MP_TUNNELBRIDGE:
 
					if (IsTunnel(t)) {
 
						if (GetTunnelTransportType(t) == TRANSPORT_RAIL) {
 
							SetRailType(t, UpdateRailType(GetRailType(t), min_rail));
 
						}
 
					} else {
 
						if (GetBridgeTransportType(t) == TRANSPORT_RAIL) {
 
							SetRailType(t, UpdateRailType(GetRailType(t), min_rail));
 
						}
 
					}
 
					break;
 

	
 
				default:
 
					break;
 
			}
 
		}
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->type == VEH_Train && (IsFrontEngine(v) || IsFreeWagon(v))) TrainConsistChanged(v);
 
		}
 

	
 
	}
 

	
 
	/* In version 16.1 of the savegame a player can decide if trains, which get
 
	 * replaced, shall keep their old length. In all prior versions, just default
 
	 * to false */
 
	if (CheckSavegameVersionOldStyle(16, 1)) {
 
		FOR_ALL_PLAYERS(p) p->renew_keep_length = false;
 
	}
 

	
 
	/* In version 17, ground type is moved from m2 to m4 for depots and
 
	 * waypoints to make way for storing the index in m2. The custom graphics
 
	 * id which was stored in m4 is now saved as a grf/id reference in the
 
	 * waypoint struct. */
 
	if (CheckSavegameVersion(17)) {
 
		Waypoint *wp;
 

	
 
		FOR_ALL_WAYPOINTS(wp) {
 
			if (wp->deleted == 0) {
 
				const StationSpec *statspec = NULL;
 

	
 
				if (HASBIT(_m[wp->xy].m3, 4))
 
					statspec = GetCustomStationSpec(STAT_CLASS_WAYP, _m[wp->xy].m4 + 1);
 

	
 
				if (statspec != NULL) {
 
					wp->stat_id = _m[wp->xy].m4 + 1;
 
					wp->grfid = statspec->grfid;
 
					wp->localidx = statspec->localidx;
 
				} else {
 
					// No custom graphics set, so set to default.
 
					wp->stat_id = 0;
 
					wp->grfid = 0;
 
					wp->localidx = 0;
 
				}
 

	
 
				// Move ground type bits from m2 to m4.
 
				_m[wp->xy].m4 = GB(_m[wp->xy].m2, 0, 4);
 
				// Store waypoint index in the tile.
 
				_m[wp->xy].m2 = wp->index;
 
			}
 
		}
 
	} else {
 
		/* As of version 17, we recalculate the custom graphic ID of waypoints
 
		 * from the GRF ID / station index. */
 
		AfterLoadWaypoints();
 
	}
 

	
 
	/* From version 15, we moved a semaphore bit from bit 2 to bit 3 in m4, making
 
	 *  room for PBS. Now in version 21 move it back :P. */
 
	if (CheckSavegameVersion(21) && !CheckSavegameVersion(15)) {
 
		BEGIN_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0) {
 
			if (IsTileType(tile, MP_RAILWAY)) {
 
				if (HasSignals(tile)) {
 
					// convert PBS signals to combo-signals
 
					if (HASBIT(_m[tile].m4, 2)) SetSignalType(tile, SIGTYPE_COMBO);
 

	
 
					// move the signal variant back
 
					SetSignalVariant(tile, HASBIT(_m[tile].m4, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC);
 
					CLRBIT(_m[tile].m4, 3);
 
				}
 

	
 
				// Clear PBS reservation on track
 
				if (!IsTileDepotType(tile, TRANSPORT_RAIL)) {
 
					SB(_m[tile].m4, 4, 4, 0);
 
				} else {
 
					CLRBIT(_m[tile].m3, 6);
 
				}
 
			}
 

	
 
			// Clear PBS reservation on crossing
 
			if (IsTileType(tile, MP_STREET) && IsLevelCrossing(tile))
 
				CLRBIT(_m[tile].m5, 0);
 

	
 
			// Clear PBS reservation on station
 
			if (IsTileType(tile, MP_STATION))
 
				CLRBIT(_m[tile].m3, 6);
 
		} END_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0);
 
	}
 

	
 
	if (CheckSavegameVersion(22))  UpdatePatches();
 

	
 
	if (CheckSavegameVersion(25)) {
 
		Vehicle *v;
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->type == VEH_Road) {
 
				v->vehstatus &= ~0x40;
 
				v->u.road.slot = NULL;
 
				v->u.road.slot_age = 0;
 
			}
 
		}
 
	}
 

	
 
	if (CheckSavegameVersion(26)) {
 
		Station *st;
 
		FOR_ALL_STATIONS(st) {
 
			st->last_vehicle_type = VEH_Invalid;
 
		}
 
	}
 

	
 
	YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
 

	
 
	if (CheckSavegameVersion(34)) FOR_ALL_PLAYERS(p) ResetPlayerLivery(p);
 

	
 
	FOR_ALL_PLAYERS(p) p->avail_railtypes = GetPlayerRailtypes(p->index);
 

	
 
	if (!CheckSavegameVersion(27)) AfterLoadStations();
 

	
 
	{
 
		/* Set up the engine count for all players */
 
		Player *players[MAX_PLAYERS];
 
		int i;
 
		const Vehicle *v;
 

	
 
		for (i = 0; i < MAX_PLAYERS; i++) players[i] = GetPlayer(i);
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			if (!IsEngineCountable(v)) continue;
 
			players[v->owner]->num_engines[v->engine_type]++;
 
		}
 
	}
 

	
 
	/* Time starts at 0 instead of 1920.
 
	 * Account for this in older games by adding an offset */
 
	if (CheckSavegameVersion(31)) {
 
		Station *st;
 
		Waypoint *wp;
 
		Engine *e;
 
		Player *player;
 
		Industry *i;
 
		Vehicle *v;
 

	
 
		_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
 
		_cur_year += ORIGINAL_BASE_YEAR;
 

	
 
		FOR_ALL_STATIONS(st)    st->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
 
		FOR_ALL_WAYPOINTS(wp)   wp->build_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
 
		FOR_ALL_ENGINES(e)      e->intro_date  += DAYS_TILL_ORIGINAL_BASE_YEAR;
 
		FOR_ALL_PLAYERS(player) player->inaugurated_year += ORIGINAL_BASE_YEAR;
 
		FOR_ALL_INDUSTRIES(i)   i->last_prod_year        += ORIGINAL_BASE_YEAR;
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			v->date_of_last_service += DAYS_TILL_ORIGINAL_BASE_YEAR;
 
			v->build_year += ORIGINAL_BASE_YEAR;
 
		}
 
	}
 

	
 
	/* From 32 on we save the industry who made the farmland.
 
	 *  To give this prettyness to old savegames, we remove all farmfields and
 
	 *  plant new ones. */
 
	if (CheckSavegameVersion(32)) {
 
		Industry *i;
 

	
 
		BEGIN_TILE_LOOP(tile_cur, MapSizeX(), MapSizeY(), 0) {
 
			if (IsTileType(tile_cur, MP_CLEAR) && IsClearGround(tile_cur, CLEAR_FIELDS)) {
 
				MakeClear(tile_cur, CLEAR_GRASS, 3);
 
			}
 
		} END_TILE_LOOP(tile_cur, MapSizeX(), MapSizeY(), 0)
 

	
 
		FOR_ALL_INDUSTRIES(i) {
 
			uint j;
 

	
 
			if (i->type == IT_FARM || i->type == IT_FARM_2) {
 
				for (j = 0; j != 50; j++) PlantRandomFarmField(i);
 
			}
 
		}
 
	}
 

	
 
	/* Setting no refit flags to all orders in savegames from before refit in orders were added */
 
	if (CheckSavegameVersion(36)) {
 
		Order *order;
 
		Vehicle *v;
 

	
 
		FOR_ALL_ORDERS(order) {
 
			order->refit_cargo   = CT_NO_REFIT;
 
			order->refit_subtype = CT_NO_REFIT;
 
		}
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			v->current_order.refit_cargo   = CT_NO_REFIT;
 
			v->current_order.refit_subtype = CT_NO_REFIT;
 
		}
 
	}
 

	
 
	if (CheckSavegameVersion(37)) {
 
		ConvertNameArray();
 
	}
 

	
 
	/* from version 38 we have optional elrails, since we cannot know the
 
	 * preference of a user, let elrails enabled; it can be disabled manually */
 
	if (CheckSavegameVersion(38)) {
 
		_patches.disable_elrails = false; // enable elrails
 
		/* do the same as when elrails were enabled/disabled manually just now */
 
		SettingsDisableElrail(_patches.disable_elrails);
 
	}
 

	
 
	if (CheckSavegameVersion(43)) {
 
		BEGIN_TILE_LOOP(tile_cur, MapSizeX(), MapSizeY(), 0) {
 
			if (IsTileType(tile_cur, MP_INDUSTRY)) {
 
				switch (GetIndustryGfx(tile_cur)) {
 
					case GFX_POWERPLANT_SPARKS:
 
						SetIndustryAnimationState(tile_cur, GB(_m[tile_cur].m1, 2, 5));
 
						break;
 

	
 
					case GFX_OILWELL_ANIMATED_1:
 
					case GFX_OILWELL_ANIMATED_2:
 
					case GFX_OILWELL_ANIMATED_3:
 
						SetIndustryAnimationState(tile_cur, GB(_m[tile_cur].m1, 0, 2));
 
						break;
 

	
 
					case GFX_COAL_MINE_TOWER_ANIMATED:
 
					case GFX_COPPER_MINE_TOWER_ANIMATED:
 
					case GFX_GOLD_MINE_TOWER_ANIMATED:
 
						 SetIndustryAnimationState(tile_cur, _m[tile_cur].m1);
 
						 break;
 

	
 
					default: /* No animation states to change */
 
						break;
 
				}
 
			}
 
		} END_TILE_LOOP(tile_cur, MapSizeX(), MapSizeY(), 0)
 
	}
 

	
 
	return true;
 
}
 

	
 
/** Reload all NewGRF files during a running game. This is a cut-down
 
 * version of AfterLoadGame().
 
 * XXX - We need to reset the vehicle position hash because with a non-empty
 
 * hash AfterLoadVehicles() will loop infinitely. We need AfterLoadVehicles()
 
 * to recalculate vehicle data as some NewGRF vehicle sets could have been
 
 * removed or added and changed statistics */
 
void ReloadNewGRFData(void)
 
{
 
	/* reload grf data */
 
	GfxLoadSprites();
 
	LoadStringWidthTable();
 
	/* reload vehicles */
 
	ResetVehiclePosHash();
 
	AfterLoadVehicles();
 
	/* update station and waypoint graphics */
 
	AfterLoadWaypoints();
 
	AfterLoadStations();
 
	/* redraw the whole screen */
 
	MarkWholeScreenDirty();
 
}
src/order_cmd.c
Show inline comments
 
deleted file
src/order_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "order.h"
 
#include "airport.h"
 
#include "depot.h"
 
#include "functions.h"
 
#include "table/strings.h"
 
#include "vehicle.h"
 
#include "waypoint.h"
 
#include "command.h"
 
#include "station.h"
 
#include "player.h"
 
#include "news.h"
 
#include "saveload.h"
 
#include "vehicle_gui.h"
 

	
 
/**
 
 * Called if a new block is added to the order-pool
 
 */
 
static void OrderPoolNewBlock(uint start_item)
 
{
 
	Order *order;
 

	
 
	/* 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 (order = GetOrder(start_item); order != NULL; order = (order->index + 1U < GetOrderPoolSize()) ? GetOrder(order->index + 1U) : NULL) order->index = start_item++;
 
}
 

	
 
DEFINE_OLD_POOL(Order, Order, OrderPoolNewBlock, NULL)
 

	
 
/**
 
 *
 
 * Unpacks a order from savegames made with TTD(Patch)
 
 *
 
 */
 
Order UnpackOldOrder(uint16 packed)
 
{
 
	Order order;
 
	order.type    = GB(packed, 0, 4);
 
	order.flags   = GB(packed, 4, 4);
 
	order.dest    = GB(packed, 8, 8);
 
	order.next    = NULL;
 

	
 
	order.refit_cargo   = CT_NO_REFIT;
 
	order.refit_subtype = 0;
 
	order.index = 0; // avoid compiler warning
 

	
 
	// Sanity check
 
	// TTD stores invalid orders as OT_NOTHING with non-zero flags/station
 
	if (order.type == OT_NOTHING && (order.flags != 0 || order.dest != 0)) {
 
		order.type = OT_DUMMY;
 
		order.flags = 0;
 
	}
 

	
 
	return order;
 
}
 

	
 
/**
 
 *
 
 * Unpacks a order from savegames with version 4 and lower
 
 *
 
 */
 
static Order UnpackVersion4Order(uint16 packed)
 
{
 
	Order order;
 
	order.type  = GB(packed, 0, 4);
 
	order.flags = GB(packed, 4, 4);
 
	order.dest  = GB(packed, 8, 8);
 
	order.next  = NULL;
 
	order.index = 0; // avoid compiler warning
 
	order.refit_cargo   = CT_NO_REFIT;
 
	order.refit_subtype = 0;
 
	return order;
 
}
 

	
 
/**
 
 *
 
 * Updates the widgets of a vehicle which contains the order-data
 
 *
 
 */
 
void InvalidateVehicleOrder(const Vehicle *v)
 
{
 
	InvalidateWindow(WC_VEHICLE_VIEW,   v->index);
 
	InvalidateWindow(WC_VEHICLE_ORDERS, v->index);
 
}
 

	
 
/**
 
 *
 
 * Swap two orders
 
 *
 
 */
 
static void SwapOrders(Order *order1, Order *order2)
 
{
 
	Order temp_order;
 

	
 
	temp_order = *order1;
 
	AssignOrder(order1, *order2);
 
	order1->next = order2->next;
 
	AssignOrder(order2, temp_order);
 
	order2->next = temp_order.next;
 
}
 

	
 
/**
 
 *
 
 * Allocate a new order
 
 *
 
 * @return Order* if a free space is found, else NULL.
 
 *
 
 */
 
static Order *AllocateOrder(void)
 
{
 
	Order *order;
 

	
 
	/* 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 (order = GetOrder(0); order != NULL; order = (order->index + 1U < GetOrderPoolSize()) ? GetOrder(order->index + 1U) : NULL) {
 
		if (!IsValidOrder(order)) {
 
			OrderID index = order->index;
 

	
 
			memset(order, 0, sizeof(*order));
 
			order->index = index;
 
			order->next = NULL;
 
			order->refit_cargo   = CT_NO_REFIT;
 
			order->refit_subtype = 0;
 

	
 
			return order;
 
		}
 
	}
 

	
 
	/* Check if we can add a block to the pool */
 
	if (AddBlockToPool(&_Order_pool)) return AllocateOrder();
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 *
 
 * Assign data to an order (from an other order)
 
 *   This function makes sure that the index is maintained correctly
 
 *
 
 */
 
void AssignOrder(Order *order, Order data)
 
{
 
	order->type  = data.type;
 
	order->flags = data.flags;
 
	order->dest  = data.dest;
 

	
 
	order->refit_cargo   = data.refit_cargo;
 
	order->refit_subtype = data.refit_subtype;
 
}
 

	
 

	
 
/**
 
 * Delete all news items regarding defective orders about a vehicle
 
 * This could kill still valid warnings (for example about void order when just
 
 * another order gets added), but assume the player will notice the problems,
 
 * when (s)he's changing the orders.
 
 */
 
static void DeleteOrderWarnings(const Vehicle* v)
 
{
 
	DeleteVehicleNews(v->index, STR_TRAIN_HAS_TOO_FEW_ORDERS  + (v->type - VEH_Train) * 4);
 
	DeleteVehicleNews(v->index, STR_TRAIN_HAS_VOID_ORDER      + (v->type - VEH_Train) * 4);
 
	DeleteVehicleNews(v->index, STR_TRAIN_HAS_DUPLICATE_ENTRY + (v->type - VEH_Train) * 4);
 
	DeleteVehicleNews(v->index, STR_TRAIN_HAS_INVALID_ENTRY   + (v->type - VEH_Train) * 4);
 
}
 

	
 

	
 
/** Add an order to the orderlist of a vehicle.
 
 * @param tile unused
 
 * @param p1 various bitstuffed elements
 
 * - p1 = (bit  0 - 15) - ID of the vehicle
 
 * - p1 = (bit 16 - 31) - the selected order (if any). If the last order is given,
 
 *                        the order will be inserted before that one
 
 *                        only the first 8 bits used currently (bit 16 - 23) (max 255)
 
 * @param p2 packed order to insert
 
 */
 
int32 CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	VehicleID veh   = GB(p1,  0, 16);
 
	VehicleOrderID sel_ord = GB(p1, 16, 16);
 
	Order new_order = UnpackOrder(p2);
 

	
 
	if (!IsValidVehicleID(veh)) return CMD_ERROR;
 

	
 
	v = GetVehicle(veh);
 

	
 
	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	/* Check if the inserted order is to the correct destination (owner, type),
 
	 * and has the correct flags if any */
 
	switch (new_order.type) {
 
		case OT_GOTO_STATION: {
 
			const Station *st;
 

	
 
			if (!IsValidStationID(new_order.dest)) return CMD_ERROR;
 
			st = GetStation(new_order.dest);
 

	
 
			if (st->airport_type != AT_OILRIG && !IsBuoy(st) && !CheckOwnership(st->owner)) {
 
				return CMD_ERROR;
 
			}
 

	
 
			switch (v->type) {
 
				case VEH_Train:
 
					if (!(st->facilities & FACIL_TRAIN)) return CMD_ERROR;
 
					break;
 

	
 
				case VEH_Road:
 
					if (v->cargo_type == CT_PASSENGERS) {
 
						if (!(st->facilities & FACIL_BUS_STOP)) return CMD_ERROR;
 
					} else {
 
						if (!(st->facilities & FACIL_TRUCK_STOP)) return CMD_ERROR;
 
					}
 
					break;
 

	
 
				case VEH_Ship:
 
					if (!(st->facilities & FACIL_DOCK)) return CMD_ERROR;
 
					break;
 

	
 
				case VEH_Aircraft:
 
					if (!(st->facilities & FACIL_AIRPORT)) return CMD_ERROR;
 
					break;
 

	
 
				default: return CMD_ERROR;
 
			}
 

	
 
			/* Order flags can be any of the following for stations:
 
			 * [full-load | unload] [+ transfer] [+ non-stop]
 
			 * non-stop orders (if any) are only valid for trains */
 
			switch (new_order.flags) {
 
				case 0:
 
				case OF_FULL_LOAD:
 
				case OF_FULL_LOAD | OF_TRANSFER:
 
				case OF_UNLOAD:
 
				case OF_UNLOAD | OF_TRANSFER:
 
				case OF_TRANSFER:
 
					break;
 

	
 
				case OF_NON_STOP:
 
				case OF_NON_STOP | OF_FULL_LOAD:
 
				case OF_NON_STOP | OF_FULL_LOAD | OF_TRANSFER:
 
				case OF_NON_STOP | OF_UNLOAD:
 
				case OF_NON_STOP | OF_UNLOAD | OF_TRANSFER:
 
				case OF_NON_STOP | OF_TRANSFER:
 
					if (v->type != VEH_Train) return CMD_ERROR;
 
					break;
 

	
 
				default: return CMD_ERROR;
 
			}
 
			break;
 
		}
 

	
 
		case OT_GOTO_DEPOT: {
 
			if (v->type == VEH_Aircraft) {
 
				const Station* st;
 

	
 
				if (!IsValidStationID(new_order.dest)) return CMD_ERROR;
 
				st = GetStation(new_order.dest);
 

	
 
				if ((st->airport_type != AT_OILRIG && !CheckOwnership(st->owner)) ||
 
						!(st->facilities & FACIL_AIRPORT) ||
 
						GetAirport(st->airport_type)->nof_depots == 0) {
 
					return CMD_ERROR;
 
				}
 
			} else {
 
				const Depot* dp;
 

	
 
				if (!IsValidDepotID(new_order.dest)) return CMD_ERROR;
 
				dp = GetDepot(new_order.dest);
 

	
 
				if (!CheckOwnership(GetTileOwner(dp->xy))) return CMD_ERROR;
 

	
 
				switch (v->type) {
 
					case VEH_Train:
 
						if (!IsTileDepotType(dp->xy, TRANSPORT_RAIL)) return CMD_ERROR;
 
						break;
 

	
 
					case VEH_Road:
 
						if (!IsTileDepotType(dp->xy, TRANSPORT_ROAD)) return CMD_ERROR;
 
						break;
 

	
 
					case VEH_Ship:
 
						if (!IsTileDepotType(dp->xy, TRANSPORT_WATER)) return CMD_ERROR;
 
						break;
 

	
 
					default: return CMD_ERROR;
 
				}
 
			}
 

	
 
			/* Order flags can be any of the following for depots:
 
			 * order [+ halt] [+ non-stop]
 
			 * non-stop orders (if any) are only valid for trains */
 
			switch (new_order.flags) {
 
				case OF_PART_OF_ORDERS:
 
				case OF_PART_OF_ORDERS | OF_HALT_IN_DEPOT:
 
					break;
 

	
 
				case OF_NON_STOP | OF_PART_OF_ORDERS:
 
				case OF_NON_STOP | OF_PART_OF_ORDERS | OF_HALT_IN_DEPOT:
 
					if (v->type != VEH_Train) return CMD_ERROR;
 
					break;
 

	
 
				default: return CMD_ERROR;
 
			}
 
			break;
 
		}
 

	
 
		case OT_GOTO_WAYPOINT: {
 
			const Waypoint* wp;
 

	
 
			if (v->type != VEH_Train) return CMD_ERROR;
 

	
 
			if (!IsValidWaypointID(new_order.dest)) return CMD_ERROR;
 
			wp = GetWaypoint(new_order.dest);
 

	
 
			if (!CheckOwnership(GetTileOwner(wp->xy))) return CMD_ERROR;
 

	
 
			/* Order flags can be any of the following for waypoints:
 
			 * [non-stop]
 
			 * non-stop orders (if any) are only valid for trains */
 
			switch (new_order.flags) {
 
				case 0: break;
 

	
 
				case OF_NON_STOP:
 
					if (v->type != VEH_Train) return CMD_ERROR;
 
					break;
 

	
 
				default: return CMD_ERROR;
 
			}
 
			break;
 
		}
 

	
 
		default: return CMD_ERROR;
 
	}
 

	
 
	if (sel_ord > v->num_orders) return CMD_ERROR;
 

	
 
	if (IsOrderPoolFull()) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS);
 

	
 
	/* XXX - This limit is only here because the backuppedorders can't
 
	 * handle any more then this.. */
 
	if (v->num_orders >= MAX_BACKUP_ORDER_COUNT) return_cmd_error(STR_8832_TOO_MANY_ORDERS);
 

	
 
	/* For ships, make sure that the station is not too far away from the
 
	 * previous destination, for human players with new pathfinding disabled */
 
	if (v->type == VEH_Ship && IsHumanPlayer(v->owner) &&
 
		sel_ord != 0 && GetVehicleOrder(v, sel_ord - 1)->type == OT_GOTO_STATION
 
		&& !_patches.new_pathfinding_all) {
 

	
 
		int dist = DistanceManhattan(
 
			GetStation(GetVehicleOrder(v, sel_ord - 1)->dest)->xy,
 
			GetStation(new_order.dest)->xy // XXX type != OT_GOTO_STATION?
 
		);
 
		if (dist >= 130)
 
			return_cmd_error(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		Vehicle *u;
 
		Order *new = AllocateOrder();
 
		AssignOrder(new, new_order);
 

	
 
		/* Create new order and link in list */
 
		if (v->orders == NULL) {
 
			v->orders = new;
 
		} else {
 
			/* Try to get the previous item (we are inserting above the
 
			    selected) */
 
			Order *order = GetVehicleOrder(v, sel_ord - 1);
 

	
 
			if (order == NULL && GetVehicleOrder(v, sel_ord) != NULL) {
 
				/* There is no previous item, so we are altering v->orders itself
 
				    But because the orders can be shared, we copy the info over
 
				    the v->orders, so we don't have to change the pointers of
 
				    all vehicles */
 
				SwapOrders(v->orders, new);
 
				/* Now update the next pointers */
 
				v->orders->next = new;
 
			} else if (order == NULL) {
 
				/* 'sel' is a non-existing order, add him to the end */
 
				order = GetLastVehicleOrder(v);
 
				order->next = new;
 
			} else {
 
				/* Put the new order in between */
 
				new->next = order->next;
 
				order->next = new;
 
			}
 
		}
 

	
 
		u = GetFirstVehicleFromSharedList(v);
 
		DeleteOrderWarnings(u);
 
		for (; u != NULL; u = u->next_shared) {
 
			/* Increase amount of orders */
 
			u->num_orders++;
 

	
 
			/* If the orderlist was empty, assign it */
 
			if (u->orders == NULL) u->orders = v->orders;
 

	
 
			assert(v->orders == u->orders);
 

	
 
			/* If there is added an order before the current one, we need
 
			to update the selected order */
 
			if (sel_ord <= u->cur_order_index) {
 
				uint cur = u->cur_order_index + 1;
 
				/* Check if we don't go out of bound */
 
				if (cur < u->num_orders)
 
					u->cur_order_index = cur;
 
			}
 
			/* Update any possible open window of the vehicle */
 
			InvalidateVehicleOrder(u);
 
		}
 

	
 
		/* Make sure to rebuild the whole list */
 
		RebuildVehicleLists();
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Declone an order-list
 
 * @param *dst delete the orders of this vehicle
 
 * @param flags execution flags
 
 */
 
static int32 DecloneOrder(Vehicle *dst, uint32 flags)
 
{
 
	if (flags & DC_EXEC) {
 
		DeleteVehicleOrders(dst);
 
		InvalidateVehicleOrder(dst);
 
		RebuildVehicleLists();
 
	}
 
	return 0;
 
}
 

	
 
/** Delete an order from the orderlist of a vehicle.
 
 * @param tile unused
 
 * @param p1 the ID of the vehicle
 
 * @param p2 the order to delete (max 255)
 
 */
 
int32 CmdDeleteOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v, *u;
 
	VehicleID veh_id = p1;
 
	VehicleOrderID sel_ord = p2;
 
	Order *order;
 

	
 
	if (!IsValidVehicleID(veh_id)) return CMD_ERROR;
 

	
 
	v = GetVehicle(veh_id);
 

	
 
	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	/* If we did not select an order, we maybe want to de-clone the orders */
 
	if (sel_ord >= v->num_orders)
 
		return DecloneOrder(v, flags);
 

	
 
	order = GetVehicleOrder(v, sel_ord);
 
	if (order == NULL) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		if (GetVehicleOrder(v, sel_ord - 1) == NULL) {
 
			if (GetVehicleOrder(v, sel_ord + 1) != NULL) {
 
				/* First item, but not the last, so we need to alter v->orders
 
				    Because we can have shared order, we copy the data
 
				    from the next item over the deleted */
 
				order = GetVehicleOrder(v, sel_ord + 1);
 
				SwapOrders(v->orders, order);
 
			} else {
 
				/* Last item, so clean the list */
 
				v->orders = NULL;
 
			}
 
		} else {
 
			GetVehicleOrder(v, sel_ord - 1)->next = order->next;
 
		}
 

	
 
		/* Give the item free */
 
		order->type = OT_NOTHING;
 
		order->next = NULL;
 

	
 
		u = GetFirstVehicleFromSharedList(v);
 
		DeleteOrderWarnings(u);
 
		for (; u != NULL; u = u->next_shared) {
 
			u->num_orders--;
 

	
 
			if (sel_ord < u->cur_order_index)
 
				u->cur_order_index--;
 

	
 
			/* If we removed the last order, make sure the shared vehicles
 
			 * also set their orders to NULL */
 
			if (v->orders == NULL) u->orders = NULL;
 

	
 
			assert(v->orders == u->orders);
 

	
 
			/* NON-stop flag is misused to see if a train is in a station that is
 
			 * on his order list or not */
 
			if (sel_ord == u->cur_order_index && u->current_order.type == OT_LOADING &&
 
					HASBIT(u->current_order.flags, OFB_NON_STOP)) {
 
				u->current_order.flags = 0;
 
			}
 

	
 
			/* Update any possible open window of the vehicle */
 
			InvalidateVehicleOrder(u);
 
		}
 

	
 
		RebuildVehicleLists();
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Goto next order of order-list.
 
 * @param tile unused
 
 * @param p1 The ID of the vehicle which order is skipped
 
 * @param p2 unused
 
 */
 
int32 CmdSkipOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	VehicleID veh_id = p1;
 

	
 
	if (!IsValidVehicleID(veh_id)) return CMD_ERROR;
 

	
 
	v = GetVehicle(veh_id);
 

	
 
	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		/* Goto next order */
 
		VehicleOrderID b = v->cur_order_index + 1;
 
		if (b >= v->num_orders) b = 0;
 

	
 
		v->cur_order_index = b;
 

	
 
		if (v->type == VEH_Train) v->u.rail.days_since_order_progr = 0;
 

	
 
		if (v->type == VEH_Road) ClearSlot(v);
 

	
 
		/* NON-stop flag is misused to see if a train is in a station that is
 
		 * on his order list or not */
 
		if (v->current_order.type == OT_LOADING && HASBIT(v->current_order.flags, OFB_NON_STOP))
 
			v->current_order.flags = 0;
 

	
 
		InvalidateVehicleOrder(v);
 
	}
 

	
 
	/* We have an aircraft/ship, they have a mini-schedule, so update them all */
 
	if (v->type == VEH_Aircraft) InvalidateWindowClasses(WC_AIRCRAFT_LIST);
 
	if (v->type == VEH_Ship) InvalidateWindowClasses(WC_SHIPS_LIST);
 

	
 
	return 0;
 
}
 

	
 

	
 
/** Modify an order in the orderlist of a vehicle.
 
 * @param tile unused
 
 * @param p1 various bitstuffed elements
 
 * - p1 = (bit  0 - 15) - ID of the vehicle
 
 * - p1 = (bit 16 - 31) - the selected order (if any). If the last order is given,
 
 *                        the order will be inserted before that one
 
 *                        only the first 8 bits used currently (bit 16 - 23) (max 255)
 
 * @param p2 mode to change the order to (always set)
 
 */
 
int32 CmdModifyOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	Order *order;
 
	VehicleOrderID sel_ord = GB(p1, 16, 16); // XXX - automatically truncated to 8 bits.
 
	VehicleID veh   = GB(p1,  0, 16);
 

	
 
	if (!IsValidVehicleID(veh)) return CMD_ERROR;
 
	if (p2 != OFB_FULL_LOAD && p2 != OFB_UNLOAD && p2 != OFB_NON_STOP && p2 != OFB_TRANSFER) return CMD_ERROR;
 

	
 
	v = GetVehicle(veh);
 

	
 
	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	/* Is it a valid order? */
 
	if (sel_ord >= v->num_orders) return CMD_ERROR;
 

	
 
	order = GetVehicleOrder(v, sel_ord);
 
	if (order->type != OT_GOTO_STATION &&
 
			(order->type != OT_GOTO_DEPOT    || p2 == OFB_UNLOAD) &&
 
			(order->type != OT_GOTO_WAYPOINT || p2 != OFB_NON_STOP)) {
 
		return CMD_ERROR;
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		switch (p2) {
 
		case OFB_FULL_LOAD:
 
			TOGGLEBIT(order->flags, OFB_FULL_LOAD);
 
			if (order->type != OT_GOTO_DEPOT) CLRBIT(order->flags, OFB_UNLOAD);
 
			break;
 
		case OFB_UNLOAD:
 
			TOGGLEBIT(order->flags, OFB_UNLOAD);
 
			CLRBIT(order->flags, OFB_FULL_LOAD);
 
			break;
 
		case OFB_NON_STOP:
 
			TOGGLEBIT(order->flags, OFB_NON_STOP);
 
			break;
 
		case OFB_TRANSFER:
 
			TOGGLEBIT(order->flags, OFB_TRANSFER);
 
			break;
 
		default: NOT_REACHED();
 
		}
 

	
 
		/* Update the windows and full load flags, also for vehicles that share the same order list */
 
		{
 
			Vehicle* u;
 

	
 
			u = GetFirstVehicleFromSharedList(v);
 
			DeleteOrderWarnings(u);
 
			for (; u != NULL; u = u->next_shared) {
 
				/* Toggle u->current_order "Full load" flag if it changed.
 
				 * However, as the same flag is used for depot orders, check
 
				 * whether we are not going to a depot as there are three
 
				 * cases where the full load flag can be active and only
 
				 * one case where the flag is used for depot orders. In the
 
				 * other cases for the OrderType the flags are not used,
 
				 * so do not care and those orders should not be active
 
				 * when this function is called.
 
				 */
 
				if (sel_ord == u->cur_order_index &&
 
						u->current_order.type != OT_GOTO_DEPOT &&
 
						HASBIT(u->current_order.flags, OFB_FULL_LOAD) != HASBIT(order->flags, OFB_FULL_LOAD)) {
 
					TOGGLEBIT(u->current_order.flags, OFB_FULL_LOAD);
 
				}
 
				InvalidateVehicleOrder(u);
 
			}
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Clone/share/copy an order-list of an other vehicle.
 
 * @param p1 various bitstuffed elements
 
 * - p1 = (bit  0-15) - destination vehicle to clone orders to (p1 & 0xFFFF)
 
 * - p1 = (bit 16-31) - source vehicle to clone orders from, if any (none for CO_UNSHARE)
 
 * @param p2 mode of cloning: CO_SHARE, CO_COPY, or CO_UNSHARE
 
 */
 
int32 CmdCloneOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *dst;
 
	VehicleID veh_src = GB(p1, 16, 16);
 
	VehicleID veh_dst = GB(p1,  0, 16);
 

	
 
	if (!IsValidVehicleID(veh_dst)) return CMD_ERROR;
 

	
 
	dst = GetVehicle(veh_dst);
 

	
 
	if (!CheckOwnership(dst->owner)) return CMD_ERROR;
 

	
 
	switch (p2) {
 
		case CO_SHARE: {
 
			Vehicle *src;
 

	
 
			if (!IsValidVehicleID(veh_src)) return CMD_ERROR;
 

	
 
			src = GetVehicle(veh_src);
 

	
 
			/* Sanity checks */
 
			if (!CheckOwnership(src->owner) || dst->type != src->type || dst == src)
 
				return CMD_ERROR;
 

	
 
			/* Trucks can't share orders with busses (and visa versa) */
 
			if (src->type == VEH_Road) {
 
				if (src->cargo_type != dst->cargo_type && (src->cargo_type == CT_PASSENGERS || dst->cargo_type == CT_PASSENGERS))
 
					return CMD_ERROR;
 
			}
 

	
 
			/* Is the vehicle already in the shared list? */
 
			{
 
				const Vehicle* u;
 

	
 
				for (u = GetFirstVehicleFromSharedList(src); u != NULL; u = u->next_shared) {
 
					if (u == dst) return CMD_ERROR;
 
				}
 
			}
 

	
 
			if (flags & DC_EXEC) {
 
				/* If the destination vehicle had a OrderList, destroy it */
 
				DeleteVehicleOrders(dst);
 

	
 
				dst->orders = src->orders;
 
				dst->num_orders = src->num_orders;
 

	
 
				/* Link this vehicle in the shared-list */
 
				dst->next_shared = src->next_shared;
 
				dst->prev_shared = src;
 
				if (src->next_shared != NULL) src->next_shared->prev_shared = dst;
 
				src->next_shared = dst;
 

	
 
				InvalidateVehicleOrder(dst);
 
				InvalidateVehicleOrder(src);
 

	
 
				RebuildVehicleLists();
 
			}
 
		} break;
 

	
 
		case CO_COPY: {
 
			Vehicle *src;
 
			int delta;
 

	
 
			if (!IsValidVehicleID(veh_src)) return CMD_ERROR;
 

	
 
			src = GetVehicle(veh_src);
 

	
 
			/* Sanity checks */
 
			if (!CheckOwnership(src->owner) || dst->type != src->type || dst == src)
 
				return CMD_ERROR;
 

	
 
			/* Trucks can't copy all the orders from busses (and visa versa) */
 
			if (src->type == VEH_Road) {
 
				const Order *order;
 
				TileIndex required_dst = INVALID_TILE;
 

	
 
				FOR_VEHICLE_ORDERS(src, order) {
 
					if (order->type == OT_GOTO_STATION) {
 
						const Station *st = GetStation(order->dest);
 
						if (dst->cargo_type == CT_PASSENGERS) {
 
							if (st->bus_stops != NULL) required_dst = st->bus_stops->xy;
 
						} else {
 
							if (st->truck_stops != NULL) required_dst = st->truck_stops->xy;
 
						}
 
						/* This station has not the correct road-bay, so we can't copy! */
 
						if (required_dst == INVALID_TILE)
 
							return CMD_ERROR;
 
					}
 
				}
 
			}
 

	
 
			/* make sure there are orders available */
 
			delta = IsOrderListShared(dst) ? src->num_orders + 1 : src->num_orders - dst->num_orders;
 
			if (!HasOrderPoolFree(delta))
 
				return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS);
 

	
 
			if (flags & DC_EXEC) {
 
				const Order *order;
 
				Order **order_dst;
 

	
 
				/* If the destination vehicle had a OrderList, destroy it */
 
				DeleteVehicleOrders(dst);
 

	
 
				order_dst = &dst->orders;
 
				FOR_VEHICLE_ORDERS(src, order) {
 
					*order_dst = AllocateOrder();
 
					AssignOrder(*order_dst, *order);
 
					order_dst = &(*order_dst)->next;
 
				}
 

	
 
				dst->num_orders = src->num_orders;
 

	
 
				InvalidateVehicleOrder(dst);
 

	
 
				RebuildVehicleLists();
 
			}
 
		} break;
 

	
 
		case CO_UNSHARE: return DecloneOrder(dst, flags);
 
		default: return CMD_ERROR;
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Add/remove refit orders from an order
 
 * @param tile Not used
 
 * @param p1 VehicleIndex of the vehicle having the order
 
 * @param p2 bitmask
 
 *   - bit 0-7 CargoID
 
 *   - bit 8-15 Cargo subtype
 
 *   - bit 16-23 number of order to modify
 
 */
 
int32 CmdOrderRefit(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	const Vehicle *v;
 
	Order *order;
 
	VehicleID veh = GB(p1, 0, 16);
 
	VehicleOrderID order_number  = GB(p2, 16, 8);
 
	CargoID cargo = GB(p2, 0, 8);
 
	byte subtype  = GB(p2, 8, 8);
 

	
 
	if (!IsValidVehicleID(veh)) return CMD_ERROR;
 

	
 
	v = GetVehicle(veh);
 

	
 
	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	order = GetVehicleOrder(v, order_number);
 
	if (order == NULL) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		Vehicle *u;
 

	
 
		order->refit_cargo = cargo;
 
		order->refit_subtype = subtype;
 

	
 
		u = GetFirstVehicleFromSharedList(v);
 
		for (; u != NULL; u = u->next_shared) {
 
			/* Update any possible open window of the vehicle */
 
			InvalidateVehicleOrder(u);
 

	
 
			/* If the vehicle already got the current depot set as current order, then update current order as well */
 
			if (u->cur_order_index == order_number && HASBIT(u->current_order.flags, OFB_PART_OF_ORDERS)) {
 
				u->current_order.refit_cargo = cargo;
 
				u->current_order.refit_subtype = subtype;
 
			}
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
/**
 
 *
 
 * Backup a vehicle order-list, so you can replace a vehicle
 
 *  without loosing the order-list
 
 *
 
 */
 
void BackupVehicleOrders(const Vehicle *v, BackuppedOrders *bak)
 
{
 
	/* Save general info */
 
	bak->orderindex       = v->cur_order_index;
 
	bak->service_interval = v->service_interval;
 

	
 
	/* Safe custom string, if any */
 
	if (!IsCustomName(v->string_id)) {
 
		bak->name[0] = '\0';
 
	} else {
 
		GetName(bak->name, v->string_id & 0x7FF, lastof(bak->name));
 
	}
 

	
 
	/* If we have shared orders, store it on a special way */
 
	if (IsOrderListShared(v)) {
 
		const Vehicle *u = (v->next_shared) ? v->next_shared : v->prev_shared;
 

	
 
		bak->clone = u->index;
 
	} else {
 
		/* Else copy the orders */
 
		Order *order, *dest;
 

	
 
		dest = bak->order;
 

	
 
		/* We do not have shared orders */
 
		bak->clone = INVALID_VEHICLE;
 

	
 
		/* Copy the orders */
 
		FOR_VEHICLE_ORDERS(v, order) {
 
			*dest = *order;
 
			dest++;
 
		}
 
		/* End the list with an OT_NOTHING */
 
		dest->type = OT_NOTHING;
 
		dest->next = NULL;
 
	}
 
}
 

	
 
/**
 
 *
 
 * Restore vehicle orders that are backupped via BackupVehicleOrders
 
 *
 
 */
 
void RestoreVehicleOrders(const Vehicle* v, const BackuppedOrders* bak)
 
{
 
	uint i;
 

	
 
	/* If we have a custom name, process that */
 
	if (bak->name[0] != 0) {
 
		_cmd_text = bak->name;
 
		DoCommandP(0, v->index, 0, NULL, CMD_NAME_VEHICLE);
 
	}
 

	
 
	/* If we had shared orders, recover that */
 
	if (bak->clone != INVALID_VEHICLE) {
 
		DoCommandP(0, v->index | (bak->clone << 16), 0, NULL, CMD_CLONE_ORDER);
 
		return;
 
	}
 

	
 
	/* CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the
 
	 *  order number is one more than the current amount of orders, and because
 
	 *  in network the commands are queued before send, the second insert always
 
	 *  fails in test mode. By bypassing the test-mode, that no longer is a problem. */
 
	for (i = 0; bak->order[i].type != OT_NOTHING; i++) {
 
		if (!DoCommandP(0, v->index + (i << 16), PackOrder(&bak->order[i]), NULL, CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK))
 
			break;
 
	}
 

	
 
	/* Restore vehicle order-index and service interval */
 
	DoCommandP(0, v->index, bak->orderindex | (bak->service_interval << 16) , NULL, CMD_RESTORE_ORDER_INDEX);
 
}
 

	
 
/** Restore the current order-index of a vehicle and sets service-interval.
 
 * @param tile unused
 
 * @param p1 the ID of the vehicle
 
 * @param p2 various bistuffed elements
 
 * - p2 = (bit  0-15) - current order-index (p2 & 0xFFFF)
 
 * - p2 = (bit 16-31) - service interval (p2 >> 16)
 
 * @todo Unfortunately you cannot safely restore the unitnumber or the old vehicle
 
 * as far as I can see. We can store it in BackuppedOrders, and restore it, but
 
 * but we have no way of seeing it has been tampered with or not, as we have no
 
 * legit way of knowing what that ID was.@n
 
 * If we do want to backup/restore it, just add UnitID uid to BackuppedOrders, and
 
 * restore it as parameter 'y' (ugly hack I know) for example. "v->unitnumber = y;"
 
 */
 
int32 CmdRestoreOrderIndex(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	VehicleOrderID cur_ord = GB(p2,  0, 16);
 
	uint16 serv_int = GB(p2, 16, 16);
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	/* Check the vehicle type and ownership, and if the service interval and order are in range */
 
	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 
	if (serv_int != GetServiceIntervalClamped(serv_int) || cur_ord >= v->num_orders) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		v->cur_order_index = cur_ord;
 
		v->service_interval = serv_int;
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
static TileIndex GetStationTileForVehicle(const Vehicle* v, const Station* st)
 
{
 
	switch (v->type) {
 
		default: NOT_REACHED();
 
		case VEH_Train:     return st->train_tile;
 
		case VEH_Aircraft:  return st->airport_tile;
 
		case VEH_Ship:      return st->dock_tile;
 
		case VEH_Road:
 
			if (v->cargo_type == CT_PASSENGERS) {
 
				return (st->bus_stops != NULL) ? st->bus_stops->xy : 0;
 
			} else {
 
				return (st->truck_stops != NULL) ? st->truck_stops->xy : 0;
 
			}
 
	}
 
}
 

	
 

	
 
/**
 
 *
 
 * Check the orders of a vehicle, to see if there are invalid orders and stuff
 
 *
 
 */
 
void CheckOrders(const Vehicle* v)
 
{
 
	/* Does the user wants us to check things? */
 
	if (_patches.order_review_system == 0) return;
 

	
 
	/* Do nothing for crashed vehicles */
 
	if (v->vehstatus & VS_CRASHED) return;
 

	
 
	/* Do nothing for stopped vehicles if setting is '1' */
 
	if (_patches.order_review_system == 1 && v->vehstatus & VS_STOPPED)
 
		return;
 

	
 
	/* do nothing we we're not the first vehicle in a share-chain */
 
	if (v->next_shared != NULL) return;
 

	
 
	/* Only check every 20 days, so that we don't flood the message log */
 
	if (v->owner == _local_player && v->day_counter % 20 == 0) {
 
		int n_st, problem_type = -1;
 
		const Order *order;
 
		int message = 0;
 

	
 
		/* Check the order list */
 
		n_st = 0;
 

	
 
		FOR_VEHICLE_ORDERS(v, order) {
 
			/* Dummy order? */
 
			if (order->type == OT_DUMMY) {
 
				problem_type = 1;
 
				break;
 
			}
 
			/* Does station have a load-bay for this vehicle? */
 
			if (order->type == OT_GOTO_STATION) {
 
				const Station* st = GetStation(order->dest);
 
				TileIndex required_tile = GetStationTileForVehicle(v, st);
 

	
 
				n_st++;
 
				if (required_tile == 0) problem_type = 3;
 
			}
 
		}
 

	
 
		/* Check if the last and the first order are the same */
 
		if (v->num_orders > 1) {
 
			const Order* last = GetLastVehicleOrder(v);
 

	
 
			if (v->orders->type  == last->type &&
 
					v->orders->flags == last->flags &&
 
					v->orders->dest  == last->dest) {
 
				problem_type = 2;
 
			}
 
		}
 

	
 
		/* Do we only have 1 station in our order list? */
 
		if (n_st < 2 && problem_type == -1) problem_type = 0;
 

	
 
		/* We don't have a problem */
 
		if (problem_type < 0) return;
 

	
 
		message = STR_TRAIN_HAS_TOO_FEW_ORDERS + ((v->type - VEH_Train) << 2) + problem_type;
 
		//DEBUG(misc, 3, "Triggered News Item for vehicle %d", v->index);
 

	
 
		SetDParam(0, v->unitnumber);
 
		AddNewsItem(
 
			message,
 
			NEWS_FLAGS(NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, 0),
 
			v->index,
 
			0
 
		);
 
	}
 
}
 

	
 
/**
 
 * Removes an order from all vehicles. Triggers when, say, a station is removed.
 
 * @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]).
 
 * @param destination The destination. Can be a StationID, DepotID or WaypointID.
 
 */
 
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination)
 
{
 
	Vehicle *v;
 

	
 
	/* Aircraft have StationIDs for depot orders and never use DepotIDs
 
	 * This fact is handled specially below
 
	 */
 

	
 
	/* Go through all vehicles */
 
	FOR_ALL_VEHICLES(v) {
 
		Order *order;
 
		bool invalidate;
 

	
 
		/* Forget about this station if this station is removed */
 
		if (v->last_station_visited == destination && type == OT_GOTO_STATION) {
 
			v->last_station_visited = INVALID_STATION;
 
		}
 

	
 
		order = &v->current_order;
 
		if ((v->type == VEH_Aircraft && order->type == OT_GOTO_DEPOT ? OT_GOTO_STATION : order->type) == type &&
 
				v->current_order.dest == destination) {
 
			order->type = OT_DUMMY;
 
			order->flags = 0;
 
			InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		}
 

	
 
		/* Clear the order from the order-list */
 
		invalidate = false;
 
		FOR_VEHICLE_ORDERS(v, order) {
 
			if ((v->type == VEH_Aircraft && order->type == OT_GOTO_DEPOT ? OT_GOTO_STATION : order->type) == type &&
 
					order->dest == destination) {
 
				order->type = OT_DUMMY;
 
				order->flags = 0;
 
				invalidate = true;
 
			}
 
		}
 

	
 
		/* Only invalidate once, and if needed */
 
		if (invalidate) InvalidateWindow(WC_VEHICLE_ORDERS, v->index);
 
	}
 
}
 

	
 
/**
 
 *
 
 * Checks if a vehicle has a GOTO_DEPOT in his order list
 
 *
 
 * @return True if this is true (lol ;))
 
 *
 
 */
 
bool VehicleHasDepotOrders(const Vehicle *v)
 
{
 
	const Order *order;
 

	
 
	FOR_VEHICLE_ORDERS(v, order) {
 
		if (order->type == OT_GOTO_DEPOT)
 
			return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
/**
 
 *
 
 * Delete all orders from a vehicle
 
 *
 
 */
 
void DeleteVehicleOrders(Vehicle *v)
 
{
 
	Order *cur, *next;
 

	
 
	DeleteOrderWarnings(v);
 

	
 
	/* If we have a shared order-list, don't delete the list, but just
 
	    remove our pointer */
 
	if (IsOrderListShared(v)) {
 
		const Vehicle *u = v;
 

	
 
		v->orders = NULL;
 
		v->num_orders = 0;
 

	
 
		/* Unlink ourself */
 
		if (v->prev_shared != NULL) {
 
			v->prev_shared->next_shared = v->next_shared;
 
			u = v->prev_shared;
 
		}
 
		if (v->next_shared != NULL) {
 
			v->next_shared->prev_shared = v->prev_shared;
 
			u = v->next_shared;
 
		}
 
		v->prev_shared = NULL;
 
		v->next_shared = NULL;
 

	
 
		/* We only need to update this-one, because if there is a third
 
		 *  vehicle which shares the same order-list, nothing will change. If
 
		 *  this is the last vehicle, the last line of the order-window
 
		 *  will change from Shared order list, to Order list, so it needs
 
		 *  an update */
 
		InvalidateVehicleOrder(u);
 
		return;
 
	}
 

	
 
	/* Remove the orders */
 
	cur = v->orders;
 
	v->orders = NULL;
 
	v->num_orders = 0;
 

	
 
	if (cur != NULL) {
 
		/* Delete the vehicle list of shared orders, if any */
 
		int window_type = 0;
 

	
 
		switch (v->type) {
 
			case VEH_Train:    window_type = WC_TRAINS_LIST;   break;
 
			case VEH_Road:     window_type = WC_ROADVEH_LIST;  break;
 
			case VEH_Ship:     window_type = WC_SHIPS_LIST;    break;
 
			case VEH_Aircraft: window_type = WC_AIRCRAFT_LIST; break;
 
			default: NOT_REACHED();
 
		}
 
		DeleteWindowById(window_type, (cur->index << 16) | (v->type << 11) | VLW_SHARED_ORDERS | v->owner);
 
	}
 

	
 
	while (cur != NULL) {
 
		next = cur->next;
 
		DeleteOrder(cur);
 
		cur = next;
 
	}
 
}
 

	
 
/**
 
 *
 
 * Check if we share our orders with an other vehicle
 
 *
 
 * @return Returns the vehicle who has the same order
 
 *
 
 */
 
bool IsOrderListShared(const Vehicle *v)
 
{
 
	return v->next_shared != NULL || v->prev_shared != NULL;
 
}
 

	
 
/**
 
 *
 
 * Check if a vehicle has any valid orders
 
 *
 
 * @return false if there are no valid orders
 
 *
 
 */
 
bool CheckForValidOrders(const Vehicle* v)
 
{
 
	const Order *order;
 

	
 
	FOR_VEHICLE_ORDERS(v, order) if (order->type != OT_DUMMY) return true;
 

	
 
	return false;
 
}
 

	
 
void InitializeOrders(void)
 
{
 
	CleanPool(&_Order_pool);
 
	AddBlockToPool(&_Order_pool);
 

	
 
	_backup_orders_tile = 0;
 
}
 

	
 
static const SaveLoad _order_desc[] = {
 
	SLE_VAR(Order, type,  SLE_UINT8),
 
	SLE_VAR(Order, flags, SLE_UINT8),
 
	SLE_VAR(Order, dest,  SLE_UINT16),
 
	SLE_REF(Order, next,  REF_ORDER),
 
	SLE_CONDVAR(Order, refit_cargo,    SLE_UINT8, 36, SL_MAX_VERSION),
 
	SLE_CONDVAR(Order, refit_subtype,  SLE_UINT8, 36, SL_MAX_VERSION),
 

	
 
	/* Leftover from the minor savegame version stuff
 
	 * We will never use those free bytes, but we have to keep this line to allow loading of old savegames */
 
	SLE_CONDNULL(10, 5, 35),
 
	SLE_END()
 
};
 

	
 
static void Save_ORDR(void)
 
{
 
	Order *order;
 

	
 
	FOR_ALL_ORDERS(order) {
 
		SlSetArrayIndex(order->index);
 
		SlObject(order, _order_desc);
 
	}
 
}
 

	
 
static void Load_ORDR(void)
 
{
 
	if (CheckSavegameVersionOldStyle(5, 2)) {
 
		/* Version older than 5.2 did not have a ->next pointer. Convert them
 
		    (in the old days, the orderlist was 5000 items big) */
 
		uint len = SlGetFieldLength();
 
		uint i;
 

	
 
		if (CheckSavegameVersion(5)) {
 
			/* Pre-version 5 had an other layout for orders
 
			    (uint16 instead of uint32) */
 
			uint16 orders[5000];
 

	
 
			len /= sizeof(uint16);
 
			assert (len <= lengthof(orders));
 

	
 
			SlArray(orders, len, SLE_UINT16);
 

	
 
			for (i = 0; i < len; ++i) {
 
				if (!AddBlockIfNeeded(&_Order_pool, i))
 
					error("Orders: failed loading savegame: too many orders");
 

	
 
				AssignOrder(GetOrder(i), UnpackVersion4Order(orders[i]));
 
			}
 
		} else if (CheckSavegameVersionOldStyle(5, 2)) {
 
			uint32 orders[5000];
 

	
 
			len /= sizeof(uint32);
 
			assert (len <= lengthof(orders));
 

	
 
			SlArray(orders, len, SLE_UINT32);
 

	
 
			for (i = 0; i < len; ++i) {
 
				if (!AddBlockIfNeeded(&_Order_pool, i))
 
					error("Orders: failed loading savegame: too many orders");
 

	
 
				AssignOrder(GetOrder(i), UnpackOrder(orders[i]));
 
			}
 
		}
 

	
 
		/* Update all the next pointer */
 
		for (i = 1; i < len; ++i) {
 
			/* The orders were built like this:
 
			 *   Vehicle one had order[0], and as long as order++.type was not
 
			 *   OT_NOTHING, it was part of the order-list of that vehicle */
 
			if (GetOrder(i)->type != OT_NOTHING)
 
				GetOrder(i - 1)->next = GetOrder(i);
 
		}
 
	} else {
 
		int index;
 

	
 
		while ((index = SlIterateArray()) != -1) {
 
			Order *order;
 

	
 
			if (!AddBlockIfNeeded(&_Order_pool, index))
 
				error("Orders: failed loading savegame: too many orders");
 

	
 
			order = GetOrder(index);
 
			SlObject(order, _order_desc);
 
		}
 
	}
 
}
 

	
 
const ChunkHandler _order_chunk_handlers[] = {
 
	{ 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY | CH_LAST},
 
};
src/order_gui.c
Show inline comments
 
deleted file
src/order_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "road_map.h"
 
#include "station_map.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "vehicle.h"
 
#include "station.h"
 
#include "town.h"
 
#include "command.h"
 
#include "viewport.h"
 
#include "depot.h"
 
#include "waypoint.h"
 
#include "train.h"
 
#include "water_map.h"
 
#include "vehicle_gui.h"
 

	
 
static int OrderGetSel(const Window *w)
 
{
 
	const Vehicle *v = GetVehicle(w->window_number);
 
	int num = WP(w,order_d).sel;
 

	
 
	return (num >= 0 && num < v->num_orders) ? num : v->num_orders;
 
}
 

	
 
static StringID StationOrderStrings[] = {
 
	STR_8806_GO_TO,
 
	STR_8807_GO_TO_TRANSFER,
 
	STR_8808_GO_TO_UNLOAD,
 
	STR_8809_GO_TO_TRANSFER_UNLOAD,
 
	STR_880A_GO_TO_LOAD,
 
	STR_880B_GO_TO_TRANSFER_LOAD,
 
	STR_NULL,
 
	STR_NULL,
 
	STR_880C_GO_NON_STOP_TO,
 
	STR_880D_GO_TO_NON_STOP_TRANSFER,
 
	STR_880E_GO_NON_STOP_TO_UNLOAD,
 
	STR_880F_GO_TO_NON_STOP_TRANSFER_UNLOAD,
 
	STR_8810_GO_NON_STOP_TO_LOAD,
 
	STR_8811_GO_TO_NON_STOP_TRANSFER_LOAD,
 
	STR_NULL
 
};
 

	
 
static void DrawOrdersWindow(Window *w)
 
{
 
	const Vehicle *v;
 
	const Order *order;
 
	StringID str;
 
	int sel;
 
	int y, i;
 
	bool shared_orders;
 
	byte color;
 

	
 
	v = GetVehicle(w->window_number);
 

	
 
	shared_orders = IsOrderListShared(v);
 

	
 
	SetVScrollCount(w, v->num_orders + 1);
 

	
 
	sel = OrderGetSel(w);
 
	SetDParam(2, STR_8827_FULL_LOAD);
 

	
 
	order = GetVehicleOrder(v, sel);
 

	
 
	if (v->owner == _local_player) {
 
		/* skip */
 
		SetWindowWidgetDisabledState(w,  4, v->num_orders == 0);
 

	
 
		/* delete */
 
		SetWindowWidgetDisabledState(w,  5,
 
				(uint)v->num_orders + (shared_orders ? 1 : 0) <= (uint)WP(w, order_d).sel);
 

	
 
		/* non-stop only for trains */
 
		SetWindowWidgetDisabledState(w,  6, v->type != VEH_Train || order == NULL);
 
		SetWindowWidgetDisabledState(w,  8, order == NULL); // full load
 
		SetWindowWidgetDisabledState(w,  9, order == NULL); // unload
 
		SetWindowWidgetDisabledState(w, 10, order == NULL); // transfer
 
		/* Disable list of vehicles with the same shared orders if there is no list */
 
		SetWindowWidgetDisabledState(w, 11, !shared_orders || v->orders == NULL);
 
		SetWindowWidgetDisabledState(w, 12, order == NULL); // Refit
 
		HideWindowWidget(w, 12); // Refit
 
	} else {
 
		DisableWindowWidget(w, 10);
 
	}
 

	
 
	ShowWindowWidget(w, 9); // Unload
 

	
 
	if (order != NULL) {
 
		switch (order->type) {
 
			case OT_GOTO_STATION: break;
 

	
 
			case OT_GOTO_DEPOT:
 
				DisableWindowWidget(w, 10);
 

	
 
				/* Remove unload and replace it with refit */
 
				HideWindowWidget(w,  9);
 
				ShowWindowWidget(w, 12);
 
				SetDParam(2,STR_SERVICE);
 
				break;
 

	
 
			case OT_GOTO_WAYPOINT:
 
				DisableWindowWidget(w,  8);
 
				DisableWindowWidget(w,  9);
 
				DisableWindowWidget(w, 10);
 
				break;
 

	
 
			default: // every other orders
 
				DisableWindowWidget(w, 6);
 
				DisableWindowWidget(w, 8);
 
				DisableWindowWidget(w, 9);
 
		}
 
	}
 

	
 
	SetDParam(0, v->string_id);
 
	SetDParam(1, v->unitnumber);
 
	DrawWindowWidgets(w);
 

	
 
	y = 15;
 

	
 
	i = w->vscroll.pos;
 
	order = GetVehicleOrder(v, i);
 
	while (order != NULL) {
 
		str = (v->cur_order_index == i) ? STR_8805 : STR_8804;
 
		SetDParam(3, STR_EMPTY);
 

	
 
		if (i - w->vscroll.pos < w->vscroll.cap) {
 
			SetDParam(1, 6);
 

	
 
			switch (order->type) {
 
				case OT_GOTO_STATION:
 
					SetDParam(1, StationOrderStrings[order->flags]);
 
					SetDParam(2, order->dest);
 
					break;
 

	
 
				case OT_GOTO_DEPOT: {
 
					StringID s = STR_NULL;
 

	
 
					if (v->type == VEH_Aircraft) {
 
						s = STR_GO_TO_AIRPORT_HANGAR;
 
						SetDParam(2, order->dest);
 
					} else {
 
						SetDParam(2, GetDepot(order->dest)->town_index);
 

	
 
						switch (v->type) {
 
							case VEH_Train: s = (order->flags & OF_NON_STOP) ? STR_880F_GO_NON_STOP_TO_TRAIN_DEPOT : STR_GO_TO_TRAIN_DEPOT; break;
 
							case VEH_Road:  s = STR_9038_GO_TO_ROADVEH_DEPOT; break;
 
							case VEH_Ship:  s = STR_GO_TO_SHIP_DEPOT; break;
 
							default: break;
 
						}
 
					}
 

	
 
					if (order->flags & OF_FULL_LOAD) s++; /* service at */
 

	
 
					SetDParam(1, s);
 
					if (order->refit_cargo < NUM_CARGO) {
 
						SetDParam(3, STR_REFIT_ORDER);
 
						SetDParam(4, _cargoc.names_s[order->refit_cargo]);
 
					} else {
 
						SetDParam(3, STR_EMPTY);
 
					}
 
					break;
 
				}
 

	
 
				case OT_GOTO_WAYPOINT:
 
					SetDParam(1, (order->flags & OF_NON_STOP) ? STR_GO_NON_STOP_TO_WAYPOINT : STR_GO_TO_WAYPOINT);
 
					SetDParam(2, order->dest);
 
					break;
 

	
 
				default: break;
 
			}
 

	
 
			color = (i == WP(w,order_d).sel) ? 0xC : 0x10;
 
			SetDParam(0, i + 1);
 
			if (order->type != OT_DUMMY) {
 
				DrawString(2, y, str, color);
 
			} else {
 
				SetDParam(1, STR_INVALID_ORDER);
 
				SetDParam(2, order->dest);
 
				DrawString(2, y, str, color);
 
			}
 
			y += 10;
 
		}
 

	
 
		i++;
 
		order = order->next;
 
	}
 

	
 
	if (i - w->vscroll.pos < w->vscroll.cap) {
 
		str = shared_orders ? STR_END_OF_SHARED_ORDERS : STR_882A_END_OF_ORDERS;
 
		color = (i == WP(w,order_d).sel) ? 0xC : 0x10;
 
		DrawString(2, y, str, color);
 
	}
 
}
 

	
 
static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
 
{
 
	Order order;
 
	order.next  = NULL;
 
	order.index = 0;
 
	order.refit_cargo   = CT_INVALID;
 
	order.refit_subtype = 0;
 

	
 
	// check depot first
 
	if (_patches.gotodepot) {
 
		switch (GetTileType(tile)) {
 
		case MP_RAILWAY:
 
			if (v->type == VEH_Train && IsTileOwner(tile, _local_player)) {
 
				if (IsRailDepot(tile)) {
 
					order.type = OT_GOTO_DEPOT;
 
					order.flags = OF_PART_OF_ORDERS;
 
					order.dest = GetDepotByTile(tile)->index;
 
					return order;
 
				}
 
			}
 
			break;
 

	
 
		case MP_STREET:
 
			if (GetRoadTileType(tile) == ROAD_TILE_DEPOT && v->type == VEH_Road && IsTileOwner(tile, _local_player)) {
 
				order.type = OT_GOTO_DEPOT;
 
				order.flags = OF_PART_OF_ORDERS;
 
				order.dest = GetDepotByTile(tile)->index;
 
				return order;
 
			}
 
			break;
 

	
 
		case MP_STATION:
 
			if (v->type != VEH_Aircraft) break;
 
			if (IsHangar(tile) && IsTileOwner(tile, _local_player)) {
 
				order.type = OT_GOTO_DEPOT;
 
				order.flags = OF_PART_OF_ORDERS;
 
				order.dest = GetStationIndex(tile);
 
				return order;
 
			}
 
			break;
 

	
 
		case MP_WATER:
 
			if (v->type != VEH_Ship) break;
 
			if (IsTileDepotType(tile, TRANSPORT_WATER) &&
 
					IsTileOwner(tile, _local_player)) {
 
				TileIndex tile2 = GetOtherShipDepotTile(tile);
 

	
 
				order.type = OT_GOTO_DEPOT;
 
				order.flags = OF_PART_OF_ORDERS;
 
				order.dest = GetDepotByTile(tile < tile2 ? tile : tile2)->index;
 
				return order;
 
			}
 

	
 
			default:
 
				break;
 
		}
 
	}
 

	
 
	// check waypoint
 
	if (IsTileType(tile, MP_RAILWAY) &&
 
			v->type == VEH_Train &&
 
			IsTileOwner(tile, _local_player) &&
 
			IsRailWaypoint(tile)) {
 
		order.type = OT_GOTO_WAYPOINT;
 
		order.flags = 0;
 
		order.dest = GetWaypointByTile(tile)->index;
 
		return order;
 
	}
 

	
 
	if (IsTileType(tile, MP_STATION)) {
 
		StationID st_index = GetStationIndex(tile);
 
		const Station *st = GetStation(st_index);
 

	
 
		if (st->owner == _current_player || st->owner == OWNER_NONE) {
 
			byte facil;
 
			(facil=FACIL_DOCK, v->type == VEH_Ship) ||
 
			(facil=FACIL_TRAIN, v->type == VEH_Train) ||
 
			(facil=FACIL_AIRPORT, v->type == VEH_Aircraft) ||
 
			(facil=FACIL_BUS_STOP, v->type == VEH_Road && v->cargo_type == CT_PASSENGERS) ||
 
			(facil=FACIL_TRUCK_STOP, 1);
 
			if (st->facilities & facil) {
 
				order.type = OT_GOTO_STATION;
 
				order.flags = 0;
 
				order.dest = st_index;
 
				return order;
 
			}
 
		}
 
	}
 

	
 
	// not found
 
	order.type = OT_NOTHING;
 
	order.flags = 0;
 
	order.dest = INVALID_STATION;
 
	return order;
 
}
 

	
 
static bool HandleOrderVehClick(const Vehicle *v, const Vehicle *u, Window *w)
 
{
 
	if (u->type != v->type) return false;
 

	
 
	if (u->type == VEH_Train && !IsFrontEngine(u)) {
 
		u = GetFirstVehicleInChain(u);
 
		if (!IsFrontEngine(u)) return false;
 
	}
 

	
 
	// v is vehicle getting orders. Only copy/clone orders if vehicle doesn't have any orders yet
 
	// obviously if you press CTRL on a non-empty orders vehicle you know what you are doing
 
	if (v->num_orders != 0 && _ctrl_pressed == 0) return false;
 

	
 
	if (DoCommandP(v->tile, v->index | (u->index << 16), _ctrl_pressed ? 0 : 1, NULL,
 
		_ctrl_pressed ? CMD_CLONE_ORDER | CMD_MSG(STR_CANT_SHARE_ORDER_LIST) : CMD_CLONE_ORDER | CMD_MSG(STR_CANT_COPY_ORDER_LIST))) {
 
		WP(w,order_d).sel = -1;
 
		ResetObjectToPlace();
 
	}
 

	
 
	return true;
 
}
 

	
 
static void OrdersPlaceObj(const Vehicle *v, TileIndex tile, Window *w)
 
{
 
	Order cmd;
 
	const Vehicle *u;
 

	
 
	// check if we're clicking on a vehicle first.. clone orders in that case.
 
	u = CheckMouseOverVehicle();
 
	if (u != NULL && HandleOrderVehClick(v, u, w)) return;
 

	
 
	cmd = GetOrderCmdFromTile(v, tile);
 
	if (cmd.type == OT_NOTHING) return;
 

	
 
	if (DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), PackOrder(&cmd), NULL, CMD_INSERT_ORDER | CMD_MSG(STR_8833_CAN_T_INSERT_NEW_ORDER))) {
 
		if (WP(w,order_d).sel != -1) WP(w,order_d).sel++;
 
		ResetObjectToPlace();
 
	}
 
}
 

	
 
static void OrderClick_Goto(Window *w, const Vehicle *v)
 
{
 
	InvalidateWidget(w, 7);
 
	ToggleWidgetLoweredState(w, 7);
 
	if (IsWindowWidgetLowered(w, 7)) {
 
		_place_clicked_vehicle = NULL;
 
		SetObjectToPlaceWnd(ANIMCURSOR_PICKSTATION, 1, w);
 
	} else {
 
		ResetObjectToPlace();
 
	}
 
}
 

	
 
static void OrderClick_FullLoad(Window *w, const Vehicle *v)
 
{
 
	DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), OFB_FULL_LOAD, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
 
}
 

	
 
static void OrderClick_Unload(Window *w, const Vehicle *v)
 
{
 
	DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), OFB_UNLOAD,    NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
 
}
 

	
 
static void OrderClick_Nonstop(Window *w, const Vehicle *v)
 
{
 
	DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), OFB_NON_STOP,  NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
 
}
 

	
 
static void OrderClick_Transfer(Window* w, const Vehicle* v)
 
{
 
	DoCommandP(v->tile, v->index + (OrderGetSel(w) <<  16), OFB_TRANSFER, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER));
 
}
 

	
 
static void OrderClick_Skip(Window *w, const Vehicle *v)
 
{
 
	DoCommandP(v->tile, v->index, 0, NULL, CMD_SKIP_ORDER);
 
}
 

	
 
static void OrderClick_Delete(Window *w, const Vehicle *v)
 
{
 
	DoCommandP(v->tile, v->index, OrderGetSel(w), NULL, CMD_DELETE_ORDER | CMD_MSG(STR_8834_CAN_T_DELETE_THIS_ORDER));
 
}
 

	
 
static void OrderClick_Refit(Window *w, const Vehicle *v)
 
{
 
	if (_ctrl_pressed) {
 
		/* Cancel refitting */
 
		DoCommandP(v->tile, v->index, (WP(w,order_d).sel << 16) | (CT_NO_REFIT << 8) | CT_NO_REFIT, NULL, CMD_ORDER_REFIT);
 
	} else {
 
		ShowVehicleRefitWindow(v, WP(w,order_d).sel);
 
	}
 
}
 

	
 
typedef void OnButtonVehClick(Window *w, const Vehicle *v);
 

	
 
static OnButtonVehClick* const _order_button_proc[] = {
 
	OrderClick_Skip,
 
	OrderClick_Delete,
 
	OrderClick_Nonstop,
 
	OrderClick_Goto,
 
	OrderClick_FullLoad,
 
	OrderClick_Unload,
 
	OrderClick_Transfer
 
};
 

	
 
static const uint16 _order_keycodes[] = {
 
	'D', //skip order
 
	'F', //delete order
 
	'G', //non-stop
 
	'H', //goto order
 
	'J', //full load
 
	'K'  //unload
 
};
 

	
 
static void OrdersWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_CREATE:
 
			/* Move Refit to the same location as Unload
 
			 * This will ensure that they always stay at the same location even if Unload is moved in a later commit */
 
			w->widget[12].left   = w->widget[9].left;
 
			w->widget[12].right  = w->widget[9].right;
 
			w->widget[12].top    = w->widget[9].top;
 
			w->widget[12].bottom = w->widget[9].bottom;
 
			break;
 

	
 
	case WE_PAINT:
 
		DrawOrdersWindow(w);
 
		break;
 

	
 
	case WE_CLICK: {
 
		Vehicle *v = GetVehicle(w->window_number);
 
		switch (e->we.click.widget) {
 
		case 2: { /* orders list */
 
			int sel = (e->we.click.pt.y - 15) / 10;
 

	
 
			if ((uint)sel >= w->vscroll.cap) return;
 

	
 
			sel += w->vscroll.pos;
 

	
 
			if (_ctrl_pressed && sel < v->num_orders) {
 
				const Order *ord = GetVehicleOrder(v, sel);
 
				TileIndex xy;
 

	
 
				switch (ord->type) {
 
					case OT_GOTO_STATION:  xy = GetStation(ord->dest)->xy ; break;
 
					case OT_GOTO_DEPOT:    xy = GetDepot(ord->dest)->xy;    break;
 
					case OT_GOTO_WAYPOINT: xy = GetWaypoint(ord->dest)->xy; break;
 
					default:               xy = 0; break;
 
				}
 

	
 
				if (xy != 0) ScrollMainWindowToTile(xy);
 
				return;
 
			}
 

	
 
			if (sel == WP(w,order_d).sel) sel = -1;
 
			WP(w,order_d).sel = sel;
 
			SetWindowDirty(w);
 
		}	break;
 

	
 
		case 4: /* skip button */
 
			OrderClick_Skip(w, v);
 
			break;
 

	
 
		case 5: /* delete button */
 
			OrderClick_Delete(w, v);
 
			break;
 

	
 
		case 6: /* non stop button */
 
			OrderClick_Nonstop(w, v);
 
			break;
 

	
 
		case 7: /* goto button */
 
			OrderClick_Goto(w, v);
 
			break;
 

	
 
		case 8: /* full load button */
 
			OrderClick_FullLoad(w, v);
 
			break;
 

	
 
		case 9: /* unload button */
 
			OrderClick_Unload(w, v);
 
			break;
 
		case 10: /* transfer button */
 
			OrderClick_Transfer(w, v);
 
			break;
 
		case 11: /* Vehicle with same shared Orders button */
 
			ShowVehWithSharedOrders(v, v->type);
 
			break;
 
		case 12:
 
			OrderClick_Refit(w, v);
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_KEYPRESS: {
 
		Vehicle *v = GetVehicle(w->window_number);
 
		uint i;
 

	
 
		if (v->owner != _local_player) break;
 

	
 
		for (i = 0; i < lengthof(_order_keycodes); i++) {
 
			if (e->we.keypress.keycode == _order_keycodes[i]) {
 
				e->we.keypress.cont = false;
 
				//see if the button is disabled
 
				if (!IsWindowWidgetDisabled(w, i + 4)) _order_button_proc[i](w, v);
 
				break;
 
			}
 
		}
 
		break;
 
	}
 

	
 
	case WE_RCLICK: {
 
		const Vehicle *v = GetVehicle(w->window_number);
 
		int s = OrderGetSel(w);
 

	
 
		if (e->we.click.widget != 8) break;
 
		if (s == v->num_orders || GetVehicleOrder(v, s)->type != OT_GOTO_DEPOT) {
 
			GuiShowTooltips(STR_8857_MAKE_THE_HIGHLIGHTED_ORDER);
 
		} else {
 
			GuiShowTooltips(STR_SERVICE_HINT);
 
		}
 
	} break;
 

	
 
	case WE_PLACE_OBJ: {
 
		OrdersPlaceObj(GetVehicle(w->window_number), e->we.place.tile, w);
 
	} break;
 

	
 
	case WE_ABORT_PLACE_OBJ: {
 
		RaiseWindowWidget(w, 7);
 
		InvalidateWidget(w, 7);
 
	} break;
 

	
 
	// check if a vehicle in a depot was clicked..
 
	case WE_MOUSELOOP: {
 
		const Vehicle *v = _place_clicked_vehicle;
 
		/*
 
		 * Check if we clicked on a vehicle
 
		 * and if the GOTO button of this window is pressed
 
		 * This is because of all open order windows WE_MOUSELOOP is called
 
		 * and if you have 3 windows open, and this check is not done
 
		 * the order is copied to the last open window instead of the
 
		 * one where GOTO is enabled
 
		 */
 
		if (v != NULL && IsWindowWidgetLowered(w, 7)) {
 
			_place_clicked_vehicle = NULL;
 
			HandleOrderVehClick(GetVehicle(w->window_number), v, w);
 
		}
 
	} break;
 

	
 
	case WE_RESIZE:
 
		/* Update the scroll + matrix */
 
		w->vscroll.cap = (w->widget[2].bottom - w->widget[2].top) / 10;
 
		break;
 

	
 
	case WE_TIMEOUT: { // handle button unclick ourselves...
 
		// unclick all buttons except for the 'goto' button (7), which is 'persistent'
 
		uint i;
 
		for (i = 0; i < w->widget_count; i++) {
 
			if (IsWindowWidgetLowered(w, i) && i != 7) {
 
				RaiseWindowWidget(w, i);
 
				InvalidateWidget(w, i);
 
			}
 
		}
 
	} break;
 
	}
 
}
 

	
 
static const Widget _orders_train_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_RIGHT,   14,    11,   398,     0,    13, STR_8829_ORDERS,         STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_RB,      14,     0,   386,    14,    75, 0x0,                     STR_8852_ORDERS_LIST_CLICK_ON_ORDER},
 
{  WWT_SCROLLBAR,   RESIZE_LRB,     14,   387,   398,    14,    75, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,     0,    52,    76,    87, STR_8823_SKIP,           STR_8853_SKIP_THE_CURRENT_ORDER},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,    53,   105,    76,    87, STR_8824_DELETE,         STR_8854_DELETE_THE_HIGHLIGHTED},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   106,   158,    76,    87, STR_8825_NON_STOP,       STR_8855_MAKE_THE_HIGHLIGHTED_ORDER},
 
{    WWT_TEXTBTN,   RESIZE_TB,      14,   159,   211,    76,    87, STR_8826_GO_TO,          STR_8856_INSERT_A_NEW_ORDER_BEFORE},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   212,   264,    76,    87, STR_FULLLOAD_OR_SERVICE, STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   265,   319,    76,    87, STR_8828_UNLOAD,         STR_8858_MAKE_THE_HIGHLIGHTED_ORDER},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   320,   372,    76,    87, STR_886F_TRANSFER,       STR_886D_MAKE_THE_HIGHLIGHTED_ORDER},
 
{ WWT_PUSHIMGBTN,   RESIZE_TB,      14,   373,   386,    76,    87, SPR_SHARED_ORDERS_ICON,  STR_VEH_WITH_SHARED_ORDERS_LIST_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   320,   372,    76,    87, STR_REFIT,               STR_REFIT_TIP},
 
{      WWT_PANEL,   RESIZE_RTB,     14,   387,   386,    76,    87, 0x0,                     STR_NULL},
 
{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   387,   398,    76,    87, 0x0,                     STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _orders_train_desc = {
 
	WDP_AUTO, WDP_AUTO, 399, 88,
 
	WC_VEHICLE_ORDERS,WC_VEHICLE_VIEW,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE,
 
	_orders_train_widgets,
 
	OrdersWndProc
 
};
 

	
 
static const Widget _orders_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_RIGHT,   14,    11,   409,     0,    13, STR_8829_ORDERS,         STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_RB,      14,     0,   397,    14,    75, 0x0,                     STR_8852_ORDERS_LIST_CLICK_ON_ORDER},
 
{  WWT_SCROLLBAR,   RESIZE_LRB,     14,   398,   409,    14,    75, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,     0,    63,    76,    87, STR_8823_SKIP,           STR_8853_SKIP_THE_CURRENT_ORDER},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,    64,   128,    76,    87, STR_8824_DELETE,         STR_8854_DELETE_THE_HIGHLIGHTED},
 
{      WWT_EMPTY,   RESIZE_TB,      14,     0,     0,    76,    87, 0x0,                     0x0},
 
{    WWT_TEXTBTN,   RESIZE_TB,      14,   129,   192,    76,    87, STR_8826_GO_TO,          STR_8856_INSERT_A_NEW_ORDER_BEFORE},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   193,   256,    76,    87, STR_FULLLOAD_OR_SERVICE, STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   257,   319,    76,    87, STR_8828_UNLOAD,         STR_8858_MAKE_THE_HIGHLIGHTED_ORDER},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   320,   383,    76,    87, STR_886F_TRANSFER,       STR_886D_MAKE_THE_HIGHLIGHTED_ORDER},
 
{ WWT_PUSHIMGBTN,   RESIZE_TB,      14,   384,   397,    76,    87, SPR_SHARED_ORDERS_ICON,  STR_VEH_WITH_SHARED_ORDERS_LIST_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_TB,      14,   320,   383,    76,    87, STR_REFIT,               STR_REFIT_TIP},
 
{      WWT_PANEL,   RESIZE_RTB,     14,   397,   396,    76,    87, 0x0,                     STR_NULL},
 
{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   398,   409,    76,    87, 0x0,                     STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _orders_desc = {
 
	WDP_AUTO, WDP_AUTO, 410, 88,
 
	WC_VEHICLE_ORDERS,WC_VEHICLE_VIEW,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE,
 
	_orders_widgets,
 
	OrdersWndProc
 
};
 

	
 
static const Widget _other_orders_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,        STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_RIGHT,   14,    11,   331,     0,    13, STR_A00B_ORDERS, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_RB,      14,     0,   319,    14,    75, 0x0,             STR_8852_ORDERS_LIST_CLICK_ON_ORDER},
 
{  WWT_SCROLLBAR,   RESIZE_LRB,     14,   320,   331,    14,    75, 0x0,             STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_EMPTY,   RESIZE_NONE,    14,     0,   319,    76,    87, 0x0,             STR_NULL},
 
{      WWT_EMPTY,   RESIZE_NONE,    14,     0,   319,    76,    87, 0x0,             STR_NULL},
 
{      WWT_EMPTY,   RESIZE_NONE,    14,     0,   319,    76,    87, 0x0,             STR_NULL},
 
{      WWT_EMPTY,   RESIZE_NONE,    14,     0,   319,    76,    87, 0x0,             STR_NULL},
 
{      WWT_EMPTY,   RESIZE_NONE,    14,     0,   319,    76,    87, 0x0,             STR_NULL},
 
{      WWT_EMPTY,   RESIZE_NONE,    14,     0,   319,    76,    87, 0x0,             STR_NULL},
 
{      WWT_PANEL,   RESIZE_RTB,     14,     0,   319,    76,    87, 0x0,             STR_NULL},
 
{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   320,   331,    76,    87, 0x0,             STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _other_orders_desc = {
 
	WDP_AUTO, WDP_AUTO, 332, 88,
 
	WC_VEHICLE_ORDERS,WC_VEHICLE_VIEW,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_other_orders_widgets,
 
	OrdersWndProc
 
};
 

	
 
void ShowOrdersWindow(const Vehicle *v)
 
{
 
	Window *w;
 
	VehicleID veh = v->index;
 

	
 
	DeleteWindowById(WC_VEHICLE_ORDERS, veh);
 
	DeleteWindowById(WC_VEHICLE_DETAILS, veh);
 

	
 
	if (v->owner != _local_player) {
 
		w = AllocateWindowDescFront(&_other_orders_desc, veh);
 
	} else {
 
		w = AllocateWindowDescFront((v->type == VEH_Train) ? &_orders_train_desc : &_orders_desc, veh);
 
	}
 

	
 
	if (w != NULL) {
 
		w->caption_color = v->owner;
 
		w->vscroll.cap = 6;
 
		w->resize.step_height = 10;
 
		WP(w,order_d).sel = -1;
 
	}
 
}
src/os/macosx/G5_detector.c
Show inline comments
 
deleted file
src/os/macosx/G5_detector.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include <mach/mach.h>
 
#include <mach/mach_host.h>
 
#include <mach/host_info.h>
 
#include <mach/machine.h>
 
#include <stdio.h>
 

	
 

	
 
#ifndef CPU_SUBTYPE_POWERPC_970
 
#define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100)
 
#endif
 

	
 
// this function is a lightly modified version of some code from Apple's developer homepage to detect G5 CPUs at runtime
 
main()
 
{
 
	host_basic_info_data_t hostInfo;
 
	mach_msg_type_number_t infoCount;
 
	boolean_t is_G5;
 

	
 
	infoCount = HOST_BASIC_INFO_COUNT;
 
	host_info(mach_host_self(), HOST_BASIC_INFO,
 
			  (host_info_t)&hostInfo, &infoCount);
 

	
 
	 is_G5 = ((hostInfo.cpu_type == CPU_TYPE_POWERPC) &&
 
			(hostInfo.cpu_subtype == CPU_SUBTYPE_POWERPC_970));
 
	 if (is_G5)
 
		 printf("1");
 
}
src/os/macosx/splash.c
Show inline comments
 
deleted file
src/os/macosx/splash.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../../stdafx.h"
 
#include "../../openttd.h"
 
#include "../../variables.h"
 
#include "../../macros.h"
 
#include "../../debug.h"
 
#include "../../functions.h"
 
#include "../../gfx.h"
 
#include "../../fileio.h"
 

	
 
#include "splash.h"
 

	
 
#ifdef WITH_PNG
 

	
 
#include <png.h>
 

	
 
static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message)
 
{
 
	DEBUG(misc, 0, "[libpng] error: %s - %s", message, (char *)png_get_error_ptr(png_ptr));
 
	longjmp(png_ptr->jmpbuf, 1);
 
}
 

	
 
static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message)
 
{
 
	DEBUG(misc, 1, "[libpng] warning: %s - %s", message, (char *)png_get_error_ptr(png_ptr));
 
}
 

	
 
void DisplaySplashImage(void)
 
{
 
	png_byte header[8];
 
	FILE *f;
 
	png_structp png_ptr;
 
	png_infop info_ptr, end_info;
 
	uint width, height, bit_depth, color_type;
 
	png_colorp palette;
 
	int num_palette;
 
	png_bytep *row_pointers;
 
	uint8 *src, *dst;
 
	uint y;
 
	uint xoff, yoff;
 
	int i;
 

	
 
	f = FioFOpenFile(SPLASH_IMAGE_FILE);
 
	if (f == NULL) return;
 

	
 
	fread(header, 1, 8, f);
 
	if (png_sig_cmp(header, 0, 8) != 0) {
 
		fclose(f);
 
		return;
 
	}
 

	
 
	png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, (png_voidp) NULL, png_my_error, png_my_warning);
 

	
 
	if (png_ptr == NULL) {
 
		fclose(f);
 
		return;
 
	}
 

	
 
	info_ptr = png_create_info_struct(png_ptr);
 
	if (info_ptr == NULL) {
 
		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
 
		fclose(f);
 
		return;
 
	}
 

	
 
	end_info = png_create_info_struct(png_ptr);
 
	if (end_info == NULL) {
 
		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
 
		fclose(f);
 
		return;
 
	}
 

	
 
	if (setjmp(png_jmpbuf(png_ptr))) {
 
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
 
		fclose(f);
 
		return;
 
	}
 

	
 
	png_init_io(png_ptr, f);
 
	png_set_sig_bytes(png_ptr, 8);
 

	
 
	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
 

	
 
	width            = png_get_image_width(png_ptr, info_ptr);
 
	height           = png_get_image_height(png_ptr, info_ptr);
 
	bit_depth        = png_get_bit_depth(png_ptr, info_ptr);
 
	color_type       = png_get_color_type(png_ptr, info_ptr);
 

	
 
	if (color_type != PNG_COLOR_TYPE_PALETTE || bit_depth != 8) {
 
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
 
		fclose(f);
 
		return;
 
	}
 

	
 
	if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) {
 
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
 
		fclose(f);
 
		return;
 
	}
 

	
 
	png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
 

	
 
	row_pointers = png_get_rows(png_ptr, info_ptr);
 

	
 
	memset(_screen.dst_ptr, 0xff, _screen.pitch * _screen.height);
 

	
 
	if (width > (uint) _screen.width) width = _screen.width;
 
	if (height > (uint) _screen.height) height = _screen.height;
 

	
 
	xoff = (_screen.width - width) / 2;
 
	yoff = (_screen.height - height) / 2;
 
	for (y = 0; y < height; y++) {
 
		src = row_pointers[y];
 
		dst = ((uint8 *) _screen.dst_ptr) + (yoff + y) * _screen.pitch + xoff;
 

	
 
		memcpy(dst, src, width);
 
	}
 

	
 
	for (i = 0; i < num_palette; i++) {
 
		_cur_palette[i].r = palette[i].red;
 
		_cur_palette[i].g = palette[i].green;
 
		_cur_palette[i].b = palette[i].blue;
 
	}
 

	
 
	_cur_palette[0xff].r = 0;
 
	_cur_palette[0xff].g = 0;
 
	_cur_palette[0xff].b = 0;
 

	
 
	_pal_first_dirty = 0;
 
	_pal_last_dirty = 0xff;
 

	
 
	png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
 
	fclose(f);
 
	return;
 
}
 

	
 

	
 

	
 
#else /* WITH_PNG */
 

	
 
void DisplaySplashImage(void) {}
 

	
 
#endif /* WITH_PNG */
src/os2.c
Show inline comments
 
deleted file
src/os2.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "variables.h"
 
#include "string.h"
 
#include "table/strings.h"
 
#include "gfx.h"
 
#include "gui.h"
 
#include "functions.h"
 
#include "macros.h"
 

	
 
#include <direct.h>
 
#include <unistd.h>
 
#include <sys/stat.h>
 
#include <stdlib.h>
 
#include <time.h>
 
#include <dos.h>
 

	
 
#define INCL_WIN
 
#define INCL_WINCLIPBOARD
 

	
 
#include <os2.h>
 
#include <i86.h>
 

	
 
bool FiosIsRoot(const char *file)
 
{
 
	return path[3] == '\0';
 
}
 

	
 
void FiosGetDrives(void)
 
{
 
	FiosItem *fios;
 
	unsigned disk, disk2, save, total;
 

	
 
	_dos_getdrive(&save); // save original drive
 

	
 
	/* get an available drive letter */
 
	for (disk = 1;; disk++) {
 
		_dos_setdrive(disk, &total);
 
		if (disk >= total) return;
 
		_dos_getdrive(&disk2);
 

	
 
		if (disk == disk2) {
 
			FiosItem *fios = FiosAlloc();
 
			fios->type = FIOS_TYPE_DRIVE;
 
			fios->mtime = 0;
 
			snprintf(fios->name, lengthof(fios->name),  "%c:", 'A' + disk - 1);
 
			ttd_strlcpy(fios->title, fios->name, lengthof(fios->title));
 
		}
 
	}
 

	
 
	_dos_setdrive(save, &total); // restore the original drive
 
}
 

	
 
bool FiosGetDiskFreeSpace(const char *path, uint32 *tot)
 
{
 
	struct diskfree_t free;
 
	char drive = path[0] - 'A' + 1;
 

	
 
	if (tot != NULL && _getdiskfree(drive, &free) == 0) {
 
		*tot = free.avail_clusters * free.sectors_per_cluster * free.bytes_per_sector;
 
		return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb)
 
{
 
	char filename[MAX_PATH];
 

	
 
	snprintf(filename, lengthof(filename), "%s" PATHSEP "%s", path, ent->d_name);
 
	if (stat(filename, sb) != 0) return false;
 

	
 
	return (ent->d_name[0] != '.'); // hidden file
 
}
 

	
 
static void ChangeWorkingDirectory(char *exe)
 
{
 
	char *s = strrchr(exe, '\\');
 
	if (s != NULL) {
 
		*s = '\0';
 
		chdir(exe);
 
		*s = '\\';
 
	}
 
}
 

	
 
void ShowInfo(const char *str)
 
{
 
	HAB hab;
 
	HMQ hmq;
 
	ULONG rc;
 

	
 
	// init PM env.
 
	hmq = WinCreateMsgQueue((hab = WinInitialize(0)), 0);
 

	
 
	// display the box
 
	rc = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, str, "OpenTTD", 0, MB_OK | MB_MOVEABLE | MB_INFORMATION);
 

	
 
	// terminate PM env.
 
	WinDestroyMsgQueue(hmq);
 
	WinTerminate(hab);
 
}
 

	
 
void ShowOSErrorBox(const char *buf)
 
{
 
	HAB hab;
 
	HMQ hmq;
 
	ULONG rc;
 

	
 
	// init PM env.
 
	hmq = WinCreateMsgQueue((hab = WinInitialize(0)), 0);
 

	
 
	// display the box
 
	rc = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, buf, "OpenTTD", 0, MB_OK | MB_MOVEABLE | MB_ERROR);
 

	
 
	// terminate PM env.
 
	WinDestroyMsgQueue(hmq);
 
	WinTerminate(hab);
 
}
 

	
 
int CDECL main(int argc, char* argv[])
 
{
 
	// change the working directory to enable doubleclicking in UIs
 
	ChangeWorkingDirectory(argv[0]);
 

	
 
	_random_seeds[1][1] = _random_seeds[1][0] = _random_seeds[0][1] = _random_seeds[0][0] = time(NULL);
 

	
 
	return ttd_main(argc, argv);
 
}
 

	
 
void DeterminePaths(void)
 
{
 
	char *s;
 

	
 
	_paths.game_data_dir = malloc(MAX_PATH);
 
	ttd_strlcpy(_paths.game_data_dir, GAME_DATA_DIR, MAX_PATH);
 
	#if defined SECOND_DATA_DIR
 
	_paths.second_data_dir = malloc(MAX_PATH);
 
	ttd_strlcpy(_paths.second_data_dir, SECOND_DATA_DIR, MAX_PATH);
 
	#endif
 

	
 
#if defined(USE_HOMEDIR)
 
	{
 
		const char *homedir = getenv("HOME");
 

	
 
		if (homedir == NULL) {
 
			const struct passwd *pw = getpwuid(getuid());
 
			if (pw != NULL) homedir = pw->pw_dir;
 
		}
 

	
 
		_paths.personal_dir = str_fmt("%s" PATHSEP "%s", homedir, PERSONAL_DIR);
 
	}
 

	
 
#else /* not defined(USE_HOMEDIR) */
 

	
 
	_paths.personal_dir = malloc(MAX_PATH);
 
	ttd_strlcpy(_paths.personal_dir, PERSONAL_DIR, MAX_PATH);
 

	
 
	// check if absolute or relative path
 
	s = strchr(_paths.personal_dir, '\\');
 

	
 
	// add absolute path
 
	if (s == NULL || _paths.personal_dir != s) {
 
		getcwd(_paths.personal_dir, MAX_PATH);
 
		s = strchr(_paths.personal_dir, 0);
 
		*s++ = '\\';
 
		ttd_strlcpy(s, PERSONAL_DIR, MAX_PATH);
 
	}
 

	
 
#endif /* defined(USE_HOMEDIR) */
 

	
 
	s = strchr(_paths.personal_dir, 0);
 

	
 
	// append a / ?
 
	if (s[-1] != '\\') strcpy(s, "\\");
 

	
 
	_paths.save_dir = str_fmt("%ssave", _paths.personal_dir);
 
	_paths.autosave_dir = str_fmt("%s\\autosave", _paths.save_dir);
 
	_paths.scenario_dir = str_fmt("%sscenario", _paths.personal_dir);
 
	_paths.heightmap_dir = str_fmt("%sscenario\\heightmap", _paths.personal_dir);
 
	_paths.gm_dir = str_fmt("%sgm\\", _paths.game_data_dir);
 
	_paths.data_dir = str_fmt("%sdata\\", _paths.game_data_dir);
 

	
 
	if (_config_file == NULL)
 
		_config_file = str_fmt("%sopenttd.cfg", _paths.personal_dir);
 

	
 
	_highscore_file = str_fmt("%shs.dat", _paths.personal_dir);
 
	_log_file = str_fmt("%sopenttd.log", _paths.personal_dir);
 

	
 
#if defined CUSTOM_LANG_DIR
 
	// sets the search path for lng files to the custom one
 
	_paths.lang_dir = malloc( MAX_PATH );
 
	ttd_strlcpy( _paths.lang_dir, CUSTOM_LANG_DIR, MAX_PATH);
 
#else
 
	_paths.lang_dir = str_fmt("%slang\\", _paths.game_data_dir);
 
#endif
 

	
 
	// create necessary folders
 
	mkdir(_paths.personal_dir);
 
	mkdir(_paths.save_dir);
 
	mkdir(_paths.autosave_dir);
 
	mkdir(_paths.scenario_dir);
 
	mkdir(_paths.heightmap_dir);
 
}
 

	
 
/**
 
 * Insert a chunk of text from the clipboard onto the textbuffer. Get TEXT clipboard
 
 * and append this up to the maximum length (either absolute or screenlength). If maxlength
 
 * is zero, we don't care about the screenlength but only about the physical length of the string
 
 * @param tb @Textbuf type to be changed
 
 * @return Return true on successfull change of Textbuf, or false otherwise
 
 */
 
bool InsertTextBufferClipboard(Textbuf *tb)
 
{
 
	HAB hab = 0;
 

	
 
	if (WinOpenClipbrd(hab))
 
	{
 
		const char* text = (const char*)WinQueryClipbrdData(hab, CF_TEXT);
 

	
 
		if (text != NULL)
 
		{
 
			uint length = 0;
 
			uint width = 0;
 
			const char* i;
 

	
 
			for (i = text; IsValidAsciiChar(*i); i++)
 
			{
 
				uint w;
 

	
 
				if (tb->length + length >= tb->maxlength - 1) break;
 

	
 
				w = GetCharacterWidth(FS_NORMAL, (byte)*i);
 
				if (tb->maxwidth != 0 && width + tb->width + w > tb->maxwidth) break;
 

	
 
				width += w;
 
				length++;
 
			}
 

	
 
			memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->length - tb->caretpos + 1);
 
			memcpy(tb->buf + tb->caretpos, text, length);
 
			tb->width += width;
 
			tb->caretxoffs += width;
 
			tb->length += length;
 
			tb->caretpos += length;
 

	
 
			WinCloseClipbrd(hab);
 
			return true;
 
		}
 

	
 
		WinCloseClipbrd(hab);
 
	}
 

	
 
	return false;
 
}
 

	
 

	
 
void CSleep(int milliseconds)
 
{
 
	delay(milliseconds);
 
}
 

	
 
const char *FS2OTTD(const char *name) {return name;}
 
const char *OTTD2FS(const char *name) {return name;}
src/os_timer.c
Show inline comments
 
deleted file
src/os_timer.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 

	
 
#undef RDTSC_AVAILABLE
 

	
 
/* rdtsc for MSC_VER, uses simple inline assembly, or _rdtsc
 
 * from external win64.asm because VS2005 does not support inline assembly */
 
#if defined(_MSC_VER) && !defined(RDTSC_AVAILABLE)
 
# if defined (_M_AMD64)
 
extern uint64 _rdtsc(void);
 
#	else
 
uint64 _declspec(naked) _rdtsc(void)
 
{
 
	_asm {
 
		rdtsc
 
		ret
 
	}
 
}
 
# endif
 
# define RDTSC_AVAILABLE
 
#endif
 

	
 
/* rdtsc for OS/2. Hopefully this works, who knows */
 
#if defined (__WATCOMC__) && !defined(RDTSC_AVAILABLE)
 
unsigned __int64 _rdtsc( void);
 
# pragma aux _rdtsc = 0x0F 0x31 value [edx eax] parm nomemory modify exact [edx eax] nomemory;
 
# define RDTSC_AVAILABLE
 
#endif
 

	
 
/* rdtsc for all other *nix-en (hopefully). Use GCC syntax */
 
#if defined(__i386__) || defined(__x86_64__) && !defined(RDTSC_AVAILABLE)
 
uint64 _rdtsc(void)
 
{
 
	uint32 high, low;
 
	__asm__ __volatile__ ("rdtsc" : "=a" (low), "=d" (high));
 
	return ((uint64)high << 32) | low;
 
}
 
# define RDTSC_AVAILABLE
 
#endif
 

	
 
/* rdtsc for PPC which has this not */
 
#if (defined(__POWERPC__) || defined(__powerpc__)) && !defined(RDTSC_AVAILABLE)
 
uint64 _rdtsc(void)
 
{
 
	uint32 high = 0, high2 = 0, low;
 
	/* PPC does not have rdtsc, so we cheat by reading the two 32-bit time-counters
 
	 * it has, 'Move From Time Base (Upper)'. Since these are two reads, in the
 
	 * very unlikely event that the lower part overflows to the upper part while we
 
	 * read it; we double-check and reread the registers */
 
	asm volatile (
 
				  "mftbu %0\n"
 
				  "mftb %1\n"
 
				  "mftbu %2\n"
 
				  "cmpw %3,%4\n"
 
				  "bne- $-16\n"
 
				  : "=r" (high), "=r" (low), "=r" (high2)
 
				  : "0" (high), "2" (high2)
 
				  );
 
	return ((uint64)high << 32) | low;
 
}
 
# define RDTSC_AVAILABLE
 
#endif
 

	
 
/* In all other cases we have no support for rdtsc. No major issue,
 
 * you just won't be able to profile your code with TIC()/TOC() */
 
#if !defined(RDTSC_AVAILABLE)
 
#warning "(non-fatal) No support for rdtsc(), you won't be able to profile with TIC/TOC"
 
uint64 _rdtsc(void) {return 0;}
 
#endif
src/pathfind.c
Show inline comments
 
deleted file
src/pathfind.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "station_map.h"
 
#include "depot.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "pathfind.h"
 
#include "rail.h"
 
#include "debug.h"
 
#include "tunnel_map.h"
 
#include "variables.h"
 
#include "depot.h"
 

	
 
// remember which tiles we have already visited so we don't visit them again.
 
static bool TPFSetTileBit(TrackPathFinder *tpf, TileIndex tile, int dir)
 
{
 
	uint hash, val, offs;
 
	TrackPathFinderLink *link, *new_link;
 
	uint bits = 1 << dir;
 

	
 
	if (tpf->disable_tile_hash)
 
		return true;
 

	
 
	hash = PATHFIND_HASH_TILE(tile);
 

	
 
	val = tpf->hash_head[hash];
 

	
 
	if (val == 0) {
 
		/* unused hash entry, set the appropriate bit in it and return true
 
		 * to indicate that a bit was set. */
 
		tpf->hash_head[hash] = bits;
 
		tpf->hash_tile[hash] = tile;
 
		return true;
 
	} else if (!(val & 0x8000)) {
 
		/* single tile */
 

	
 
		if (tile == tpf->hash_tile[hash]) {
 
			/* found another bit for the same tile,
 
			 * check if this bit is already set, if so, return false */
 
			if (val & bits)
 
				return false;
 

	
 
			/* otherwise set the bit and return true to indicate that the bit
 
			 * was set */
 
			tpf->hash_head[hash] = val | bits;
 
			return true;
 
		} else {
 
			/* two tiles with the same hash, need to make a link */
 

	
 
			/* allocate a link. if out of links, handle this by returning
 
			 * that a tile was already visisted. */
 
			if (tpf->num_links_left == 0) {
 
				return false;
 
			}
 
			tpf->num_links_left--;
 
			link = tpf->new_link++;
 

	
 
			/* move the data that was previously in the hash_??? variables
 
			 * to the link struct, and let the hash variables point to the link */
 
			link->tile = tpf->hash_tile[hash];
 
			tpf->hash_tile[hash] = PATHFIND_GET_LINK_OFFS(tpf, link);
 

	
 
			link->flags = tpf->hash_head[hash];
 
			tpf->hash_head[hash] = 0xFFFF; /* multi link */
 

	
 
			link->next = 0xFFFF;
 
		}
 
	} else {
 
		/* a linked list of many tiles,
 
		 * find the one corresponding to the tile, if it exists.
 
		 * otherwise make a new link */
 

	
 
		offs = tpf->hash_tile[hash];
 
		do {
 
			link = PATHFIND_GET_LINK_PTR(tpf, offs);
 
			if (tile == link->tile) {
 
				/* found the tile in the link list,
 
				 * check if the bit was alrady set, if so return false to indicate that the
 
				 * bit was already set */
 
				if (link->flags & bits)
 
					return false;
 
				link->flags |= bits;
 
				return true;
 
			}
 
		} while ((offs=link->next) != 0xFFFF);
 
	}
 

	
 
	/* get here if we need to add a new link to link,
 
	 * first, allocate a new link, in the same way as before */
 
	if (tpf->num_links_left == 0) {
 
			return false;
 
	}
 
	tpf->num_links_left--;
 
	new_link = tpf->new_link++;
 

	
 
	/* then fill the link with the new info, and establish a ptr from the old
 
	 * link to the new one */
 
	new_link->tile = tile;
 
	new_link->flags = bits;
 
	new_link->next = 0xFFFF;
 

	
 
	link->next = PATHFIND_GET_LINK_OFFS(tpf, new_link);
 
	return true;
 
}
 

	
 
static const byte _bits_mask[4] = {
 
	0x19,
 
	0x16,
 
	0x25,
 
	0x2A,
 
};
 

	
 
static const byte _tpf_new_direction[14] = {
 
	0, 1, 0, 1, 2, 1,
 
	0, 0,
 
	2, 3, 3, 2, 3, 0,
 
};
 

	
 
static const byte _tpf_prev_direction[14] = {
 
	0, 1, 1, 0, 1, 2,
 
	0, 0,
 
	2, 3, 2, 3, 0, 3,
 
};
 

	
 

	
 
static const byte _otherdir_mask[4] = {
 
	0x10,
 
	0,
 
	0x5,
 
	0x2A,
 
};
 

	
 
static void TPFMode2(TrackPathFinder* tpf, TileIndex tile, DiagDirection direction)
 
{
 
	uint bits;
 
	int i;
 
	RememberData rd;
 

	
 
	assert(tpf->tracktype == TRANSPORT_WATER);
 

	
 
	// This addition will sometimes overflow by a single tile.
 
	// The use of TILE_MASK here makes sure that we still point at a valid
 
	// tile, and then this tile will be in the sentinel row/col, so GetTileTrackStatus will fail.
 
	tile = TILE_MASK(tile + TileOffsByDiagDir(direction));
 

	
 
	if (++tpf->rd.cur_length > 50)
 
		return;
 

	
 
	bits = GetTileTrackStatus(tile, tpf->tracktype);
 
	bits = (byte)((bits | (bits >> 8)) & _bits_mask[direction]);
 
	if (bits == 0)
 
		return;
 

	
 
	assert(TileX(tile) != MapMaxX() && TileY(tile) != MapMaxY());
 

	
 
	if ( (bits & (bits - 1)) == 0 ) {
 
		/* only one direction */
 
		i = 0;
 
		while (!(bits&1))
 
			i++, bits>>=1;
 

	
 
		rd = tpf->rd;
 
		goto continue_here;
 
	}
 
	/* several directions */
 
	i=0;
 
	do {
 
		if (!(bits & 1)) continue;
 
		rd = tpf->rd;
 

	
 
		// Change direction 4 times only
 
		if ((byte)i != tpf->rd.pft_var6) {
 
			if (++tpf->rd.depth > 4) {
 
				tpf->rd = rd;
 
				return;
 
			}
 
			tpf->rd.pft_var6 = (byte)i;
 
		}
 

	
 
continue_here:;
 
		tpf->the_dir = i + (HASBIT(_otherdir_mask[direction], i) ? 8 : 0);
 

	
 
		if (!tpf->enum_proc(tile, tpf->userdata, tpf->the_dir, tpf->rd.cur_length, NULL)) {
 
			TPFMode2(tpf, tile, _tpf_new_direction[tpf->the_dir]);
 
		}
 

	
 
		tpf->rd = rd;
 
	} while (++i, bits>>=1);
 

	
 
}
 

	
 

	
 
/* Returns the end tile and the length of a tunnel. The length does not
 
 * include the starting tile (entry), it does include the end tile (exit).
 
 */
 
FindLengthOfTunnelResult FindLengthOfTunnel(TileIndex tile, DiagDirection dir)
 
{
 
	TileIndexDiff delta = TileOffsByDiagDir(dir);
 
	uint z = GetTileZ(tile);
 
	FindLengthOfTunnelResult flotr;
 

	
 
	flotr.length = 0;
 

	
 
	dir = ReverseDiagDir(dir);
 
	do {
 
		flotr.length++;
 
		tile += delta;
 
	} while(
 
		!IsTunnelTile(tile) ||
 
		GetTunnelDirection(tile) != dir ||
 
		GetTileZ(tile) != z
 
	);
 

	
 
	flotr.tile = tile;
 
	return flotr;
 
}
 

	
 
static const uint16 _tpfmode1_and[4] = { 0x1009, 0x16, 0x520, 0x2A00 };
 

	
 
static uint SkipToEndOfTunnel(TrackPathFinder* tpf, TileIndex tile, DiagDirection direction)
 
{
 
	FindLengthOfTunnelResult flotr;
 
	TPFSetTileBit(tpf, tile, 14);
 
	flotr = FindLengthOfTunnel(tile, direction);
 
	tpf->rd.cur_length += flotr.length;
 
	TPFSetTileBit(tpf, flotr.tile, 14);
 
	return flotr.tile;
 
}
 

	
 
const byte _ffb_64[128] = {
 
 0,  0,  1,  0,  2,  0,  1,  0,
 
 3,  0,  1,  0,  2,  0,  1,  0,
 
 4,  0,  1,  0,  2,  0,  1,  0,
 
 3,  0,  1,  0,  2,  0,  1,  0,
 
 5,  0,  1,  0,  2,  0,  1,  0,
 
 3,  0,  1,  0,  2,  0,  1,  0,
 
 4,  0,  1,  0,  2,  0,  1,  0,
 
 3,  0,  1,  0,  2,  0,  1,  0,
 

	
 
 0,  0,  0,  2,  0,  4,  4,  6,
 
 0,  8,  8, 10,  8, 12, 12, 14,
 
 0, 16, 16, 18, 16, 20, 20, 22,
 
16, 24, 24, 26, 24, 28, 28, 30,
 
 0, 32, 32, 34, 32, 36, 36, 38,
 
32, 40, 40, 42, 40, 44, 44, 46,
 
32, 48, 48, 50, 48, 52, 52, 54,
 
48, 56, 56, 58, 56, 60, 60, 62,
 
};
 

	
 
static void TPFMode1(TrackPathFinder* tpf, TileIndex tile, DiagDirection direction)
 
{
 
	uint bits;
 
	int i;
 
	RememberData rd;
 
	TileIndex tile_org = tile;
 

	
 
	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
		if (IsTunnel(tile)) {
 
			if (GetTunnelDirection(tile) != direction ||
 
					GetTunnelTransportType(tile) != tpf->tracktype) {
 
				return;
 
			}
 
			tile = SkipToEndOfTunnel(tpf, tile, direction);
 
		} else {
 
			TileIndex tile_end;
 
			if (GetBridgeRampDirection(tile) != direction ||
 
					GetBridgeTransportType(tile) != tpf->tracktype) {
 
				return;
 
			}
 
			//fprintf(stderr, "%s: Planning over bridge\n", __func__);
 
			// TODO doesn't work - WHAT doesn't work?
 
			TPFSetTileBit(tpf, tile, 14);
 
			tile_end = GetOtherBridgeEnd(tile);
 
			tpf->rd.cur_length += DistanceManhattan(tile, tile_end);
 
			tile = tile_end;
 
			TPFSetTileBit(tpf, tile, 14);
 
		}
 
	}
 
	tile += TileOffsByDiagDir(direction);
 

	
 
	/* Check in case of rail if the owner is the same */
 
	if (tpf->tracktype == TRANSPORT_RAIL) {
 
		// don't enter train depot from the back
 
		if (IsTileDepotType(tile, TRANSPORT_RAIL) && GetRailDepotDirection(tile) == direction) return;
 

	
 
		if (IsTileType(tile_org, MP_RAILWAY) || IsTileType(tile_org, MP_STATION) || IsTileType(tile_org, MP_TUNNELBRIDGE))
 
			if (IsTileType(tile, MP_RAILWAY) || IsTileType(tile, MP_STATION) || IsTileType(tile, MP_TUNNELBRIDGE))
 
				if (GetTileOwner(tile_org) != GetTileOwner(tile)) return;
 
	}
 

	
 
	// check if the new tile can be entered from that direction
 
	if (tpf->tracktype == TRANSPORT_ROAD) {
 
		// road stops and depots now have a track (r4419)
 
		// don't enter road stop from the back
 
		if (IsRoadStopTile(tile) && ReverseDiagDir(GetRoadStopDir(tile)) != direction) return;
 
		// don't enter road depot from the back
 
		if (IsTileDepotType(tile, TRANSPORT_ROAD) && ReverseDiagDir(GetRoadDepotDirection(tile)) != direction) return;
 
	}
 

	
 
	/* Check if the new tile is a tunnel or bridge head and that the direction
 
	 * and transport type match */
 
	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
		if (IsTunnel(tile)) {
 
			if (GetTunnelDirection(tile) != direction ||
 
					GetTunnelTransportType(tile) != tpf->tracktype) {
 
				return;
 
			}
 
		} else if (IsBridge(tile)) {
 
			if (GetBridgeRampDirection(tile) != direction ||
 
					GetBridgeTransportType(tile) != tpf->tracktype) {
 
				return;
 
			}
 
		}
 
	}
 

	
 
	tpf->rd.cur_length++;
 

	
 
	bits = GetTileTrackStatus(tile, tpf->tracktype);
 

	
 
	if ((byte)bits != tpf->var2) {
 
		bits &= _tpfmode1_and[direction];
 
		bits = bits | (bits>>8);
 
	}
 
	bits &= 0xBF;
 

	
 
	if (bits != 0) {
 
		if (!tpf->disable_tile_hash || (tpf->rd.cur_length <= 64 && (KILL_FIRST_BIT(bits) == 0 || ++tpf->rd.depth <= 7))) {
 
			do {
 
				i = FIND_FIRST_BIT(bits);
 
				bits = KILL_FIRST_BIT(bits);
 

	
 
				tpf->the_dir = (_otherdir_mask[direction] & (byte)(1 << i)) ? (i+8) : i;
 
				rd = tpf->rd;
 

	
 
				if (TPFSetTileBit(tpf, tile, tpf->the_dir) &&
 
						!tpf->enum_proc(tile, tpf->userdata, tpf->the_dir, tpf->rd.cur_length, &tpf->rd.pft_var6) ) {
 
					TPFMode1(tpf, tile, _tpf_new_direction[tpf->the_dir]);
 
				}
 
				tpf->rd = rd;
 
			} while (bits != 0);
 
		}
 
	}
 

	
 
	/* the next is only used when signals are checked.
 
	 * seems to go in 2 directions simultaneously */
 

	
 
	/* if i can get rid of this, tail end recursion can be used to minimize
 
	 * stack space dramatically. */
 

	
 
	/* If we are doing signal setting, we must reverse at evere tile, so we
 
	 * iterate all the tracks in a signal block, even when a normal train would
 
	 * not reach it (for example, when two lines merge */
 
	if (tpf->hasbit_13)
 
		return;
 

	
 
	direction = ReverseDiagDir(direction);
 
	tile += TileOffsByDiagDir(direction);
 

	
 
	bits = GetTileTrackStatus(tile, tpf->tracktype);
 
	bits |= (bits >> 8);
 

	
 
	if ( (byte)bits != tpf->var2) {
 
		bits &= _bits_mask[direction];
 
	}
 

	
 
	bits &= 0xBF;
 
	if (bits == 0)
 
		return;
 

	
 
	do {
 
		i = FIND_FIRST_BIT(bits);
 
		bits = KILL_FIRST_BIT(bits);
 

	
 
		tpf->the_dir = (_otherdir_mask[direction] & (byte)(1 << i)) ? (i+8) : i;
 
		rd = tpf->rd;
 
		if (TPFSetTileBit(tpf, tile, tpf->the_dir) &&
 
				!tpf->enum_proc(tile, tpf->userdata, tpf->the_dir, tpf->rd.cur_length, &tpf->rd.pft_var6) ) {
 
			TPFMode1(tpf, tile, _tpf_new_direction[tpf->the_dir]);
 
		}
 
		tpf->rd = rd;
 
	} while (bits != 0);
 
}
 

	
 
void FollowTrack(TileIndex tile, uint16 flags, DiagDirection direction, TPFEnumProc *enum_proc, TPFAfterProc *after_proc, void *data)
 
{
 
	TrackPathFinder tpf;
 

	
 
	assert(direction < 4);
 

	
 
	/* initialize path finder variables */
 
	tpf.userdata = data;
 
	tpf.enum_proc = enum_proc;
 
	tpf.new_link = tpf.links;
 
	tpf.num_links_left = lengthof(tpf.links);
 

	
 
	tpf.rd.cur_length = 0;
 
	tpf.rd.depth = 0;
 
	tpf.rd.pft_var6 = 0;
 

	
 
	tpf.var2 = HASBIT(flags, 15) ? 0x43 : 0xFF; /* 0x8000 */
 

	
 
	tpf.disable_tile_hash = HASBIT(flags, 12);  /* 0x1000 */
 
	tpf.hasbit_13         = HASBIT(flags, 13);  /* 0x2000 */
 

	
 

	
 
	tpf.tracktype = (byte)flags;
 

	
 
	if (HASBIT(flags, 11)) {
 
		tpf.rd.pft_var6 = 0xFF;
 
		tpf.enum_proc(tile, data, 0, 0, 0);
 
		TPFMode2(&tpf, tile, direction);
 
	} else {
 
		/* clear the hash_heads */
 
		memset(tpf.hash_head, 0, sizeof(tpf.hash_head));
 
		TPFMode1(&tpf, tile, direction);
 
	}
 

	
 
	if (after_proc != NULL)
 
		after_proc(&tpf);
 
}
 

	
 
typedef struct {
 
	TileIndex tile;
 
	uint16 cur_length; // This is the current length to this tile.
 
	uint16 priority; // This is the current length + estimated length to the goal.
 
	byte track;
 
	byte depth;
 
	byte state;
 
	byte first_track;
 
} StackedItem;
 

	
 
static const byte _new_track[6][4] = {
 
{0,    0xff, 8,    0xff,},
 
{0xff, 1,    0xff, 9,},
 
{0xff, 2,    10,   0xff,},
 
{3,    0xff, 0xff, 11,},
 
{12,   4,    0xff, 0xff,},
 
{0xff, 0xff, 5,    13,},
 
};
 

	
 
typedef struct HashLink {
 
	TileIndex tile;
 
	uint16 typelength;
 
	uint16 next;
 
} HashLink;
 

	
 
typedef struct {
 
	NTPEnumProc *enum_proc;
 
	void *userdata;
 
	TileIndex dest;
 

	
 
	TransportType tracktype;
 
	RailTypeMask railtypes;
 
	uint maxlength;
 

	
 
	HashLink *new_link;
 
	uint num_links_left;
 

	
 
	uint nstack;
 
	StackedItem stack[256]; // priority queue of stacked items
 

	
 
	uint16 hash_head[0x400]; // hash heads. 0 means unused. 0xFFFC = length, 0x3 = dir
 
	TileIndex hash_tile[0x400]; // tiles. or links.
 

	
 
	HashLink links[0x400]; // hash links
 

	
 
} NewTrackPathFinder;
 
#define NTP_GET_LINK_OFFS(tpf, link) ((byte*)(link) - (byte*)tpf->links)
 
#define NTP_GET_LINK_PTR(tpf, link_offs) (HashLink*)((byte*)tpf->links + (link_offs))
 

	
 
#define ARR(i) tpf->stack[(i)-1]
 

	
 
// called after a new element was added in the queue at the last index.
 
// move it down to the proper position
 
static inline void HeapifyUp(NewTrackPathFinder *tpf)
 
{
 
	StackedItem si;
 
	int i = ++tpf->nstack;
 

	
 
	while (i != 1 && ARR(i).priority < ARR(i>>1).priority) {
 
		// the child element is larger than the parent item.
 
		// swap the child item and the parent item.
 
		si = ARR(i); ARR(i) = ARR(i>>1); ARR(i>>1) = si;
 
		i>>=1;
 
	}
 
}
 

	
 
// called after the element 0 was eaten. fill it with a new element
 
static inline void HeapifyDown(NewTrackPathFinder *tpf)
 
{
 
	StackedItem si;
 
	int i = 1, j;
 
	int n;
 

	
 
	assert(tpf->nstack > 0);
 
	n = --tpf->nstack;
 

	
 
	if (n == 0) return; // heap is empty so nothing to do?
 

	
 
	// copy the last item to index 0. we use it as base for heapify.
 
	ARR(1) = ARR(n+1);
 

	
 
	while ((j=i*2) <= n) {
 
		// figure out which is smaller of the children.
 
		if (j != n && ARR(j).priority > ARR(j+1).priority)
 
			j++; // right item is smaller
 

	
 
		assert(i <= n && j <= n);
 
		if (ARR(i).priority <= ARR(j).priority)
 
			break; // base elem smaller than smallest, done!
 

	
 
		// swap parent with the child
 
		si = ARR(i); ARR(i) = ARR(j); ARR(j) = si;
 
		i = j;
 
	}
 
}
 

	
 
// mark a tile as visited and store the length of the path.
 
// if we already had a better path to this tile, return false.
 
// otherwise return true.
 
static bool NtpVisit(NewTrackPathFinder* tpf, TileIndex tile, DiagDirection dir, uint length)
 
{
 
	uint hash,head;
 
	HashLink *link, *new_link;
 

	
 
	assert(length < 16384-1);
 

	
 
	hash = PATHFIND_HASH_TILE(tile);
 

	
 
	// never visited before?
 
	if ((head=tpf->hash_head[hash]) == 0) {
 
		tpf->hash_tile[hash] = tile;
 
		tpf->hash_head[hash] = dir | (length << 2);
 
		return true;
 
	}
 

	
 
	if (head != 0xffff) {
 
		if (tile == tpf->hash_tile[hash] && (head & 0x3) == dir) {
 

	
 
			// longer length
 
			if (length >= (head >> 2)) return false;
 

	
 
			tpf->hash_head[hash] = dir | (length << 2);
 
			return true;
 
		}
 
		// two tiles with the same hash, need to make a link
 
		// allocate a link. if out of links, handle this by returning
 
		// that a tile was already visisted.
 
		if (tpf->num_links_left == 0) {
 
			DEBUG(ntp, 1, "No links left");
 
			return false;
 
		}
 

	
 
		tpf->num_links_left--;
 
		link = tpf->new_link++;
 

	
 
		/* move the data that was previously in the hash_??? variables
 
		 * to the link struct, and let the hash variables point to the link */
 
		link->tile = tpf->hash_tile[hash];
 
		tpf->hash_tile[hash] = NTP_GET_LINK_OFFS(tpf, link);
 

	
 
		link->typelength = tpf->hash_head[hash];
 
		tpf->hash_head[hash] = 0xFFFF; /* multi link */
 
		link->next = 0xFFFF;
 
	} else {
 
		// a linked list of many tiles,
 
		// find the one corresponding to the tile, if it exists.
 
		// otherwise make a new link
 

	
 
		uint offs = tpf->hash_tile[hash];
 
		do {
 
			link = NTP_GET_LINK_PTR(tpf, offs);
 
			if (tile == link->tile && (link->typelength & 0x3U) == dir) {
 
				if (length >= (uint)(link->typelength >> 2)) return false;
 
				link->typelength = dir | (length << 2);
 
				return true;
 
			}
 
		} while ((offs = link->next) != 0xFFFF);
 
	}
 

	
 
	/* get here if we need to add a new link to link,
 
	 * first, allocate a new link, in the same way as before */
 
	if (tpf->num_links_left == 0) {
 
		DEBUG(ntp, 1, "No links left");
 
		return false;
 
	}
 
	tpf->num_links_left--;
 
	new_link = tpf->new_link++;
 

	
 
	/* then fill the link with the new info, and establish a ptr from the old
 
	 * link to the new one */
 
	new_link->tile = tile;
 
	new_link->typelength = dir | (length << 2);
 
	new_link->next = 0xFFFF;
 

	
 
	link->next = NTP_GET_LINK_OFFS(tpf, new_link);
 
	return true;
 
}
 

	
 
/**
 
 * Checks if the shortest path to the given tile/dir so far is still the given
 
 * length.
 
 * @return true if the length is still the same
 
 * @pre    The given tile/dir combination should be present in the hash, by a
 
 *         previous call to NtpVisit().
 
 */
 
static bool NtpCheck(NewTrackPathFinder *tpf, TileIndex tile, uint dir, uint length)
 
{
 
	uint hash,head,offs;
 
	HashLink *link;
 

	
 
	hash = PATHFIND_HASH_TILE(tile);
 
	head=tpf->hash_head[hash];
 
	assert(head);
 

	
 
	if (head != 0xffff) {
 
		assert( tpf->hash_tile[hash] == tile && (head & 3) == dir);
 
		assert( (head >> 2) <= length);
 
		return length == (head >> 2);
 
	}
 

	
 
	// else it's a linked list of many tiles
 
	offs = tpf->hash_tile[hash];
 
	for (;;) {
 
		link = NTP_GET_LINK_PTR(tpf, offs);
 
		if (tile == link->tile && (link->typelength & 0x3U) == dir) {
 
			assert((uint)(link->typelength >> 2) <= length);
 
			return length == (uint)(link->typelength >> 2);
 
		}
 
		offs = link->next;
 
		assert(offs != 0xffff);
 
	}
 
}
 

	
 

	
 
static const uint16 _is_upwards_slope[15] = {
 
	0, // no tileh
 
	(1 << TRACKDIR_X_SW) | (1 << TRACKDIR_Y_NW), // 1
 
	(1 << TRACKDIR_X_SW) | (1 << TRACKDIR_Y_SE), // 2
 
	(1 << TRACKDIR_X_SW), // 3
 
	(1 << TRACKDIR_X_NE) | (1 << TRACKDIR_Y_SE), // 4
 
	0, // 5
 
	(1 << TRACKDIR_Y_SE), // 6
 
	0, // 7
 
	(1 << TRACKDIR_X_NE) | (1 << TRACKDIR_Y_NW), // 8,
 
	(1 << TRACKDIR_Y_NW), // 9
 
	0, //10
 
	0, //11,
 
	(1 << TRACKDIR_X_NE), //12
 
	0, //13
 
	0, //14
 
};
 

	
 
static uint DistanceMoo(TileIndex t0, TileIndex t1)
 
{
 
	const uint dx = abs(TileX(t0) - TileX(t1));
 
	const uint dy = abs(TileY(t0) - TileY(t1));
 

	
 
	const uint straightTracks = 2 * min(dx, dy); /* The number of straight (not full length) tracks */
 
	/* OPTIMISATION:
 
	 * Original: diagTracks = max(dx, dy) - min(dx,dy);
 
	 * Proof:
 
	 * (dx-dy) - straightTracks  == (min + max) - straightTracks = min + // max - 2 * min = max - min */
 
	const uint diagTracks = dx + dy - straightTracks; /* The number of diagonal (full tile length) tracks. */
 

	
 
	return diagTracks*DIAG_FACTOR + straightTracks*STR_FACTOR;
 
}
 

	
 
// These has to be small cause the max length of a track
 
// is currently limited to 16384
 

	
 
static const byte _length_of_track[16] = {
 
	DIAG_FACTOR, DIAG_FACTOR, STR_FACTOR, STR_FACTOR, STR_FACTOR, STR_FACTOR, 0, 0,
 
	DIAG_FACTOR, DIAG_FACTOR, STR_FACTOR, STR_FACTOR, STR_FACTOR, STR_FACTOR, 0, 0
 
};
 

	
 
// new more optimized pathfinder for trains...
 
// Tile is the tile the train is at.
 
// direction is the tile the train is moving towards.
 

	
 
static void NTPEnum(NewTrackPathFinder* tpf, TileIndex tile, DiagDirection direction)
 
{
 
	TrackBits bits, allbits;
 
	uint track;
 
	TileIndex tile_org;
 
	StackedItem si;
 
	int estimation;
 

	
 

	
 

	
 
	// Need to have a special case for the start.
 
	// We shouldn't call the callback for the current tile.
 
	si.cur_length = 1; // Need to start at 1 cause 0 is a reserved value.
 
	si.depth = 0;
 
	si.state = 0;
 
	si.first_track = 0xFF;
 
	goto start_at;
 

	
 
	for (;;) {
 
		// Get the next item to search from from the priority queue
 
		do {
 
			if (tpf->nstack == 0)
 
				return; // nothing left? then we're done!
 
			si = tpf->stack[0];
 
			tile = si.tile;
 

	
 
			HeapifyDown(tpf);
 
			// Make sure we havn't already visited this tile.
 
		} while (!NtpCheck(tpf, tile, _tpf_prev_direction[si.track], si.cur_length));
 

	
 
		// Add the length of this track.
 
		si.cur_length += _length_of_track[si.track];
 

	
 
callback_and_continue:
 
		if (tpf->enum_proc(tile, tpf->userdata, si.first_track, si.cur_length))
 
			return;
 

	
 
		assert(si.track <= 13);
 
		direction = _tpf_new_direction[si.track];
 

	
 
start_at:
 
		// If the tile is the entry tile of a tunnel, and we're not going out of the tunnel,
 
		//   need to find the exit of the tunnel.
 
		if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
			if (IsTunnel(tile)) {
 
				if (GetTunnelDirection(tile) != ReverseDiagDir(direction)) {
 
					FindLengthOfTunnelResult flotr;
 

	
 
					/* We are not just driving out of the tunnel */
 
					if (GetTunnelDirection(tile) != direction ||
 
							GetTunnelTransportType(tile) != tpf->tracktype) {
 
						// We are not driving into the tunnel, or it is an invalid tunnel
 
						continue;
 
					}
 
					if (!HASBIT(tpf->railtypes, GetRailType(tile))) {
 
						bits = 0;
 
						break;
 
					}
 
					flotr = FindLengthOfTunnel(tile, direction);
 
					si.cur_length += flotr.length * DIAG_FACTOR;
 
					tile = flotr.tile;
 
					// tile now points to the exit tile of the tunnel
 
				}
 
			} else {
 
				TileIndex tile_end;
 
				if (GetBridgeRampDirection(tile) != ReverseDiagDir(direction)) {
 
					// We are not just leaving the bridge
 
					if (GetBridgeRampDirection(tile) != direction ||
 
							GetBridgeTransportType(tile) != tpf->tracktype) {
 
						// Not entering the bridge or not compatible
 
						continue;
 
					}
 
				}
 
				tile_end = GetOtherBridgeEnd(tile);
 
				si.cur_length += DistanceManhattan(tile, tile_end) * DIAG_FACTOR;
 
				tile = tile_end;
 
			}
 
		}
 

	
 
		// This is a special loop used to go through
 
		// a rail net and find the first intersection
 
		tile_org = tile;
 
		for (;;) {
 
			assert(direction <= 3);
 
			tile += TileOffsByDiagDir(direction);
 

	
 
			// too long search length? bail out.
 
			if (si.cur_length >= tpf->maxlength) {
 
				DEBUG(ntp, 1, "Cur_length too big");
 
				bits = 0;
 
				break;
 
			}
 

	
 
			// Not a regular rail tile?
 
			// Then we can't use the code below, but revert to more general code.
 
			if (!IsTileType(tile, MP_RAILWAY) || !IsPlainRailTile(tile)) {
 
				// We found a tile which is not a normal railway tile.
 
				// Determine which tracks that exist on this tile.
 
				bits = GetTileTrackStatus(tile, TRANSPORT_RAIL) & _tpfmode1_and[direction];
 
				bits = (bits | (bits >> 8)) & 0x3F;
 

	
 
				// Check that the tile contains exactly one track
 
				if (bits == 0 || KILL_FIRST_BIT(bits) != 0) break;
 

	
 
				if (!HASBIT(tpf->railtypes, IsTileType(tile, MP_STREET) ? GetRailTypeCrossing(tile) : GetRailType(tile))) {
 
					bits = 0;
 
					break;
 
				}
 

	
 
				///////////////////
 
				// If we reach here, the tile has exactly one track.
 
				//   tile - index to a tile that is not rail tile, but still straight (with optional signals)
 
				//   bits - bitmask of which track that exist on the tile (exactly one bit is set)
 
				//   direction - which direction are we moving in?
 
				///////////////////
 
				si.track = _new_track[FIND_FIRST_BIT(bits)][direction];
 
				si.cur_length += _length_of_track[si.track];
 
				goto callback_and_continue;
 
			}
 

	
 
			/* Regular rail tile, determine which tracks exist. */
 
			allbits = GetTrackBits(tile);
 
			/* Which tracks are reachable? */
 
			bits = allbits & DiagdirReachesTracks(direction);
 

	
 
			/* The tile has no reachable tracks => End of rail segment
 
			 * or Intersection => End of rail segment. We check this agains all the
 
			 * bits, not just reachable ones, to prevent infinite loops. */
 
			if (bits == 0 || TracksOverlap(allbits)) break;
 

	
 
			if (!HASBIT(tpf->railtypes, GetRailType(tile))) {
 
				bits = 0;
 
				break;
 
			}
 

	
 
			/* If we reach here, the tile has exactly one track, and this
 
			 track is reachable => Rail segment continues */
 

	
 
			track = _new_track[FIND_FIRST_BIT(bits)][direction];
 
			assert(track != 0xff);
 

	
 
			si.cur_length += _length_of_track[track];
 

	
 
			// Check if this rail is an upwards slope. If it is, then add a penalty.
 
			// Small optimization here.. if (track&7)>1 then it can't be a slope so we avoid calling GetTileSlope
 
			if ((track & 7) <= 1 && (_is_upwards_slope[GetTileSlope(tile, NULL)] & (1 << track)) ) {
 
				// upwards slope. add some penalty.
 
				si.cur_length += 4*DIAG_FACTOR;
 
			}
 

	
 
			// railway tile with signals..?
 
			if (HasSignals(tile)) {
 
				if (!HasSignalOnTrackdir(tile, track)) {
 
					// if one way signal not pointing towards us, stop going in this direction => End of rail segment.
 
					if (HasSignalOnTrackdir(tile, ReverseTrackdir(track))) {
 
						bits = 0;
 
						break;
 
					}
 
				} else if (GetSignalStateByTrackdir(tile, track) == SIGNAL_STATE_GREEN) {
 
					// green signal in our direction. either one way or two way.
 
					si.state |= 3;
 
				} else {
 
					// reached a red signal.
 
					if (HasSignalOnTrackdir(tile, ReverseTrackdir(track))) {
 
						// two way red signal. unless we passed another green signal on the way,
 
						// stop going in this direction => End of rail segment.
 
						// this is to prevent us from going into a full platform.
 
						if (!(si.state&1)) {
 
							bits = 0;
 
							break;
 
						}
 
					}
 
					if (!(si.state & 2)) {
 
						// Is this the first signal we see? And it's red... add penalty
 
						si.cur_length += 10*DIAG_FACTOR;
 
						si.state += 2; // remember that we added penalty.
 
						// Because we added a penalty, we can't just continue as usual.
 
						// Need to get out and let A* do it's job with
 
						// possibly finding an even shorter path.
 
						break;
 
					}
 
				}
 

	
 
				if (tpf->enum_proc(tile, tpf->userdata, si.first_track, si.cur_length))
 
					return; /* Don't process this tile any further */
 
			}
 

	
 
			// continue with the next track
 
			direction = _tpf_new_direction[track];
 

	
 
			// safety check if we're running around chasing our tail... (infinite loop)
 
			if (tile == tile_org) {
 
				bits = 0;
 
				break;
 
			}
 
		}
 

	
 
		// There are no tracks to choose between.
 
		// Stop searching in this direction
 
		if (bits == 0)
 
			continue;
 

	
 
		////////////////
 
		// We got multiple tracks to choose between (intersection).
 
		// Branch the search space into several branches.
 
		////////////////
 

	
 
		// Check if we've already visited this intersection.
 
		// If we've already visited it with a better length, then
 
		// there's no point in visiting it again.
 
		if (!NtpVisit(tpf, tile, direction, si.cur_length))
 
			continue;
 

	
 
		// Push all possible alternatives that we can reach from here
 
		// onto the priority heap.
 
		// 'bits' contains the tracks that we can choose between.
 

	
 
		// First compute the estimated distance to the target.
 
		// This is used to implement A*
 
		estimation = 0;
 
		if (tpf->dest != 0)
 
			estimation = DistanceMoo(tile, tpf->dest);
 

	
 
		si.depth++;
 
		if (si.depth == 0)
 
			continue; /* We overflowed our depth. No more searching in this direction. */
 
		si.tile = tile;
 
		do {
 
			si.track = _new_track[FIND_FIRST_BIT(bits)][direction];
 
			assert(si.track != 0xFF);
 
			si.priority = si.cur_length + estimation;
 

	
 
			// out of stack items, bail out?
 
			if (tpf->nstack >= lengthof(tpf->stack)) {
 
				DEBUG(ntp, 1, "Out of stack");
 
				break;
 
			}
 

	
 
			tpf->stack[tpf->nstack] = si;
 
			HeapifyUp(tpf);
 
		} while ((bits = KILL_FIRST_BIT(bits)) != 0);
 

	
 
		// If this is the first intersection, we need to fill the first_track member.
 
		// so the code outside knows which path is better.
 
		// also randomize the order in which we search through them.
 
		if (si.depth == 1) {
 
			assert(tpf->nstack == 1 || tpf->nstack == 2 || tpf->nstack == 3);
 
			if (tpf->nstack != 1) {
 
				uint32 r = Random();
 
				if (r&1) swap_byte(&tpf->stack[0].track, &tpf->stack[1].track);
 
				if (tpf->nstack != 2) {
 
					byte t = tpf->stack[2].track;
 
					if (r&2) swap_byte(&tpf->stack[0].track, &t);
 
					if (r&4) swap_byte(&tpf->stack[1].track, &t);
 
					tpf->stack[2].first_track = tpf->stack[2].track = t;
 
				}
 
				tpf->stack[0].first_track = tpf->stack[0].track;
 
				tpf->stack[1].first_track = tpf->stack[1].track;
 
			}
 
		}
 

	
 
		// Continue with the next from the queue...
 
	}
 
}
 

	
 

	
 
// new pathfinder for trains. better and faster.
 
void NewTrainPathfind(TileIndex tile, TileIndex dest, RailTypeMask railtypes, DiagDirection direction, NTPEnumProc* enum_proc, void* data)
 
{
 
	NewTrackPathFinder tpf;
 

	
 
	tpf.dest = dest;
 
	tpf.userdata = data;
 
	tpf.enum_proc = enum_proc;
 
	tpf.tracktype = TRANSPORT_RAIL;
 
	tpf.railtypes = railtypes;
 
	tpf.maxlength = min(_patches.pf_maxlength * 3, 10000);
 
	tpf.nstack = 0;
 
	tpf.new_link = tpf.links;
 
	tpf.num_links_left = lengthof(tpf.links);
 
	memset(tpf.hash_head, 0, sizeof(tpf.hash_head));
 

	
 
	NTPEnum(&tpf, tile, direction);
 
}
src/player_gui.c
Show inline comments
 
deleted file
src/player_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "player.h"
 
#include "command.h"
 
#include "vehicle.h"
 
#include "economy.h"
 
#include "network/network.h"
 
#include "variables.h"
 
#include "train.h"
 
#include "date.h"
 
#include "newgrf.h"
 
#include "network/network_data.h"
 
#include "network/network_client.h"
 

	
 
static void DoShowPlayerFinances(PlayerID player, bool show_small, bool show_stickied);
 

	
 
static void DrawPlayerEconomyStats(const Player *p, byte mode)
 
{
 
	int x,y,i,j,year;
 
	const int64 (*tbl)[13];
 
	int64 sum, cost;
 
	StringID str;
 

	
 
	if (!(mode & 1)) { // normal sized economics window (mode&1) is minimized status
 
		/* draw categories */
 
		DrawStringCenterUnderline(61, 15, STR_700F_EXPENDITURE_INCOME, 0);
 
		for (i = 0; i != 13; i++)
 
			DrawString(2, 27 + i*10, STR_7011_CONSTRUCTION + i, 0);
 
		DrawStringRightAligned(111, 27 + 10*13 + 2, STR_7020_TOTAL, 0);
 

	
 
		/* draw the price columns */
 
		year = _cur_year - 2;
 
		j = 3;
 
		x = 215;
 
		tbl = p->yearly_expenses + 2;
 
		do {
 
			if (year >= p->inaugurated_year) {
 
				SetDParam(0, year);
 
				DrawStringRightAlignedUnderline(x, 15, STR_7010, 0);
 
				sum = 0;
 
				for (i = 0; i != 13; i++) {
 
					/* draw one row in the price column */
 
					cost = (*tbl)[i];
 
					if (cost != 0) {
 
						sum += cost;
 

	
 
						str = STR_701E;
 
						if (cost < 0) { cost = -cost; str++; }
 
						SetDParam64(0, cost);
 
						DrawStringRightAligned(x, 27+i*10, str, 0);
 
					}
 
				}
 

	
 
				str = STR_701E;
 
				if (sum < 0) { sum = -sum; str++; }
 
				SetDParam64(0, sum);
 
				DrawStringRightAligned(x, 27 + 13*10 + 2, str, 0);
 

	
 
				GfxFillRect(x - 75, 27 + 10*13, x, 27 + 10*13, 215);
 
				x += 95;
 
			}
 
			year++;
 
			tbl--;
 
		} while (--j != 0);
 

	
 
		y = 171;
 

	
 
		// draw max loan aligned to loan below (y += 10)
 
		SetDParam64(0, (uint64)_economy.max_loan);
 
		DrawString(202, y+10, STR_MAX_LOAN, 0);
 
	} else {
 
		y = 15;
 
	}
 

	
 
	DrawString(2, y, STR_7026_BANK_BALANCE, 0);
 
	SetDParam64(0, p->money64);
 
	DrawStringRightAligned(182, y, STR_7028, 0);
 

	
 
	y += 10;
 

	
 
	DrawString(2, y, STR_7027_LOAN, 0);
 
	SetDParam64(0, p->current_loan);
 
	DrawStringRightAligned(182, y, STR_7028, 0);
 

	
 
	y += 12;
 

	
 
	GfxFillRect(182 - 75, y-2, 182, y-2, 215);
 

	
 
	SetDParam64(0, p->money64 - p->current_loan);
 
	DrawStringRightAligned(182, y, STR_7028, 0);
 
}
 

	
 
static const Widget _player_finances_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,               STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   379,     0,    13, STR_700E_FINANCES,      STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   380,   394,     0,    13, SPR_LARGE_SMALL_WINDOW, STR_7075_TOGGLE_LARGE_SMALL_WINDOW},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    14,   395,   406,     0,    13, 0x0,                    STR_STICKY_BUTTON},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   406,    14,   169, 0x0,                    STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   406,   170,   203, 0x0,                    STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,   202,   204,   215, STR_7029_BORROW,        STR_7035_INCREASE_SIZE_OF_LOAN},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   203,   406,   204,   215, STR_702A_REPAY,         STR_7036_REPAY_PART_OF_LOAN},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _other_player_finances_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,               STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   379,     0,    13, STR_700E_FINANCES,      STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   380,   394,     0,    13, SPR_LARGE_SMALL_WINDOW, STR_7075_TOGGLE_LARGE_SMALL_WINDOW},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    14,   395,   406,     0,    13, 0x0,                    STR_STICKY_BUTTON},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   406,    14,   169, 0x0,                    STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   406,   170,   203, 0x0,                    STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _other_player_finances_small_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,               STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   253,     0,    13, STR_700E_FINANCES,      STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   254,   267,     0,    13, SPR_LARGE_SMALL_WINDOW, STR_7075_TOGGLE_LARGE_SMALL_WINDOW},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    14,   268,   279,     0,    13, 0x0,                    STR_STICKY_BUTTON},
 
{      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,                    STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   279,    14,    47, 0x0,                    STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _player_finances_small_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,               STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   253,     0,    13, STR_700E_FINANCES,      STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,   254,   267,     0,    13, SPR_LARGE_SMALL_WINDOW, STR_7075_TOGGLE_LARGE_SMALL_WINDOW},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    14,   268,   279,     0,    13, 0x0,                    STR_STICKY_BUTTON},
 
{      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,                    STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   279,    14,    47, STR_NULL,               STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,   139,    48,    59, STR_7029_BORROW,        STR_7035_INCREASE_SIZE_OF_LOAN},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   140,   279,    48,    59, STR_702A_REPAY,         STR_7036_REPAY_PART_OF_LOAN},
 
{   WIDGETS_END},
 
};
 

	
 

	
 
static void PlayerFinancesWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		PlayerID player = w->window_number;
 
		const Player *p = GetPlayer(player);
 

	
 
		if (player == _local_player) {
 
			/* borrow/repay buttons only exist for local player */
 
			SetWindowWidgetDisabledState(w, 7, p->current_loan == 0);
 
		}
 

	
 
		SetDParam(0, p->name_1);
 
		SetDParam(1, p->name_2);
 
		SetDParam(2, GetPlayerNameString(player, 3));
 
		SetDParam(4, 10000);
 
		DrawWindowWidgets(w);
 

	
 
		DrawPlayerEconomyStats(p, (byte)WP(w,def_d).data_1);
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 2: {/* toggle size */
 
			byte mode = (byte)WP(w,def_d).data_1;
 
			bool stickied = !!(w->flags4 & WF_STICKY);
 
			PlayerID player = w->window_number;
 
			DeleteWindow(w);
 
			DoShowPlayerFinances(player, !HASBIT(mode, 0), stickied);
 
		} break;
 

	
 
		case 6: /* increase loan */
 
			DoCommandP(0, 0, _ctrl_pressed, NULL, CMD_INCREASE_LOAN | CMD_MSG(STR_702C_CAN_T_BORROW_ANY_MORE_MONEY));
 
			break;
 

	
 
		case 7: /* repay loan */
 
			DoCommandP(0, 0, _ctrl_pressed, NULL, CMD_DECREASE_LOAN | CMD_MSG(STR_702F_CAN_T_REPAY_LOAN));
 
			break;
 
		}
 
		break;
 
	}
 
}
 

	
 
static const WindowDesc _player_finances_desc = {
 
	WDP_AUTO, WDP_AUTO, 407, 216,
 
	WC_FINANCES,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
 
	_player_finances_widgets,
 
	PlayerFinancesWndProc
 
};
 

	
 
static const WindowDesc _player_finances_small_desc = {
 
	WDP_AUTO, WDP_AUTO, 280, 60,
 
	WC_FINANCES,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
 
	_player_finances_small_widgets,
 
	PlayerFinancesWndProc
 
};
 

	
 
static const WindowDesc _other_player_finances_desc = {
 
	WDP_AUTO, WDP_AUTO, 407, 204,
 
	WC_FINANCES,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
 
	_other_player_finances_widgets,
 
	PlayerFinancesWndProc
 
};
 

	
 
static const WindowDesc _other_player_finances_small_desc = {
 
	WDP_AUTO, WDP_AUTO, 280, 48,
 
	WC_FINANCES,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
 
	_other_player_finances_small_widgets,
 
	PlayerFinancesWndProc
 
};
 

	
 
static void DoShowPlayerFinances(PlayerID player, bool show_small, bool show_stickied)
 
{
 
	Window *w;
 
	int mode;
 

	
 
	static const WindowDesc * const desc_table[2 * 2] = {
 
		&_player_finances_desc, &_player_finances_small_desc,
 
		&_other_player_finances_desc, &_other_player_finances_small_desc,
 
	};
 

	
 
	if (!IsValidPlayer(player)) return;
 

	
 
	mode = (player != _local_player) * 2 + show_small;
 
	w = AllocateWindowDescFront(desc_table[mode], player);
 
	if (w != NULL) {
 
		w->caption_color = w->window_number;
 
		WP(w,def_d).data_1 = mode;
 
		if (show_stickied) w->flags4 |= WF_STICKY;
 
	}
 
}
 

	
 
void ShowPlayerFinances(PlayerID player)
 
{
 
	DoShowPlayerFinances(player, false, false);
 
}
 

	
 
/* List of colours for the livery window */
 
static const StringID _colour_dropdown[] = {
 
	STR_00D1_DARK_BLUE,
 
	STR_00D2_PALE_GREEN,
 
	STR_00D3_PINK,
 
	STR_00D4_YELLOW,
 
	STR_00D5_RED,
 
	STR_00D6_LIGHT_BLUE,
 
	STR_00D7_GREEN,
 
	STR_00D8_DARK_GREEN,
 
	STR_00D9_BLUE,
 
	STR_00DA_CREAM,
 
	STR_00DB_MAUVE,
 
	STR_00DC_PURPLE,
 
	STR_00DD_ORANGE,
 
	STR_00DE_BROWN,
 
	STR_00DF_GREY,
 
	STR_00E0_WHITE,
 
	INVALID_STRING_ID
 
};
 

	
 
/* Association of liveries to livery classes */
 
static const LiveryClass livery_class[LS_END] = {
 
	LC_OTHER,
 
	LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL, LC_RAIL,
 
	LC_ROAD, LC_ROAD,
 
	LC_SHIP, LC_SHIP,
 
	LC_AIRCRAFT, LC_AIRCRAFT, LC_AIRCRAFT,
 
};
 

	
 
/* Number of liveries in each class, used to determine the height of the livery window */
 
static const byte livery_height[] = {
 
	1,
 
	11,
 
	2,
 
	2,
 
	3,
 
};
 

	
 
typedef struct livery_d {
 
	uint32 sel;
 
	LiveryClass livery_class;
 
} livery_d;
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(livery_d));
 

	
 
static void ShowColourDropDownMenu(Window *w, uint32 widget)
 
{
 
	uint32 used_colours = 0;
 
	const Livery *livery;
 
	LiveryScheme scheme;
 

	
 
	/* Disallow other player colours for the primary colour */
 
	if (HASBIT(WP(w, livery_d).sel, LS_DEFAULT) && widget == 10) {
 
		const Player *p;
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active && p->index != _local_player) SETBIT(used_colours, p->player_color);
 
		}
 
	}
 

	
 
	/* Get the first selected livery to use as the default dropdown item */
 
	for (scheme = 0; scheme < LS_END; scheme++) {
 
		if (HASBIT(WP(w, livery_d).sel, scheme)) break;
 
	}
 
	if (scheme == LS_END) scheme = LS_DEFAULT;
 
	livery = &GetPlayer(w->window_number)->livery[scheme];
 

	
 
	ShowDropDownMenu(w, _colour_dropdown, widget == 10 ? livery->colour1 : livery->colour2, widget, used_colours, 0);
 
}
 

	
 
static void SelectPlayerLiveryWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_CREATE:
 
			LowerWindowWidget(w, WP(w, livery_d).livery_class + 2);
 
			if (!_have_2cc) {
 
				HideWindowWidget(w, 11);
 
				HideWindowWidget(w, 12);
 
			}
 
			break;
 

	
 
		case WE_PAINT: {
 
			const Player *p = GetPlayer(w->window_number);
 
			LiveryScheme scheme = LS_DEFAULT;
 
			int y = 51;
 

	
 
			/* Disable dropdown controls if no scheme is selected */
 
			SetWindowWidgetDisabledState(w,  9, (WP(w, livery_d).sel == 0));
 
			SetWindowWidgetDisabledState(w, 10, (WP(w, livery_d).sel == 0));
 
			SetWindowWidgetDisabledState(w, 11, (WP(w, livery_d).sel == 0));
 
			SetWindowWidgetDisabledState(w, 12, (WP(w, livery_d).sel == 0));
 

	
 
			if (!(WP(w, livery_d).sel == 0)) {
 
				for (scheme = 0; scheme < LS_END; scheme++) {
 
					if (HASBIT(WP(w, livery_d).sel, scheme)) break;
 
				}
 
				if (scheme == LS_END) scheme = LS_DEFAULT;
 
			}
 

	
 
			SetDParam(0, STR_00D1_DARK_BLUE + p->livery[scheme].colour1);
 
			SetDParam(1, STR_00D1_DARK_BLUE + p->livery[scheme].colour2);
 

	
 
			DrawWindowWidgets(w);
 

	
 
			for (scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
 
				if (livery_class[scheme] == WP(w, livery_d).livery_class) {
 
					bool sel = HASBIT(WP(w, livery_d).sel, scheme) != 0;
 

	
 
					if (scheme != LS_DEFAULT) {
 
						DrawSprite(p->livery[scheme].in_use ? SPR_BOX_CHECKED : SPR_BOX_EMPTY, 2, y);
 
					}
 

	
 
					DrawString(15, y, STR_LIVERY_DEFAULT + scheme, sel ? 0xC : 0x10);
 

	
 
					DrawSprite(SPR_SQUARE | GENERAL_SPRITE_COLOR(p->livery[scheme].colour1) | PALETTE_MODIFIER_COLOR, 152, y);
 
					DrawString(165, y, STR_00D1_DARK_BLUE + p->livery[scheme].colour1, sel ? 0xC : 2);
 

	
 
					if (_have_2cc) {
 
						DrawSprite(SPR_SQUARE | GENERAL_SPRITE_COLOR(p->livery[scheme].colour2) | PALETTE_MODIFIER_COLOR, 277, y);
 
						DrawString(290, y, STR_00D1_DARK_BLUE + p->livery[scheme].colour2, sel ? 0xC : 2);
 
					}
 

	
 
					y += 14;
 
				}
 
			}
 
			break;
 
		}
 

	
 
		case WE_CLICK: {
 
			switch (e->we.click.widget) {
 
				/* Livery Class buttons */
 
				case 2:
 
				case 3:
 
				case 4:
 
				case 5:
 
				case 6: {
 
					LiveryScheme scheme;
 

	
 
					RaiseWindowWidget(w, WP(w, livery_d).livery_class + 2);
 
					WP(w, livery_d).livery_class = e->we.click.widget - 2;
 
					WP(w, livery_d).sel = 0;
 
					LowerWindowWidget(w, WP(w, livery_d).livery_class + 2);
 

	
 
					/* Select the first item in the list */
 
					for (scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
 
						if (livery_class[scheme] == WP(w, livery_d).livery_class) {
 
							WP(w, livery_d).sel = 1 << scheme;
 
							break;
 
						}
 
					}
 
					w->height = 49 + livery_height[WP(w, livery_d).livery_class] * 14;
 
					w->widget[13].bottom = w->height - 1;
 
					w->widget[13].data = livery_height[WP(w, livery_d).livery_class] << 8 | 1;
 
					MarkWholeScreenDirty();
 
					break;
 
				}
 

	
 
				case 9:
 
				case 10: // First colour dropdown
 
					ShowColourDropDownMenu(w, 10);
 
					break;
 

	
 
				case 11:
 
				case 12: // Second colour dropdown
 
					ShowColourDropDownMenu(w, 12);
 
					break;
 

	
 
				case 13: {
 
					LiveryScheme scheme;
 
					LiveryScheme j = (e->we.click.pt.y - 48) / 14;
 

	
 
					for (scheme = 0; scheme <= j; scheme++) {
 
						if (livery_class[scheme] != WP(w, livery_d).livery_class) j++;
 
						if (scheme >= LS_END) return;
 
					}
 
					if (j >= LS_END) return;
 

	
 
					/* If clicking on the left edge, toggle using the livery */
 
					if (e->we.click.pt.x < 10) {
 
						DoCommandP(0, j | (2 << 8), !GetPlayer(w->window_number)->livery[j].in_use, NULL, CMD_SET_PLAYER_COLOR);
 
					}
 

	
 
					if (_ctrl_pressed) {
 
						TOGGLEBIT(WP(w, livery_d).sel, j);
 
					} else {
 
						WP(w, livery_d).sel = 1 << j;
 
					}
 
					SetWindowDirty(w);
 
					break;
 
				}
 
			}
 
			break;
 
		}
 

	
 
		case WE_DROPDOWN_SELECT: {
 
			LiveryScheme scheme;
 

	
 
			for (scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
 
				if (HASBIT(WP(w, livery_d).sel, scheme)) {
 
					DoCommandP(0, scheme | (e->we.dropdown.button == 10 ? 0 : 256), e->we.dropdown.index, NULL, CMD_SET_PLAYER_COLOR);
 
				}
 
			}
 
			break;
 
		}
 
	}
 
}
 

	
 
static const Widget _select_player_livery_2cc_widgets[] = {
 
{ WWT_CLOSEBOX, RESIZE_NONE, 14,   0,  10,   0,  13, STR_00C5,                  STR_018B_CLOSE_WINDOW },
 
{  WWT_CAPTION, RESIZE_NONE, 14,  11, 399,   0,  13, STR_7007_NEW_COLOR_SCHEME, STR_018C_WINDOW_TITLE_DRAG_THIS },
 
{   WWT_IMGBTN, RESIZE_NONE, 14,   0,  21,  14,  35, SPR_IMG_COMPANY_GENERAL,   STR_LIVERY_GENERAL_TIP },
 
{   WWT_IMGBTN, RESIZE_NONE, 14,  22,  43,  14,  35, SPR_IMG_TRAINLIST,         STR_LIVERY_TRAIN_TIP },
 
{   WWT_IMGBTN, RESIZE_NONE, 14,  44,  65,  14,  35, SPR_IMG_TRUCKLIST,         STR_LIVERY_ROADVEH_TIP },
 
{   WWT_IMGBTN, RESIZE_NONE, 14,  66,  87,  14,  35, SPR_IMG_SHIPLIST,          STR_LIVERY_SHIP_TIP },
 
{   WWT_IMGBTN, RESIZE_NONE, 14,  88, 109,  14,  35, SPR_IMG_AIRPLANESLIST,     STR_LIVERY_AIRCRAFT_TIP },
 
{    WWT_PANEL, RESIZE_NONE, 14, 110, 399,  14,  35, 0x0,                       STR_NULL },
 
{    WWT_PANEL, RESIZE_NONE, 14,   0, 149,  36,  47, 0x0,                       STR_NULL },
 
{  WWT_TEXTBTN, RESIZE_NONE, 14, 150, 262,  36,  47, STR_02BD,                  STR_LIVERY_PRIMARY_TIP },
 
{  WWT_TEXTBTN, RESIZE_NONE, 14, 263, 274,  36,  47, STR_0225,                  STR_LIVERY_PRIMARY_TIP },
 
{  WWT_TEXTBTN, RESIZE_NONE, 14, 275, 387,  36,  47, STR_02E1,                  STR_LIVERY_SECONDARY_TIP },
 
{  WWT_TEXTBTN, RESIZE_NONE, 14, 388, 399,  36,  47, STR_0225,                  STR_LIVERY_SECONDARY_TIP },
 
{   WWT_MATRIX, RESIZE_NONE, 14,   0, 399,  48,  48 + 1 * 14, (1 << 8) | 1,     STR_LIVERY_PANEL_TIP },
 
{ WIDGETS_END },
 
};
 

	
 
static const WindowDesc _select_player_livery_2cc_desc = {
 
	WDP_AUTO, WDP_AUTO, 400, 49 + 1 * 14,
 
	WC_PLAYER_COLOR, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_select_player_livery_2cc_widgets,
 
	SelectPlayerLiveryWndProc
 
};
 

	
 

	
 
static const Widget _select_player_livery_widgets[] = {
 
{ WWT_CLOSEBOX, RESIZE_NONE, 14,   0,  10,   0,  13, STR_00C5,                  STR_018B_CLOSE_WINDOW },
 
{  WWT_CAPTION, RESIZE_NONE, 14,  11, 274,   0,  13, STR_7007_NEW_COLOR_SCHEME, STR_018C_WINDOW_TITLE_DRAG_THIS },
 
{   WWT_IMGBTN, RESIZE_NONE, 14,   0,  21,  14,  35, SPR_IMG_COMPANY_GENERAL,   STR_LIVERY_GENERAL_TIP },
 
{   WWT_IMGBTN, RESIZE_NONE, 14,  22,  43,  14,  35, SPR_IMG_TRAINLIST,         STR_LIVERY_TRAIN_TIP },
 
{   WWT_IMGBTN, RESIZE_NONE, 14,  44,  65,  14,  35, SPR_IMG_TRUCKLIST,         STR_LIVERY_ROADVEH_TIP },
 
{   WWT_IMGBTN, RESIZE_NONE, 14,  66,  87,  14,  35, SPR_IMG_SHIPLIST,          STR_LIVERY_SHIP_TIP },
 
{   WWT_IMGBTN, RESIZE_NONE, 14,  88, 109,  14,  35, SPR_IMG_AIRPLANESLIST,     STR_LIVERY_AIRCRAFT_TIP },
 
{    WWT_PANEL, RESIZE_NONE, 14, 110, 274,  14,  35, 0x0,                       STR_NULL },
 
{    WWT_PANEL, RESIZE_NONE, 14,   0, 149,  36,  47, 0x0,                       STR_NULL },
 
{  WWT_TEXTBTN, RESIZE_NONE, 14, 150, 262,  36,  47, STR_02BD,                  STR_LIVERY_PRIMARY_TIP },
 
{  WWT_TEXTBTN, RESIZE_NONE, 14, 263, 274,  36,  47, STR_0225,                  STR_LIVERY_PRIMARY_TIP },
 
{  WWT_TEXTBTN, RESIZE_NONE, 14, 275, 275,  36,  47, STR_02E1,                  STR_LIVERY_SECONDARY_TIP },
 
{  WWT_TEXTBTN, RESIZE_NONE, 14, 275, 275,  36,  47, STR_0225,                  STR_LIVERY_SECONDARY_TIP },
 
{   WWT_MATRIX, RESIZE_NONE, 14,   0, 274,  48,  48 + 1 * 14, (1 << 8) | 1,     STR_LIVERY_PANEL_TIP },
 
{ WIDGETS_END },
 
};
 

	
 
static const WindowDesc _select_player_livery_desc = {
 
	WDP_AUTO, WDP_AUTO, 275, 49 + 1 * 14,
 
	WC_PLAYER_COLOR, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_select_player_livery_widgets,
 
	SelectPlayerLiveryWndProc
 
};
 

	
 
static void SelectPlayerFaceWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		Player *p;
 
		LowerWindowWidget(w, WP(w, facesel_d).gender + 5);
 
		DrawWindowWidgets(w);
 
		p = GetPlayer(w->window_number);
 
		DrawPlayerFace(WP(w,facesel_d).face, p->player_color, 2, 16);
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 3: DeleteWindow(w); break;
 
		case 4: /* ok click */
 
			DoCommandP(0, 0, WP(w,facesel_d).face, NULL, CMD_SET_PLAYER_FACE);
 
			DeleteWindow(w);
 
			break;
 
		case 5: /* male click */
 
		case 6: /* female click */
 
			RaiseWindowWidget(w, WP(w, facesel_d).gender + 5);
 
			WP(w, facesel_d).gender = e->we.click.widget - 5;
 
			LowerWindowWidget(w, WP(w, facesel_d).gender + 5);
 
			SetWindowDirty(w);
 
			break;
 
		case 7:
 
			WP(w,facesel_d).face = (WP(w,facesel_d).gender << 31) + GB(InteractiveRandom(), 0, 31);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
		break;
 
	}
 
}
 

	
 
static const Widget _select_player_face_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   189,     0,    13, STR_7043_FACE_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   189,    14,   136, 0x0,                     STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    94,   137,   148, STR_012E_CANCEL,         STR_7047_CANCEL_NEW_FACE_SELECTION},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,    95,   189,   137,   148, STR_012F_OK,             STR_7048_ACCEPT_NEW_FACE_SELECTION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    95,   187,    25,    36, STR_7044_MALE,           STR_7049_SELECT_MALE_FACES},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    95,   187,    37,    48, STR_7045_FEMALE,         STR_704A_SELECT_FEMALE_FACES},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,    95,   187,    79,    90, STR_7046_NEW_FACE,       STR_704B_GENERATE_RANDOM_NEW_FACE},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _select_player_face_desc = {
 
	WDP_AUTO, WDP_AUTO, 190, 149,
 
	WC_PLAYER_FACE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_select_player_face_widgets,
 
	SelectPlayerFaceWndProc
 
};
 

	
 
/* Names of the widgets. Keep them in the same order as in the widget array */
 
enum PlayerCompanyWindowWidgets {
 
	PCW_WIDGET_CLOSEBOX = 0,
 
	PCW_WIDGET_CAPTION,
 
	PCW_WIDGET_FACE,
 
	PCW_WIDGET_NEW_FACE,
 
	PCW_WIDGET_COLOR_SCHEME,
 
	PCW_WIDGET_PRESIDENT_NAME,
 
	PCW_WIDGET_COMPANY_NAME,
 
	PCW_WIDGET_BUILD_VIEW_HQ,
 
	PCW_WIDGET_RELOCATE_HQ,
 
	PCW_WIDGET_BUY_SHARE,
 
	PCW_WIDGET_SELL_SHARE,
 
	PCW_WIDGET_COMPANY_PASSWORD,
 
};
 

	
 
static const Widget _player_company_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                          STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   359,     0,    13, STR_7001,                          STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   359,    14,   157, 0x0,                               STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    89,   158,   169, STR_7004_NEW_FACE,                 STR_7030_SELECT_NEW_FACE_FOR_PRESIDENT},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,    90,   179,   158,   169, STR_7005_COLOR_SCHEME,             STR_7031_CHANGE_THE_COMPANY_VEHICLE},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   180,   269,   158,   169, STR_7009_PRESIDENT_NAME,           STR_7032_CHANGE_THE_PRESIDENT_S},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   270,   359,   158,   169, STR_7008_COMPANY_NAME,             STR_7033_CHANGE_THE_COMPANY_NAME},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   266,   355,    18,    29, STR_7072_VIEW_HQ,                  STR_7070_BUILD_COMPANY_HEADQUARTERS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   266,   355,    32,    43, STR_RELOCATE_HQ,                   STR_RELOCATE_COMPANY_HEADQUARTERS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,   179,   158,   169, STR_7077_BUY_25_SHARE_IN_COMPANY,  STR_7079_BUY_25_SHARE_IN_THIS_COMPANY},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   180,   359,   158,   169, STR_7078_SELL_25_SHARE_IN_COMPANY, STR_707A_SELL_25_SHARE_IN_THIS_COMPANY},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   266,   355,   138,   149, STR_COMPANY_PASSWORD,              STR_COMPANY_PASSWORD_TOOLTIP},
 
{   WIDGETS_END},
 
};
 

	
 
static void DrawPlayerVehiclesAmount(PlayerID player)
 
{
 
	const int x = 110;
 
	int y = 72;
 
	const Vehicle *v;
 
	uint train = 0;
 
	uint road  = 0;
 
	uint air   = 0;
 
	uint ship  = 0;
 

	
 
	DrawString(x, y, STR_7039_VEHICLES, 0);
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->owner == player) {
 
			switch (v->type) {
 
				case VEH_Train:    if (IsFrontEngine(v)) train++; break;
 
				case VEH_Road:     road++; break;
 
				case VEH_Aircraft: if (v->subtype <= 2) air++; break;
 
				case VEH_Ship:     ship++; break;
 
				default: break;
 
			}
 
		}
 
	}
 

	
 
	if (train+road+air+ship == 0) {
 
		DrawString(x+70, y, STR_7042_NONE, 0);
 
	} else {
 
		if (train != 0) {
 
			SetDParam(0, train);
 
			DrawString(x + 70, y, STR_TRAINS, 0);
 
			y += 10;
 
		}
 

	
 
		if (road != 0) {
 
			SetDParam(0, road);
 
			DrawString(x + 70, y, STR_ROAD_VEHICLES, 0);
 
			y += 10;
 
		}
 

	
 
		if (air != 0) {
 
			SetDParam(0, air);
 
			DrawString(x + 70, y, STR_AIRCRAFT, 0);
 
			y += 10;
 
		}
 

	
 
		if (ship != 0) {
 
			SetDParam(0, ship);
 
			DrawString(x + 70, y, STR_SHIPS, 0);
 
		}
 
	}
 
}
 

	
 
int GetAmountOwnedBy(const Player *p, PlayerID owner)
 
{
 
	return (p->share_owners[0] == owner) +
 
				 (p->share_owners[1] == owner) +
 
				 (p->share_owners[2] == owner) +
 
				 (p->share_owners[3] == owner);
 
}
 

	
 
static void DrawCompanyOwnerText(const Player *p)
 
{
 
	const Player *p2;
 
	int num = -1;
 

	
 
	FOR_ALL_PLAYERS(p2) {
 
		uint amt = GetAmountOwnedBy(p, p2->index);
 
		if (amt != 0) {
 
			num++;
 

	
 
			SetDParam(num * 3 + 0, amt * 25);
 
			SetDParam(num * 3 + 1, p2->name_1);
 
			SetDParam(num * 3 + 2, p2->name_2);
 

	
 
			if (num != 0) break;
 
		}
 
	}
 

	
 
	if (num >= 0) DrawString(120, 124, STR_707D_OWNED_BY + num, 0);
 
}
 

	
 
static void PlayerCompanyWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			const Player *p = GetPlayer(w->window_number);
 
			bool local = w->window_number == _local_player;
 

	
 
			SetWindowWidgetHiddenState(w, PCW_WIDGET_NEW_FACE,       !local);
 
			SetWindowWidgetHiddenState(w, PCW_WIDGET_COLOR_SCHEME,   !local);
 
			SetWindowWidgetHiddenState(w, PCW_WIDGET_PRESIDENT_NAME, !local);
 
			SetWindowWidgetHiddenState(w, PCW_WIDGET_COMPANY_NAME,   !local);
 
			w->widget[PCW_WIDGET_BUILD_VIEW_HQ].data = (local && p->location_of_house == 0) ? STR_706F_BUILD_HQ : STR_7072_VIEW_HQ;
 
			if (local && p->location_of_house != 0) w->widget[PCW_WIDGET_BUILD_VIEW_HQ].type = WWT_PUSHTXTBTN; //HQ is already built.
 
			SetWindowWidgetDisabledState(w, PCW_WIDGET_BUILD_VIEW_HQ, !local && p->location_of_house == 0);
 
			SetWindowWidgetHiddenState(w, PCW_WIDGET_RELOCATE_HQ,      !local || p->location_of_house == 0);
 
			SetWindowWidgetHiddenState(w, PCW_WIDGET_BUY_SHARE,        local);
 
			SetWindowWidgetHiddenState(w, PCW_WIDGET_SELL_SHARE,       local);
 
			SetWindowWidgetHiddenState(w, PCW_WIDGET_COMPANY_PASSWORD, !local || !_networking);
 

	
 
			if (!local) {
 
				if (_patches.allow_shares) { // Shares are allowed
 
					/* If all shares are owned by someone (none by nobody), disable buy button */
 
					SetWindowWidgetDisabledState(w, PCW_WIDGET_BUY_SHARE, GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 0 ||
 
							/* Only 25% left to buy. If the player is human, disable buying it up.. TODO issues! */
 
							(GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 1 && !p->is_ai) ||
 
							/* Spectators cannot do anything of course */
 
							_local_player == PLAYER_SPECTATOR);
 

	
 
					/* If the player doesn't own any shares, disable sell button */
 
					SetWindowWidgetDisabledState(w, PCW_WIDGET_SELL_SHARE, (GetAmountOwnedBy(p, _local_player) == 0) ||
 
							/* Spectators cannot do anything of course */
 
							_local_player == PLAYER_SPECTATOR);
 
				} else { // Shares are not allowed, disable buy/sell buttons
 
					DisableWindowWidget(w, PCW_WIDGET_BUY_SHARE);
 
					DisableWindowWidget(w, PCW_WIDGET_SELL_SHARE);
 
				}
 
			}
 

	
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			SetDParam(2, GetPlayerNameString((byte)w->window_number, 3));
 

	
 
			DrawWindowWidgets(w);
 

	
 
			SetDParam(0, p->inaugurated_year);
 
			DrawString(110, 25, STR_7038_INAUGURATED, 0);
 

	
 
			DrawPlayerVehiclesAmount(w->window_number);
 

	
 
			DrawString(110,48, STR_7006_COLOR_SCHEME, 0);
 
			// Draw company-colour bus
 
			DrawSprite(PLAYER_SPRITE_COLOR(p->index) + SPRITE_PALETTE(SPR_VEH_BUS_SW_VIEW), 215, 49);
 

	
 
			DrawPlayerFace(p->face, p->player_color, 2, 16);
 

	
 
			SetDParam(0, p->president_name_1);
 
			SetDParam(1, p->president_name_2);
 
			DrawStringMultiCenter(48, 141, STR_7037_PRESIDENT, 94);
 

	
 
			SetDParam64(0, CalculateCompanyValue(p));
 
			DrawString(110, 114, STR_7076_COMPANY_VALUE, 0);
 

	
 
			DrawCompanyOwnerText(p);
 

	
 
			break;
 
		}
 

	
 
		case WE_CLICK:
 
			switch (e->we.click.widget) {
 
				case PCW_WIDGET_NEW_FACE: {
 
					Window *wf = AllocateWindowDescFront(&_select_player_face_desc, w->window_number);
 
					if (wf != NULL) {
 
						wf->caption_color = w->window_number;
 
						WP(wf,facesel_d).face = GetPlayer(wf->window_number)->face;
 
						WP(wf,facesel_d).gender = 0;
 
					}
 
					break;
 
				}
 

	
 
				case PCW_WIDGET_COLOR_SCHEME: {
 
					Window *wf = AllocateWindowDescFront(_have_2cc ? &_select_player_livery_2cc_desc : &_select_player_livery_desc, w->window_number);
 
					if (wf != NULL) {
 
						wf->caption_color = wf->window_number;
 
						WP(wf,livery_d).livery_class = LC_OTHER;
 
						WP(wf,livery_d).sel = 1;
 
						LowerWindowWidget(wf, 2);
 
					}
 
					break;
 
				}
 

	
 
				case PCW_WIDGET_PRESIDENT_NAME: {
 
					const Player *p = GetPlayer(w->window_number);
 
					WP(w, def_d).byte_1 = 0;
 
					SetDParam(0, p->president_name_2);
 
					ShowQueryString(p->president_name_1, STR_700B_PRESIDENT_S_NAME, 31, 94, w, CS_ALPHANUMERAL);
 
					break;
 
				}
 

	
 
				case PCW_WIDGET_COMPANY_NAME: {
 
					Player *p = GetPlayer(w->window_number);
 
					WP(w,def_d).byte_1 = 1;
 
					SetDParam(0, p->name_2);
 
					ShowQueryString(p->name_1, STR_700A_COMPANY_NAME, 31, 150, w, CS_ALPHANUMERAL);
 
					break;
 
				}
 

	
 
				case PCW_WIDGET_BUILD_VIEW_HQ: {
 
					TileIndex tile = GetPlayer(w->window_number)->location_of_house;
 
					if (tile == 0) {
 
						if ((byte)w->window_number != _local_player)
 
							return;
 
						SetObjectToPlaceWnd(SPR_CURSOR_HQ, 1, w);
 
						SetTileSelectSize(2, 2);
 
						LowerWindowWidget(w, PCW_WIDGET_BUILD_VIEW_HQ);
 
						InvalidateWidget(w, PCW_WIDGET_BUILD_VIEW_HQ);
 
					} else {
 
						ScrollMainWindowToTile(tile);
 
					}
 
					break;
 
				}
 

	
 
				case PCW_WIDGET_RELOCATE_HQ:
 
					SetObjectToPlaceWnd(SPR_CURSOR_HQ, 1, w);
 
					SetTileSelectSize(2, 2);
 
					LowerWindowWidget(w, PCW_WIDGET_RELOCATE_HQ);
 
					InvalidateWidget(w, PCW_WIDGET_RELOCATE_HQ);
 
					break;
 

	
 
				case PCW_WIDGET_BUY_SHARE:
 
					DoCommandP(0, w->window_number, 0, NULL, CMD_BUY_SHARE_IN_COMPANY | CMD_MSG(STR_707B_CAN_T_BUY_25_SHARE_IN_THIS));
 
					break;
 

	
 
				case PCW_WIDGET_SELL_SHARE:
 
					DoCommandP(0, w->window_number, 0, NULL, CMD_SELL_SHARE_IN_COMPANY | CMD_MSG(STR_707C_CAN_T_SELL_25_SHARE_IN));
 
					break;
 

	
 
				#ifdef ENABLE_NETWORK
 
				case PCW_WIDGET_COMPANY_PASSWORD:
 
					if (w->window_number == _local_player) {
 
						WP(w,def_d).byte_1 = 2;
 
						ShowQueryString(BindCString(_network_player_info[_local_player].password),
 
							STR_SET_COMPANY_PASSWORD, sizeof(_network_player_info[_local_player].password), 250, w, CS_ALPHANUMERAL);
 
					}
 
					break;
 
				#endif /* ENABLE_NETWORK */
 
			}
 
			break;
 

	
 
		case WE_MOUSELOOP:
 
			/* redraw the window every now and then */
 
			if ((++w->vscroll.pos & 0x1F) == 0) SetWindowDirty(w);
 
			break;
 

	
 
		case WE_PLACE_OBJ:
 
			if (DoCommandP(e->we.place.tile, 0, 0, NULL, CMD_BUILD_COMPANY_HQ | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_7071_CAN_T_BUILD_COMPANY_HEADQUARTERS)))
 
				ResetObjectToPlace();
 
				w->widget[PCW_WIDGET_BUILD_VIEW_HQ].type = WWT_PUSHTXTBTN; // this button can now behave as a normal push button
 
				RaiseWindowButtons(w);
 
			break;
 

	
 
		case WE_ABORT_PLACE_OBJ:
 
			RaiseWindowButtons(w);
 
			break;
 

	
 
		case WE_DESTROY:
 
			DeleteWindowById(WC_PLAYER_FACE, w->window_number);
 
			break;
 

	
 
		case WE_ON_EDIT_TEXT: {
 
			char *b = e->we.edittext.str;
 

	
 
			// empty string is allowed for password
 
			if (*b == '\0' && WP(w,def_d).byte_1 != 2) return;
 

	
 
			_cmd_text = b;
 
			switch (WP(w,def_d).byte_1) {
 
				case 0: /* Change president name */
 
					DoCommandP(0, 0, 0, NULL, CMD_CHANGE_PRESIDENT_NAME | CMD_MSG(STR_700D_CAN_T_CHANGE_PRESIDENT));
 
					break;
 
				case 1: /* Change company name */
 
					DoCommandP(0, 0, 0, NULL, CMD_CHANGE_COMPANY_NAME | CMD_MSG(STR_700C_CAN_T_CHANGE_COMPANY_NAME));
 
					break;
 
				#ifdef ENABLE_NETWORK
 
				case 2: /* Change company password */
 
					if (*b == '\0') *b = '*'; // empty password is a '*' because of console argument
 
					NetworkChangeCompanyPassword(1, &b);
 
					break;
 
				#endif /* ENABLE_NETWORK */
 
			}
 
			break;
 
		}
 
	}
 
}
 

	
 

	
 
static const WindowDesc _player_company_desc = {
 
	WDP_AUTO, WDP_AUTO, 360, 170,
 
	WC_COMPANY, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_player_company_widgets,
 
	PlayerCompanyWndProc
 
};
 

	
 
void ShowPlayerCompany(PlayerID player)
 
{
 
	Window *w;
 

	
 
	if (!IsValidPlayer(player)) return;
 

	
 
	w = AllocateWindowDescFront(&_player_company_desc, player);
 
	if (w != NULL) w->caption_color = w->window_number;
 
}
 

	
 

	
 

	
 
static void BuyCompanyWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		Player *p = GetPlayer(w->window_number);
 
		SetDParam(0, p->name_1);
 
		SetDParam(1, p->name_2);
 
		DrawWindowWidgets(w);
 

	
 
		DrawPlayerFace(p->face, p->player_color, 2, 16);
 

	
 
		SetDParam(0, p->name_1);
 
		SetDParam(1, p->name_2);
 
		SetDParam(2, p->bankrupt_value);
 
		DrawStringMultiCenter(214, 65, STR_705B_WE_ARE_LOOKING_FOR_A_TRANSPORT, 238);
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 3:
 
			DeleteWindow(w);
 
			break;
 
		case 4: {
 
			DoCommandP(0, w->window_number, 0, NULL, CMD_BUY_COMPANY | CMD_MSG(STR_7060_CAN_T_BUY_COMPANY));
 
			break;
 
		}
 
		}
 
		break;
 
	}
 
}
 

	
 
static const Widget _buy_company_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     5,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     5,    11,   333,     0,    13, STR_00B3_MESSAGE_FROM, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     5,     0,   333,    14,   136, 0x0,                   STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,     5,   148,   207,   117,   128, STR_00C9_NO,           STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,     5,   218,   277,   117,   128, STR_00C8_YES,          STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _buy_company_desc = {
 
	153, 171, 334, 137,
 
	WC_BUY_COMPANY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_buy_company_widgets,
 
	BuyCompanyWndProc
 
};
 

	
 

	
 
void ShowBuyCompanyDialog(uint player)
 
{
 
	AllocateWindowDescFront(&_buy_company_desc, player);
 
}
 

	
 
/********** HIGHSCORE and ENDGAME windows */
 

	
 
/* Always draw a maximized window and within there the centered background */
 
static void SetupHighScoreEndWindow(Window *w, uint *x, uint *y)
 
{
 
	uint i;
 
	// resize window to "full-screen"
 
	w->width = _screen.width;
 
	w->height = _screen.height;
 
	w->widget[0].right = w->width - 1;
 
	w->widget[0].bottom = w->height - 1;
 

	
 
	DrawWindowWidgets(w);
 

	
 
	/* Center Highscore/Endscreen background */
 
	*x = max(0, (_screen.width  / 2) - (640 / 2));
 
	*y = max(0, (_screen.height / 2) - (480 / 2));
 
	for (i = 0; i < 10; i++) // the image is split into 10 50px high parts
 
		DrawSprite(WP(w, highscore_d).background_img + i, *x, *y + (i * 50));
 
}
 

	
 
extern StringID EndGameGetPerformanceTitleFromValue(uint value);
 

	
 
/* End game window shown at the end of the game */
 
static void EndGameWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const Player *p;
 
		uint x, y;
 

	
 
		SetupHighScoreEndWindow(w, &x, &y);
 

	
 
		if (!IsValidPlayer(_local_player)) break;
 

	
 
		p = GetPlayer(_local_player);
 
		/* We need to get performance from last year because the image is shown
 
		 * at the start of the new year when these things have already been copied */
 
		if (WP(w, highscore_d).background_img == SPR_TYCOON_IMG2_BEGIN) { // Tycoon of the century \o/
 
			SetDParam(0, p->president_name_1);
 
			SetDParam(1, p->president_name_2);
 
			SetDParam(2, p->name_1);
 
			SetDParam(3, p->name_2);
 
			SetDParam(4, EndGameGetPerformanceTitleFromValue(p->old_economy[0].performance_history));
 
			DrawStringMultiCenter(x + (640 / 2), y + 107, STR_021C_OF_ACHIEVES_STATUS, 640);
 
		} else {
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			SetDParam(2, EndGameGetPerformanceTitleFromValue(p->old_economy[0].performance_history));
 
			DrawStringMultiCenter(x + (640 / 2), y + 157, STR_021B_ACHIEVES_STATUS, 640);
 
		}
 
	} break;
 
	case WE_CLICK: /* Close the window (and show the highscore window) */
 
		DeleteWindow(w);
 
		break;
 
	case WE_DESTROY: /* Show the highscore window when this one is closed */
 
		if (!_networking) DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // unpause
 
		ShowHighscoreTable(w->window_number, WP(w, highscore_d).rank);
 
		break;
 
	}
 
}
 

	
 
static void HighScoreWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const HighScore *hs = _highscore_table[w->window_number];
 
		uint x, y;
 
		uint8 i;
 

	
 
		SetupHighScoreEndWindow(w, &x, &y);
 

	
 
		SetDParam(0, _patches.ending_year);
 
		SetDParam(1, w->window_number + STR_6801_EASY);
 
		DrawStringMultiCenter(x + (640 / 2), y + 62, !_networking ? STR_0211_TOP_COMPANIES_WHO_REACHED : STR_TOP_COMPANIES_NETWORK_GAME, 500);
 

	
 
		/* Draw Highscore peepz */
 
		for (i = 0; i < lengthof(_highscore_table[0]); i++) {
 
			SetDParam(0, i + 1);
 
			DrawString(x + 40, y + 140 + (i * 55), STR_0212, 0x10);
 

	
 
			if (hs[i].company[0] != '\0') {
 
				uint16 colour = (WP(w, highscore_d).rank == (int8)i) ? 0x3 : 0x10; // draw new highscore in red
 

	
 
				DoDrawString(hs[i].company, x + 71, y + 140 + (i * 55), colour);
 
				SetDParam(0, hs[i].title);
 
				SetDParam(1, hs[i].score);
 
				DrawString(x + 71, y + 160 + (i * 55), STR_HIGHSCORE_STATS, colour);
 
			}
 
		}
 
	} break;
 

	
 
	case WE_CLICK: /* Onclick to close window, and in destroy event handle the rest */
 
		DeleteWindow(w);
 
		break;
 

	
 
	case WE_DESTROY: /* Get back all the hidden windows */
 
		if (_game_mode != GM_MENU) ShowVitalWindows();
 

	
 
		if (!_networking) DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // unpause
 
		break;
 
	}
 
	}
 

	
 
static const Widget _highscore_widgets[] = {
 
{      WWT_PANEL, RESIZE_NONE, 16, 0, 640, 0, 480, 0x0, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _highscore_desc = {
 
	0, 0, 641, 481,
 
	WC_HIGHSCORE,0,
 
	0,
 
	_highscore_widgets,
 
	HighScoreWndProc
 
};
 

	
 
static const WindowDesc _endgame_desc = {
 
	0, 0, 641, 481,
 
	WC_ENDSCREEN,0,
 
	0,
 
	_highscore_widgets,
 
	EndGameWndProc
 
};
 

	
 
/* Show the highscore table for a given difficulty. When called from
 
 * endgame ranking is set to the top5 element that was newly added
 
 * and is thus highlighted */
 
void ShowHighscoreTable(int difficulty, int8 ranking)
 
{
 
	Window *w;
 

	
 
	// pause game to show the chart
 
	if (!_networking) DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
 

	
 
	/* Close all always on-top windows to get a clean screen */
 
	if (_game_mode != GM_MENU) HideVitalWindows();
 

	
 
	DeleteWindowByClass(WC_HIGHSCORE);
 
	w = AllocateWindowDesc(&_highscore_desc);
 

	
 
	if (w != NULL) {
 
		MarkWholeScreenDirty();
 
		w->window_number = difficulty; // show highscore chart for difficulty...
 
		WP(w, highscore_d).background_img = SPR_HIGHSCORE_CHART_BEGIN; // which background to show
 
		WP(w, highscore_d).rank = ranking;
 
	}
 
}
 

	
 
/* Show the endgame victory screen in 2050. Update the new highscore
 
 * if it was high enough */
 
void ShowEndGameChart(void)
 
{
 
	Window *w;
 

	
 
	/* Dedicated server doesn't need the highscore window */
 
	if (_network_dedicated) return;
 
	/* Pause in single-player to have a look at the highscore at your own leisure */
 
	if (!_networking) DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
 

	
 
	HideVitalWindows();
 
	DeleteWindowByClass(WC_ENDSCREEN);
 
	w = AllocateWindowDesc(&_endgame_desc);
 

	
 
	if (w != NULL) {
 
		MarkWholeScreenDirty();
 

	
 
		WP(w, highscore_d).background_img = SPR_TYCOON_IMG1_BEGIN;
 

	
 
		if (_local_player != PLAYER_SPECTATOR) {
 
			const Player *p = GetPlayer(_local_player);
 
			if (p->old_economy[0].performance_history == SCORE_MAX)
 
				WP(w, highscore_d).background_img = SPR_TYCOON_IMG2_BEGIN;
 
		}
 

	
 
		/* In a network game show the endscores of the custom difficulty 'network' which is the last one
 
		 * as well as generate a TOP5 of that game, and not an all-time top5. */
 
		if (_networking) {
 
			w->window_number = lengthof(_highscore_table) - 1;
 
			WP(w, highscore_d).rank = SaveHighScoreValueNetwork();
 
		} else {
 
			// in single player _local player is always valid
 
			const Player *p = GetPlayer(_local_player);
 
			w->window_number = _opt.diff_level;
 
			WP(w, highscore_d).rank = SaveHighScoreValue(p);
 
		}
 
	}
 
}
src/players.c
Show inline comments
 
deleted file
src/players.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/** @file players.c
 
 * @todo Cleanup the messy DrawPlayerFace function asap
 
 */
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "engine.h"
 
#include "functions.h"
 
#include "string.h"
 
#include "strings.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "map.h"
 
#include "player.h"
 
#include "town.h"
 
#include "vehicle.h"
 
#include "station.h"
 
#include "gfx.h"
 
#include "news.h"
 
#include "saveload.h"
 
#include "command.h"
 
#include "sound.h"
 
#include "network/network.h"
 
#include "variables.h"
 
#include "engine.h"
 
#include "ai/ai.h"
 
#include "date.h"
 
#include "window.h"
 

	
 
/**
 
 * Sets the local player and updates the patch settings that are set on a
 
 * per-company (player) basis to reflect the core's state in the GUI.
 
 * @param new_player the new player
 
 * @pre IsValidPlayer(new_player) || new_player == PLAYER_SPECTATOR || new_player == OWNER_NONE
 
 */
 
void SetLocalPlayer(PlayerID new_player)
 
{
 
	/* Player could also be PLAYER_SPECTATOR or OWNER_NONE */
 
	assert(IsValidPlayer(new_player) || new_player == PLAYER_SPECTATOR || new_player == OWNER_NONE);
 

	
 
	_local_player = new_player;
 

	
 
	/* Do not update the patches if we are in the intro GUI */
 
	if (IsValidPlayer(new_player) && _game_mode != GM_MENU) {
 
		const Player *p = GetPlayer(new_player);
 
		_patches.autorenew        = p->engine_renew;
 
		_patches.autorenew_months = p->engine_renew_months;
 
		_patches.autorenew_money  = p->engine_renew_money;
 
		InvalidateWindow(WC_GAME_OPTIONS, 0);
 
	}
 
}
 

	
 

	
 
uint16 GetDrawStringPlayerColor(PlayerID player)
 
{
 
	/* Get the color for DrawString-subroutines which matches the color
 
	 * of the player */
 
	if (!IsValidPlayer(player)) return _colour_gradient[COLOUR_WHITE][4] | IS_PALETTE_COLOR;
 
	return (_colour_gradient[_player_colors[player]][4]) | IS_PALETTE_COLOR;
 
}
 

	
 

	
 
static const SpriteID cheeks_table[4] = {
 
	0x325, 0x326,
 
	0x390, 0x3B0,
 
};
 

	
 
static const SpriteID mouth_table[3] = {
 
	0x34C, 0x34D, 0x34F
 
};
 

	
 
void DrawPlayerFace(uint32 face, int color, int x, int y)
 
{
 
	byte flag = 0;
 

	
 
	if ( (int32)face < 0)
 
		flag |= 1;
 
	if ((((((face >> 7) ^ face) >> 7) ^ face) & 0x8080000) == 0x8000000)
 
		flag |= 2;
 

	
 
	/* draw the gradient */
 
	DrawSprite(GENERAL_SPRITE_COLOR(color) + SPRITE_PALETTE(SPR_GRADIENT), x, y);
 

	
 
	/* draw the cheeks */
 
	DrawSprite(cheeks_table[flag&3], x, y);
 

	
 
	/* draw the chin */
 
	/* FIXME: real code uses -2 in zoomlevel 1 */
 
	{
 
		uint val = GB(face, 4, 2);
 
		if (!(flag & 2)) {
 
			DrawSprite(0x327 + (flag&1?0:val), x, y);
 
		} else {
 
			DrawSprite((flag&1?0x3B1:0x391) + (val>>1), x, y);
 
		}
 
	}
 
	/* draw the eyes */
 
	{
 
		uint val1 = GB(face,  6, 4);
 
		uint val2 = GB(face, 20, 3);
 
		uint32 high = 0x314 << PALETTE_SPRITE_START;
 

	
 
		if (val2 >= 6) {
 
			high = 0x30F << PALETTE_SPRITE_START;
 
			if (val2 != 6)
 
				high = 0x30D << PALETTE_SPRITE_START;
 
		}
 

	
 
		if (!(flag & 2)) {
 
			if (!(flag & 1)) {
 
				DrawSprite(high+((val1 * 12 >> 4) + SPRITE_PALETTE(0x32B)), x, y);
 
			} else {
 
				DrawSprite(high+(val1 + SPRITE_PALETTE(0x337)), x, y);
 
			}
 
		} else {
 
			if (!(flag & 1)) {
 
				DrawSprite(high+((val1 * 11 >> 4) + SPRITE_PALETTE(0x39A)), x, y);
 
			} else {
 
				DrawSprite(high+(val1 + SPRITE_PALETTE(0x3B8)), x, y);
 
			}
 
		}
 
	}
 

	
 
	/* draw the mouth */
 
	{
 
		uint val = GB(face, 10, 6);
 
		uint val2;
 

	
 
		if (!(flag&1)) {
 
			val2 = ((val&0xF) * 15 >> 4);
 

	
 
			if (val2 < 3) {
 
				DrawSprite((flag&2 ? 0x397 : 0x367) + val2, x, y);
 
				/* skip the rest */
 
				goto skip_mouth;
 
			}
 

	
 
			val2 -= 3;
 
			if (flag & 2) {
 
				if (val2 > 8) val2 = 0;
 
				val2 += 0x3A5 - 0x35B;
 
			}
 
			DrawSprite(val2 + 0x35B, x, y);
 
		} else if (!(flag&2)) {
 
			DrawSprite(((val&0xF) * 10 >> 4) + 0x351, x, y);
 
		} else {
 
			DrawSprite(((val&0xF) * 9 >> 4) + 0x3C8, x, y);
 
		}
 

	
 
		val >>= 3;
 

	
 
		if (!(flag&2)) {
 
			if (!(flag&1)) {
 
				DrawSprite(0x349 + val, x, y);
 
			} else {
 
				DrawSprite( mouth_table[(val*3>>3)], x, y);
 
			}
 
		} else {
 
			if (!(flag&1)) {
 
				DrawSprite(0x393 + (val&3), x, y);
 
			} else {
 
				DrawSprite(0x3B3 + (val*5>>3), x, y);
 
			}
 
		}
 

	
 
		skip_mouth:;
 
	}
 

	
 

	
 
	/* draw the hair */
 
	{
 
		uint val = GB(face, 16, 4);
 
		if (flag & 2) {
 
			if (flag & 1) {
 
				DrawSprite(0x3D9 + (val * 5 >> 4), x, y);
 
			} else {
 
				DrawSprite(0x3D4 + (val * 5 >> 4), x, y);
 
			}
 
		} else {
 
			if (flag & 1) {
 
				DrawSprite(0x38B + (val * 5 >> 4), x, y);
 
			} else {
 
				DrawSprite(0x382 + (val * 9 >> 4), x, y);
 
			}
 
		}
 
	}
 

	
 
	/* draw the tie */
 
	{
 
		uint val = GB(face, 20, 8);
 

	
 
		if (!(flag&1)) {
 
			DrawSprite(0x36B + (GB(val, 0, 2) * 3 >> 2), x, y);
 
			DrawSprite(0x36E + (GB(val, 2, 2) * 4 >> 2), x, y);
 
			DrawSprite(0x372 + (GB(val, 4, 4) * 6 >> 4), x, y);
 
		} else {
 
			DrawSprite(0x378 + (GB(val, 0, 2) * 3 >> 2), x, y);
 
			DrawSprite(0x37B + (GB(val, 2, 2) * 4 >> 2), x, y);
 

	
 
			val >>= 4;
 
			if (val < 3) DrawSprite((flag & 2 ? 0x3D1 : 0x37F) + val, x, y);
 
		}
 
	}
 

	
 
	/* draw the glasses */
 
	{
 
		uint val = GB(face, 28, 3);
 

	
 
		if (flag & 2) {
 
			if (val <= 1) DrawSprite(0x3AE + val, x, y);
 
		} else {
 
			if (val <= 1) DrawSprite(0x347 + val, x, y);
 
		}
 
	}
 
}
 

	
 
void InvalidatePlayerWindows(const Player *p)
 
{
 
	PlayerID pid = p->index;
 

	
 
	if (pid == _local_player) InvalidateWindow(WC_STATUS_BAR, 0);
 
	InvalidateWindow(WC_FINANCES, pid);
 
}
 

	
 
bool CheckPlayerHasMoney(int32 cost)
 
{
 
	if (cost > 0) {
 
		PlayerID pid = _current_player;
 
		if (IsValidPlayer(pid) && cost > GetPlayer(pid)->player_money) {
 
			SetDParam(0, cost);
 
			_error_message = STR_0003_NOT_ENOUGH_CASH_REQUIRES;
 
			return false;
 
		}
 
	}
 
	return true;
 
}
 

	
 
static void SubtractMoneyFromAnyPlayer(Player *p, int32 cost)
 
{
 
	p->money64 -= cost;
 
	UpdatePlayerMoney32(p);
 

	
 
	p->yearly_expenses[0][_yearly_expenses_type] += cost;
 

	
 
	if (HASBIT(1<<7|1<<8|1<<9|1<<10, _yearly_expenses_type)) {
 
		p->cur_economy.income -= cost;
 
	} else if (HASBIT(1<<2|1<<3|1<<4|1<<5|1<<6|1<<11, _yearly_expenses_type)) {
 
		p->cur_economy.expenses -= cost;
 
	}
 

	
 
	InvalidatePlayerWindows(p);
 
}
 

	
 
void SubtractMoneyFromPlayer(int32 cost)
 
{
 
	PlayerID pid = _current_player;
 

	
 
	if (IsValidPlayer(pid)) SubtractMoneyFromAnyPlayer(GetPlayer(pid), cost);
 
}
 

	
 
void SubtractMoneyFromPlayerFract(PlayerID player, int32 cost)
 
{
 
	Player *p = GetPlayer(player);
 
	byte m = p->player_money_fraction;
 

	
 
	p->player_money_fraction = m - (byte)cost;
 
	cost >>= 8;
 
	if (p->player_money_fraction > m) cost++;
 
	if (cost != 0) SubtractMoneyFromAnyPlayer(p, cost);
 
}
 

	
 
// the player_money field is kept as it is, but money64 contains the actual amount of money.
 
void UpdatePlayerMoney32(Player *p)
 
{
 
	if (p->money64 < -2000000000) {
 
		p->player_money = -2000000000;
 
	} else if (p->money64 > 2000000000) {
 
		p->player_money = 2000000000;
 
	} else {
 
		p->player_money = (int32)p->money64;
 
	}
 
}
 

	
 
void GetNameOfOwner(Owner owner, TileIndex tile)
 
{
 
	SetDParam(2, owner);
 

	
 
	if (owner != OWNER_TOWN) {
 
		if (!IsValidPlayer(owner)) {
 
			SetDParam(0, STR_0150_SOMEONE);
 
		} else {
 
			const Player* p = GetPlayer(owner);
 

	
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
		}
 
	} else {
 
		const Town* t = ClosestTownFromTile(tile, (uint)-1);
 

	
 
		SetDParam(0, STR_TOWN);
 
		SetDParam(1, t->index);
 
	}
 
}
 

	
 

	
 
bool CheckOwnership(PlayerID owner)
 
{
 
	assert(owner < OWNER_END);
 

	
 
	if (owner == _current_player) return true;
 
	_error_message = STR_013B_OWNED_BY;
 
	GetNameOfOwner(owner, 0);
 
	return false;
 
}
 

	
 
bool CheckTileOwnership(TileIndex tile)
 
{
 
	Owner owner = GetTileOwner(tile);
 

	
 
	assert(owner < OWNER_END);
 

	
 
	if (owner == _current_player) return true;
 
	_error_message = STR_013B_OWNED_BY;
 

	
 
	// no need to get the name of the owner unless we're the local player (saves some time)
 
	if (IsLocalPlayer()) GetNameOfOwner(owner, tile);
 
	return false;
 
}
 

	
 
static void GenerateCompanyName(Player *p)
 
{
 
	TileIndex tile;
 
	Town *t;
 
	StringID str;
 
	Player *pp;
 
	uint32 strp;
 
	char buffer[100];
 

	
 
	if (p->name_1 != STR_SV_UNNAMED) return;
 

	
 
	tile = p->last_build_coordinate;
 
	if (tile == 0) return;
 

	
 
	t = ClosestTownFromTile(tile, (uint)-1);
 

	
 
	if (IS_INT_INSIDE(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST+1)) {
 
		str = t->townnametype - SPECSTR_TOWNNAME_START + SPECSTR_PLAYERNAME_START;
 
		strp = t->townnameparts;
 

	
 
verify_name:;
 
		// No player must have this name already
 
		FOR_ALL_PLAYERS(pp) {
 
			if (pp->name_1 == str && pp->name_2 == strp) goto bad_town_name;
 
		}
 

	
 
		GetString(buffer, str, lastof(buffer));
 
		if (strlen(buffer) >= 32 || GetStringBoundingBox(buffer).width >= 150)
 
			goto bad_town_name;
 

	
 
set_name:;
 
		p->name_1 = str;
 
		p->name_2 = strp;
 

	
 
		MarkWholeScreenDirty();
 

	
 
		if (!IsHumanPlayer(p->index)) {
 
			SetDParam(0, t->index);
 
			AddNewsItem((StringID)(p->index | NB_BNEWCOMPANY), NEWS_FLAGS(NM_CALLBACK, NF_TILE, NT_COMPANY_INFO, DNC_BANKRUPCY), p->last_build_coordinate, 0);
 
		}
 
		return;
 
	}
 
bad_town_name:;
 

	
 
	if (p->president_name_1 == SPECSTR_PRESIDENT_NAME) {
 
		str = SPECSTR_ANDCO_NAME;
 
		strp = p->president_name_2;
 
		goto set_name;
 
	} else {
 
		str = SPECSTR_ANDCO_NAME;
 
		strp = Random();
 
		goto verify_name;
 
	}
 
}
 

	
 
#define COLOR_SWAP(i,j) do { byte t=colors[i];colors[i]=colors[j];colors[j]=t; } while(0)
 

	
 
static const byte _color_sort[16] = {2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 2, 2, 3, 1, 1, 1};
 
static const byte _color_similar_1[16] = {8, 6, 255, 12,  255, 0, 1, 1, 0, 13,  11,  10, 3,   9,  15, 14};
 
static const byte _color_similar_2[16] = {5, 7, 255, 255, 255, 8, 7, 6, 5, 12, 255, 255, 9, 255, 255, 255};
 

	
 
static byte GeneratePlayerColor(void)
 
{
 
	byte colors[16], pcolor, t2;
 
	int i,j,n;
 
	uint32 r;
 
	Player *p;
 

	
 
	// Initialize array
 
	for (i = 0; i != 16; i++) colors[i] = i;
 

	
 
	// And randomize it
 
	n = 100;
 
	do {
 
		r = Random();
 
		COLOR_SWAP(GB(r, 0, 4), GB(r, 4, 4));
 
	} while (--n);
 

	
 
	// Bubble sort it according to the values in table 1
 
	i = 16;
 
	do {
 
		for (j = 0; j != 15; j++) {
 
			if (_color_sort[colors[j]] < _color_sort[colors[j + 1]]) {
 
				COLOR_SWAP(j, j + 1);
 
			}
 
		}
 
	} while (--i);
 

	
 
	// Move the colors that look similar to each player's color to the side
 
	FOR_ALL_PLAYERS(p) if (p->is_active) {
 
		pcolor = p->player_color;
 
		for (i=0; i!=16; i++) if (colors[i] == pcolor) {
 
			colors[i] = 0xFF;
 

	
 
			t2 = _color_similar_1[pcolor];
 
			if (t2 == 0xFF) break;
 
			for (i=0; i!=15; i++) {
 
				if (colors[i] == t2) {
 
					do COLOR_SWAP(i,i+1); while (++i != 15);
 
					break;
 
				}
 
			}
 

	
 
			t2 = _color_similar_2[pcolor];
 
			if (t2 == 0xFF) break;
 
			for (i = 0; i != 15; i++) {
 
				if (colors[i] == t2) {
 
					do COLOR_SWAP(i, i + 1); while (++i != 15);
 
					break;
 
				}
 
			}
 
			break;
 
		}
 
	}
 

	
 
	// Return the first available color
 
	for (i = 0;; i++) {
 
		if (colors[i] != 0xFF) return colors[i];
 
	}
 
}
 

	
 
static void GeneratePresidentName(Player *p)
 
{
 
	Player *pp;
 
	char buffer[100], buffer2[40];
 

	
 
	for (;;) {
 
restart:;
 

	
 
		p->president_name_2 = Random();
 
		p->president_name_1 = SPECSTR_PRESIDENT_NAME;
 

	
 
		SetDParam(0, p->president_name_2);
 
		GetString(buffer, p->president_name_1, lastof(buffer));
 
		if (strlen(buffer) >= 32 || GetStringBoundingBox(buffer).width >= 94)
 
			continue;
 

	
 
		FOR_ALL_PLAYERS(pp) {
 
			if (pp->is_active && p != pp) {
 
				SetDParam(0, pp->president_name_2);
 
				GetString(buffer2, pp->president_name_1, lastof(buffer2));
 
				if (strcmp(buffer2, buffer) == 0)
 
					goto restart;
 
			}
 
		}
 
		return;
 
	}
 
}
 

	
 
static Player *AllocatePlayer(void)
 
{
 
	Player *p;
 
	// Find a free slot
 
	FOR_ALL_PLAYERS(p) {
 
		if (!p->is_active) {
 
			int i = p->index;
 
			memset(p, 0, sizeof(Player));
 
			p->index = i;
 
			return p;
 
		}
 
	}
 
	return NULL;
 
}
 

	
 
void ResetPlayerLivery(Player *p)
 
{
 
	LiveryScheme scheme;
 

	
 
	for (scheme = 0; scheme < LS_END; scheme++) {
 
		p->livery[scheme].in_use  = false;
 
		p->livery[scheme].colour1 = p->player_color;
 
		p->livery[scheme].colour2 = p->player_color;
 
	}
 
}
 

	
 
Player *DoStartupNewPlayer(bool is_ai)
 
{
 
	Player *p;
 

	
 
	p = AllocatePlayer();
 
	if (p == NULL) return NULL;
 

	
 
	// Make a color
 
	p->player_color = GeneratePlayerColor();
 
	ResetPlayerLivery(p);
 
	_player_colors[p->index] = p->player_color;
 
	p->name_1 = STR_SV_UNNAMED;
 
	p->is_active = true;
 

	
 
	p->money64 = p->player_money = p->current_loan = 100000;
 

	
 
	p->is_ai = is_ai;
 
	p->ai.state = 5; /* AIS_WANT_NEW_ROUTE */
 
	p->share_owners[0] = p->share_owners[1] = p->share_owners[2] = p->share_owners[3] = PLAYER_SPECTATOR;
 

	
 
	p->avail_railtypes = GetPlayerRailtypes(p->index);
 
	p->inaugurated_year = _cur_year;
 
	p->face = Random();
 

	
 
	/* Engine renewal settings */
 
	p->engine_renew_list = NULL;
 
	p->renew_keep_length = false;
 
	p->engine_renew = false;
 
	p->engine_renew_months = -6;
 
	p->engine_renew_money = 100000;
 

	
 
	GeneratePresidentName(p);
 

	
 
	InvalidateWindow(WC_GRAPH_LEGEND, 0);
 
	InvalidateWindow(WC_TOOLBAR_MENU, 0);
 
	InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
	if (is_ai && (!_networking || _network_server) && _ai.enabled)
 
		AI_StartNewAI(p->index);
 

	
 
	memset(p->num_engines, 0, sizeof(p->num_engines));
 

	
 
	return p;
 
}
 

	
 
void StartupPlayers(void)
 
{
 
	// The AI starts like in the setting with +2 month max
 
	_next_competitor_start = _opt.diff.competitor_start_time * 90 * DAY_TICKS + RandomRange(60 * DAY_TICKS) + 1;
 
}
 

	
 
static void MaybeStartNewPlayer(void)
 
{
 
	uint n;
 
	Player *p;
 

	
 
	// count number of competitors
 
	n = 0;
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active && p->is_ai) n++;
 
	}
 

	
 
	// when there's a lot of computers in game, the probability that a new one starts is lower
 
	if (n < (uint)_opt.diff.max_no_competitors &&
 
			n < (_network_server ?
 
				InteractiveRandomRange(_opt.diff.max_no_competitors + 2) :
 
				RandomRange(_opt.diff.max_no_competitors + 2)
 
			)) {
 
		/* Send a command to all clients to start up a new AI.
 
		 * Works fine for Multiplayer and Singleplayer */
 
		DoCommandP(0, 1, 0, NULL, CMD_PLAYER_CTRL);
 
	}
 

	
 
	// The next AI starts like the difficulty setting said, with +2 month max
 
	_next_competitor_start = _opt.diff.competitor_start_time * 90 * DAY_TICKS + 1;
 
	_next_competitor_start += _network_server ? InteractiveRandomRange(60 * DAY_TICKS) : RandomRange(60 * DAY_TICKS);
 
}
 

	
 
void InitializePlayers(void)
 
{
 
	uint i;
 

	
 
	memset(_players, 0, sizeof(_players));
 
	for (i = 0; i != MAX_PLAYERS; i++) _players[i].index = i;
 
	_cur_player_tick_index = 0;
 
}
 

	
 
void OnTick_Players(void)
 
{
 
	Player *p;
 

	
 
	if (_game_mode == GM_EDITOR) return;
 

	
 
	p = GetPlayer(_cur_player_tick_index);
 
	_cur_player_tick_index = (_cur_player_tick_index + 1) % MAX_PLAYERS;
 
	if (p->name_1 != 0) GenerateCompanyName(p);
 

	
 
	if (AI_AllowNewAI() && _game_mode != GM_MENU && !--_next_competitor_start)
 
		MaybeStartNewPlayer();
 
}
 

	
 
// index is the next parameter in _decode_parameters to set up
 
StringID GetPlayerNameString(PlayerID player, uint index)
 
{
 
	if (IsHumanPlayer(player) && IsValidPlayer(player)) {
 
		SetDParam(index, player+1);
 
		return STR_7002_PLAYER;
 
	}
 
	return STR_EMPTY;
 
}
 

	
 
extern void ShowPlayerFinances(int player);
 

	
 
void PlayersYearlyLoop(void)
 
{
 
	Player *p;
 

	
 
	// Copy statistics
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active) {
 
			memmove(&p->yearly_expenses[1], &p->yearly_expenses[0], sizeof(p->yearly_expenses) - sizeof(p->yearly_expenses[0]));
 
			memset(&p->yearly_expenses[0], 0, sizeof(p->yearly_expenses[0]));
 
			InvalidateWindow(WC_FINANCES, p->index);
 
		}
 
	}
 

	
 
	if (_patches.show_finances && _local_player != PLAYER_SPECTATOR) {
 
		ShowPlayerFinances(_local_player);
 
		p = GetPlayer(_local_player);
 
		if (p->num_valid_stat_ent > 5 && p->old_economy[0].performance_history < p->old_economy[4].performance_history) {
 
			SndPlayFx(SND_01_BAD_YEAR);
 
		} else {
 
			SndPlayFx(SND_00_GOOD_YEAR);
 
		}
 
	}
 
}
 

	
 
byte GetPlayerRailtypes(PlayerID p)
 
{
 
	byte rt = 0;
 
	EngineID i;
 

	
 
	for (i = 0; i != TOTAL_NUM_ENGINES; i++) {
 
		const Engine* e = GetEngine(i);
 
		const EngineInfo *ei = EngInfo(i);
 

	
 
		if (e->type == VEH_Train && HASBIT(ei->climates, _opt.landscape) &&
 
				(HASBIT(e->player_avail, p) || _date >= (e->intro_date + 365)) &&
 
				!(RailVehInfo(i)->flags & RVI_WAGON)) {
 
			assert(e->railtype < RAILTYPE_END);
 
			SETBIT(rt, e->railtype);
 
		}
 
	}
 

	
 
	return rt;
 
}
 

	
 
static void DeletePlayerStuff(PlayerID pi)
 
{
 
	Player *p;
 

	
 
	DeletePlayerWindows(pi);
 
	p = GetPlayer(pi);
 
	DeleteName(p->name_1);
 
	DeleteName(p->president_name_1);
 
	p->name_1 = 0;
 
	p->president_name_1 = 0;
 
}
 

	
 
/** Change engine renewal parameters
 
 * @param tile unused
 
 * @param p1 bits 0-3 command
 
 * - p1 = 0 - change auto renew bool
 
 * - p1 = 1 - change auto renew months
 
 * - p1 = 2 - change auto renew money
 
 * - p1 = 3 - change auto renew array
 
 * - p1 = 4 - change bool, months & money all together
 
 * - p1 = 5 - change renew_keep_length
 
 * @param p2 value to set
 
 * if p1 = 0, then:
 
 * - p2 = enable engine renewal
 
 * if p1 = 1, then:
 
 * - p2 = months left before engine expires to replace it
 
 * if p1 = 2, then
 
 * - p2 = minimum amount of money available
 
 * if p1 = 3, then:
 
 * - p2 bits  0-15 = old engine type
 
 * - p2 bits 16-31 = new engine type
 
 * if p1 = 4, then:
 
 * - p1 bit     15 = enable engine renewal
 
 * - p1 bits 16-31 = months left before engine expires to replace it
 
 * - p2 bits  0-31 = minimum amount of money available
 
 * if p1 = 5, then
 
 * - p2 = enable renew_keep_length
 
 */
 
int32 CmdSetAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Player *p;
 
	if (!IsValidPlayer(_current_player)) return CMD_ERROR;
 

	
 
	p = GetPlayer(_current_player);
 
	switch (GB(p1, 0, 3)) {
 
		case 0:
 
			if (p->engine_renew == (bool)GB(p2, 0, 1))
 
				return CMD_ERROR;
 

	
 
			if (flags & DC_EXEC) {
 
				p->engine_renew = (bool)GB(p2, 0, 1);
 
				if (IsLocalPlayer()) {
 
					_patches.autorenew = p->engine_renew;
 
					InvalidateWindow(WC_GAME_OPTIONS, 0);
 
				}
 
			}
 
			break;
 
		case 1:
 
			if (p->engine_renew_months == (int16)p2)
 
				return CMD_ERROR;
 

	
 
			if (flags & DC_EXEC) {
 
				p->engine_renew_months = (int16)p2;
 
				if (IsLocalPlayer()) {
 
					_patches.autorenew_months = p->engine_renew_months;
 
					InvalidateWindow(WC_GAME_OPTIONS, 0);
 
				}
 
			}
 
			break;
 
		case 2:
 
			if (p->engine_renew_money == (uint32)p2)
 
				return CMD_ERROR;
 

	
 
			if (flags & DC_EXEC) {
 
				p->engine_renew_money = (uint32)p2;
 
				if (IsLocalPlayer()) {
 
					_patches.autorenew_money = p->engine_renew_money;
 
					InvalidateWindow(WC_GAME_OPTIONS, 0);
 
				}
 
			}
 
			break;
 
		case 3: {
 
			EngineID old_engine_type = GB(p2, 0, 16);
 
			EngineID new_engine_type = GB(p2, 16, 16);
 
			int32 cost;
 

	
 
			if (new_engine_type != INVALID_ENGINE) {
 
				/* First we make sure that it's a valid type the user requested
 
				 * check that it's an engine that is in the engine array */
 
				if (!IsEngineIndex(new_engine_type))
 
					return CMD_ERROR;
 

	
 
				// check that the new vehicle type is the same as the original one
 
				if (GetEngine(old_engine_type)->type != GetEngine(new_engine_type)->type)
 
					return CMD_ERROR;
 

	
 
				// make sure that we do not replace a plane with a helicopter or vise versa
 
				if (GetEngine(new_engine_type)->type == VEH_Aircraft &&
 
						(AircraftVehInfo(old_engine_type)->subtype & AIR_CTOL) != (AircraftVehInfo(new_engine_type)->subtype & AIR_CTOL))
 
					return CMD_ERROR;
 

	
 
				// make sure that the player can actually buy the new engine
 
				if (!HASBIT(GetEngine(new_engine_type)->player_avail, _current_player))
 
					return CMD_ERROR;
 

	
 
				cost = AddEngineReplacementForPlayer(p, old_engine_type, new_engine_type, flags);
 
			} else {
 
				cost = RemoveEngineReplacementForPlayer(p, old_engine_type, flags);
 
			}
 

	
 
			if (IsLocalPlayer()) InvalidateWindow(WC_REPLACE_VEHICLE, GetEngine(old_engine_type)->type);
 

	
 
			return cost;
 
		}
 

	
 
		case 4:
 
			if (flags & DC_EXEC) {
 
				p->engine_renew = (bool)GB(p1, 15, 1);
 
				p->engine_renew_months = (int16)GB(p1, 16, 16);
 
				p->engine_renew_money = (uint32)p2;
 

	
 
				if (IsLocalPlayer()) {
 
					_patches.autorenew = p->engine_renew;
 
					_patches.autorenew_months = p->engine_renew_months;
 
					_patches.autorenew_money = p->engine_renew_money;
 
					InvalidateWindow(WC_GAME_OPTIONS, 0);
 
				}
 
			}
 
			break;
 
		case 5:
 
			if (p->renew_keep_length == (bool)GB(p2, 0, 1))
 
				return CMD_ERROR;
 

	
 
			if (flags & DC_EXEC) {
 
				p->renew_keep_length = (bool)GB(p2, 0, 1);
 
				if (IsLocalPlayer()) {
 
					InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train);
 
				}
 
			}
 
		break;
 

	
 
	}
 
	return 0;
 
}
 

	
 
/** Control the players: add, delete, etc.
 
 * @param tile unused
 
 * @param p1 various functionality
 
 * - p1 = 0 - create a new player, Which player (network) it will be is in p2
 
 * - p1 = 1 - create a new AI player
 
 * - p1 = 2 - delete a player. Player is identified by p2
 
 * - p1 = 3 - merge two companies together. Player to merge #1 with player #2. Identified by p2
 
 * @param p2 various functionality, dictated by p1
 
 * - p1 = 0 - ClientID of the newly created player
 
 * - p1 = 2 - PlayerID of the that is getting deleted
 
 * - p1 = 3 - #1 p2 = (bit  0-15) - player to merge (p2 & 0xFFFF)
 
 *          - #2 p2 = (bit 16-31) - player to be merged into ((p2>>16)&0xFFFF)
 
 * @todo In the case of p1=0, create new player, the clientID of the new player is in parameter
 
 * p2. This parameter is passed in at function DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND)
 
 * on the server itself. First of all this is unbelievably ugly; second of all, well,
 
 * it IS ugly! <b>Someone fix this up :)</b> So where to fix?@n
 
 * @arg - network_server.c:838 DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND)@n
 
 * @arg - network_client.c:536 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP) from where the map has been received
 
 */
 
int32 CmdPlayerCtrl(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (flags & DC_EXEC) _current_player = OWNER_NONE;
 

	
 
	switch (p1) {
 
	case 0: { /* Create a new player */
 
		/* Joining Client:
 
		 * _local_player: PLAYER_SPECTATOR
 
		 * _network_playas/cid = requested company/player
 
		 *
 
		 * Other client(s)/server:
 
		 * _local_player/_network_playas: what they play as
 
		 * cid = requested company/player of joining client */
 
		Player *p;
 
		uint16 cid = p2; // ClientID
 

	
 
		/* This command is only executed in a multiplayer game */
 
		if (!_networking) return CMD_ERROR;
 

	
 
		/* Has the network client a correct ClientID? */
 
		if (!(flags & DC_EXEC)) return 0;
 
#ifdef ENABLE_NETWORK
 
		if (cid >= MAX_CLIENT_INFO) return 0;
 
#endif /* ENABLE_NETWORK */
 

	
 
		/* Delete multiplayer progress bar */
 
		DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
		p = DoStartupNewPlayer(false);
 

	
 
		/* A new player could not be created, revert to being a spectator */
 
		if (p == NULL) {
 
#ifdef ENABLE_NETWORK
 
			if (_network_server) {
 
				NetworkClientInfo *ci = &_network_client_info[cid];
 
				ci->client_playas = PLAYER_SPECTATOR;
 
				NetworkUpdateClientInfo(ci->client_index);
 
			} else
 
#endif /* ENABLE_NETWORK */
 
			{
 
				_network_playas = PLAYER_SPECTATOR;
 
				SetLocalPlayer(PLAYER_SPECTATOR);
 
			}
 
			break;
 
		}
 

	
 
		/* This is the joining client who wants a new company */
 
		if (_local_player != _network_playas) {
 
			assert(_local_player == PLAYER_SPECTATOR && _network_playas == p->index);
 
			SetLocalPlayer(p->index);
 
			MarkWholeScreenDirty();
 
		}
 

	
 
		/* Now that we have a new player, broadcast its autorenew settings to
 
		 * all clients so everything is in sync */
 
		DoCommand(0,
 
			(_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4,
 
			_patches.autorenew_money,
 
			DC_EXEC,
 
			CMD_SET_AUTOREPLACE
 
		);
 

	
 
#ifdef ENABLE_NETWORK
 
		if (_network_server) {
 
			/* XXX - UGLY! p2 (pid) is mis-used to fetch the client-id, done at
 
			 * server-side in network_server.c:838, function
 
			 * DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) */
 
			NetworkClientInfo *ci = &_network_client_info[cid];
 
			ci->client_playas = p->index;
 
			NetworkUpdateClientInfo(ci->client_index);
 

	
 
			if (IsValidPlayer(ci->client_playas)) {
 
				PlayerID player_backup = _local_player;
 
				_network_player_info[p->index].months_empty = 0;
 

	
 
				/* XXX - When a client joins, we automatically set its name to the
 
				 * player's name (for some reason). As it stands now only the server
 
				 * knows the client's name, so it needs to send out a "broadcast" to
 
				 * do this. To achieve this we send a network command. However, it
 
				 * uses _local_player to execute the command as.  To prevent abuse
 
				 * (eg. only yourself can change your name/company), we 'cheat' by
 
				 * impersonation _local_player as the server. Not the best solution;
 
				 * but it works.
 
				 * TODO: Perhaps this could be improved by when the client is ready
 
				 * with joining to let it send itself the command, and not the server?
 
				 * For example in network_client.c:534? */
 
				_cmd_text = ci->client_name;
 
				_local_player = ci->client_playas;
 
				NetworkSend_Command(0, 0, 0, CMD_CHANGE_PRESIDENT_NAME, NULL);
 
				_local_player = player_backup;
 
			}
 
		}
 
#endif /* ENABLE_NETWORK */
 
	} break;
 

	
 
	case 1: /* Make a new AI player */
 
		if (!(flags & DC_EXEC)) return 0;
 

	
 
		DoStartupNewPlayer(true);
 
		break;
 

	
 
	case 2: { /* Delete a player */
 
		Player *p;
 

	
 
		if (!IsValidPlayer(p2)) return CMD_ERROR;
 

	
 
		if (!(flags & DC_EXEC)) return 0;
 

	
 
		p = GetPlayer(p2);
 

	
 
		/* Only allow removal of HUMAN companies */
 
		if (IsHumanPlayer(p->index)) {
 
			/* Delete any open window of the company */
 
			DeletePlayerWindows(p->index);
 

	
 
			/* Show the bankrupt news */
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			AddNewsItem( (StringID)(p->index | NB_BBANKRUPT), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 

	
 
			/* Remove the company */
 
			ChangeOwnershipOfPlayerItems(p->index, PLAYER_SPECTATOR);
 
			p->is_active = false;
 
		}
 
		RemoveAllEngineReplacementForPlayer(p);
 

	
 
	} break;
 

	
 
	case 3: { /* Merge a company (#1) into another company (#2), elimination company #1 */
 
		PlayerID pid_old = GB(p2,  0, 16);
 
		PlayerID pid_new = GB(p2, 16, 16);
 

	
 
		if (!IsValidPlayer(pid_old) || !IsValidPlayer(pid_new)) return CMD_ERROR;
 

	
 
		if (!(flags & DC_EXEC)) return CMD_ERROR;
 

	
 
		ChangeOwnershipOfPlayerItems(pid_old, pid_new);
 
		DeletePlayerStuff(pid_old);
 
	} break;
 

	
 
	default: return CMD_ERROR;
 
	}
 

	
 
	return 0;
 
}
 

	
 
static const StringID _endgame_perf_titles[] = {
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
	STR_0214_ENTREPRENEUR,
 
	STR_0214_ENTREPRENEUR,
 
	STR_0215_INDUSTRIALIST,
 
	STR_0215_INDUSTRIALIST,
 
	STR_0216_CAPITALIST,
 
	STR_0216_CAPITALIST,
 
	STR_0217_MAGNATE,
 
	STR_0217_MAGNATE,
 
	STR_0218_MOGUL,
 
	STR_0218_MOGUL,
 
	STR_0219_TYCOON_OF_THE_CENTURY
 
};
 

	
 
StringID EndGameGetPerformanceTitleFromValue(uint value)
 
{
 
	value = minu(value / 64, lengthof(_endgame_perf_titles) - 1);
 

	
 
	return _endgame_perf_titles[value];
 
}
 

	
 
/* Return true if any cheat has been used, false otherwise */
 
static bool CheatHasBeenUsed(void)
 
{
 
	const Cheat* cht = (Cheat*)&_cheats;
 
	const Cheat* cht_last = &cht[sizeof(_cheats) / sizeof(Cheat)];
 

	
 
	for (; cht != cht_last; cht++) {
 
		if (cht->been_used) return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
/* Save the highscore for the player */
 
int8 SaveHighScoreValue(const Player *p)
 
{
 
	HighScore *hs = _highscore_table[_opt.diff_level];
 
	uint i;
 
	uint16 score = p->old_economy[0].performance_history;
 

	
 
	/* Exclude cheaters from the honour of being in the highscore table */
 
	if (CheatHasBeenUsed()) return -1;
 

	
 
	for (i = 0; i < lengthof(_highscore_table[0]); i++) {
 
		/* You are in the TOP5. Move all values one down and save us there */
 
		if (hs[i].score <= score) {
 
			// move all elements one down starting from the replaced one
 
			memmove(&hs[i + 1], &hs[i], sizeof(HighScore) * (lengthof(_highscore_table[0]) - i - 1));
 
			SetDParam(0, p->president_name_1);
 
			SetDParam(1, p->president_name_2);
 
			SetDParam(2, p->name_1);
 
			SetDParam(3, p->name_2);
 
			GetString(hs[i].company, STR_HIGHSCORE_NAME, lastof(hs[i].company)); // get manager/company name string
 
			hs[i].score = score;
 
			hs[i].title = EndGameGetPerformanceTitleFromValue(score);
 
			return i;
 
		}
 
	}
 

	
 
	return -1; // too bad; we did not make it into the top5
 
}
 

	
 
/* Sort all players given their performance */
 
static int CDECL HighScoreSorter(const void *a, const void *b)
 
{
 
	const Player *pa = *(const Player* const*)a;
 
	const Player *pb = *(const Player* const*)b;
 

	
 
	return pb->old_economy[0].performance_history - pa->old_economy[0].performance_history;
 
}
 

	
 
/* Save the highscores in a network game when it has ended */
 
#define LAST_HS_ITEM lengthof(_highscore_table) - 1
 
int8 SaveHighScoreValueNetwork(void)
 
{
 
	const Player* p;
 
	const Player* pl[MAX_PLAYERS];
 
	size_t count = 0;
 
	int8 player = -1;
 

	
 
	/* Sort all active players with the highest score first */
 
	FOR_ALL_PLAYERS(p) if (p->is_active) pl[count++] = p;
 
	qsort((Player*)pl, count, sizeof(pl[0]), HighScoreSorter);
 

	
 
	{
 
		uint i;
 

	
 
		memset(_highscore_table[LAST_HS_ITEM], 0, sizeof(_highscore_table[0]));
 

	
 
		/* Copy over Top5 companies */
 
		for (i = 0; i < lengthof(_highscore_table[LAST_HS_ITEM]) && i < count; i++) {
 
			HighScore* hs = &_highscore_table[LAST_HS_ITEM][i];
 

	
 
			SetDParam(0, pl[i]->president_name_1);
 
			SetDParam(1, pl[i]->president_name_2);
 
			SetDParam(2, pl[i]->name_1);
 
			SetDParam(3, pl[i]->name_2);
 
			GetString(hs->company, STR_HIGHSCORE_NAME, lastof(hs->company)); // get manager/company name string
 
			hs->score = pl[i]->old_economy[0].performance_history;
 
			hs->title = EndGameGetPerformanceTitleFromValue(hs->score);
 

	
 
			// get the ranking of the local player
 
			if (pl[i]->index == _local_player) player = i;
 
		}
 
	}
 

	
 
	/* Add top5 players to highscore table */
 
	return player;
 
}
 

	
 
/* Save HighScore table to file */
 
void SaveToHighScore(void)
 
{
 
	FILE *fp = fopen(_highscore_file, "wb");
 

	
 
	if (fp != NULL) {
 
		uint i;
 
		HighScore *hs;
 

	
 
		for (i = 0; i < LAST_HS_ITEM; i++) { // don't save network highscores
 
			for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) {
 
				/* First character is a command character, so strlen will fail on that */
 
				byte length = min(sizeof(hs->company), (hs->company[0] == '\0') ? 0 : (int)strlen(&hs->company[1]) + 1);
 

	
 
				fwrite(&length, sizeof(length), 1, fp); // write away string length
 
				fwrite(hs->company, length, 1, fp);
 
				fwrite(&hs->score, sizeof(hs->score), 1, fp);
 
				fwrite("", 2, 1, fp); /* XXX - placeholder for hs->title, not saved anymore; compatibility */
 
			}
 
		}
 
		fclose(fp);
 
	}
 
}
 

	
 
/* Initialize the highscore table to 0 and if any file exists, load in values */
 
void LoadFromHighScore(void)
 
{
 
	FILE *fp = fopen(_highscore_file, "rb");
 

	
 
	memset(_highscore_table, 0, sizeof(_highscore_table));
 

	
 
	if (fp != NULL) {
 
		uint i;
 
		HighScore *hs;
 

	
 
		for (i = 0; i < LAST_HS_ITEM; i++) { // don't load network highscores
 
			for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) {
 
				byte length;
 
				fread(&length, sizeof(length), 1, fp);
 

	
 
				fread(hs->company, 1, length, fp);
 
				fread(&hs->score, sizeof(hs->score), 1, fp);
 
				fseek(fp, 2, SEEK_CUR); /* XXX - placeholder for hs->title, not saved anymore; compatibility */
 
				hs->title = EndGameGetPerformanceTitleFromValue(hs->score);
 
			}
 
		}
 
		fclose(fp);
 
	}
 

	
 
	/* Initialize end of game variable (when to show highscore chart) */
 
	_patches.ending_year = 2051;
 
}
 

	
 
// Save/load of players
 
static const SaveLoad _player_desc[] = {
 
	    SLE_VAR(Player, name_2,          SLE_UINT32),
 
	    SLE_VAR(Player, name_1,          SLE_STRINGID),
 

	
 
	    SLE_VAR(Player, president_name_1,SLE_UINT16),
 
	    SLE_VAR(Player, president_name_2,SLE_UINT32),
 

	
 
	    SLE_VAR(Player, face,            SLE_UINT32),
 

	
 
	// money was changed to a 64 bit field in savegame version 1.
 
	SLE_CONDVAR(Player, money64,               SLE_VAR_I64 | SLE_FILE_I32, 0, 0),
 
	SLE_CONDVAR(Player, money64,               SLE_INT64, 1, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(Player, current_loan,          SLE_INT32),
 

	
 
	    SLE_VAR(Player, player_color,          SLE_UINT8),
 
	    SLE_VAR(Player, player_money_fraction, SLE_UINT8),
 
	    SLE_VAR(Player, avail_railtypes,       SLE_UINT8),
 
	    SLE_VAR(Player, block_preview,         SLE_UINT8),
 

	
 
	    SLE_VAR(Player, cargo_types,           SLE_UINT16),
 
	SLE_CONDVAR(Player, location_of_house,     SLE_FILE_U16 | SLE_VAR_U32,  0,  5),
 
	SLE_CONDVAR(Player, location_of_house,     SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Player, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32,  0,  5),
 
	SLE_CONDVAR(Player, last_build_coordinate, SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Player, inaugurated_year,      SLE_FILE_U8  | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Player, inaugurated_year,      SLE_INT32,                  31, SL_MAX_VERSION),
 

	
 
	    SLE_ARR(Player, share_owners,          SLE_UINT8, 4),
 

	
 
	    SLE_VAR(Player, num_valid_stat_ent,    SLE_UINT8),
 

	
 
	    SLE_VAR(Player, quarters_of_bankrupcy, SLE_UINT8),
 
	    SLE_VAR(Player, bankrupt_asked,        SLE_UINT8),
 
	    SLE_VAR(Player, bankrupt_timeout,      SLE_INT16),
 
	    SLE_VAR(Player, bankrupt_value,        SLE_INT32),
 

	
 
	// yearly expenses was changed to 64-bit in savegame version 2.
 
	SLE_CONDARR(Player, yearly_expenses,       SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, 0, 1),
 
	SLE_CONDARR(Player, yearly_expenses,       SLE_INT64, 3 * 13,                  2, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Player, is_ai,                 SLE_BOOL, 2, SL_MAX_VERSION),
 
	SLE_CONDVAR(Player, is_active,             SLE_BOOL, 4, SL_MAX_VERSION),
 

	
 
	// Engine renewal settings
 
	SLE_CONDNULL(512, 16, 18),
 
	SLE_CONDREF(Player, engine_renew_list,     REF_ENGINE_RENEWS,          19, SL_MAX_VERSION),
 
	SLE_CONDVAR(Player, engine_renew,          SLE_BOOL,                   16, SL_MAX_VERSION),
 
	SLE_CONDVAR(Player, engine_renew_months,   SLE_INT16,                  16, SL_MAX_VERSION),
 
	SLE_CONDVAR(Player, engine_renew_money,    SLE_UINT32,                 16, SL_MAX_VERSION),
 
	SLE_CONDVAR(Player, renew_keep_length,     SLE_BOOL,                    2, SL_MAX_VERSION), // added with 16.1, but was blank since 2
 

	
 
	// reserve extra space in savegame here. (currently 63 bytes)
 
	SLE_CONDNULL(63, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _player_economy_desc[] = {
 
	// these were changed to 64-bit in savegame format 2
 
	SLE_CONDVAR(PlayerEconomyEntry, income,              SLE_INT32,                  0, 1),
 
	SLE_CONDVAR(PlayerEconomyEntry, expenses,            SLE_INT32,                  0, 1),
 
	SLE_CONDVAR(PlayerEconomyEntry, company_value,       SLE_FILE_I32 | SLE_VAR_I64, 0, 1),
 
	SLE_CONDVAR(PlayerEconomyEntry, income,              SLE_FILE_I64 | SLE_VAR_I32, 2, SL_MAX_VERSION),
 
	SLE_CONDVAR(PlayerEconomyEntry, expenses,            SLE_FILE_I64 | SLE_VAR_I32, 2, SL_MAX_VERSION),
 
	SLE_CONDVAR(PlayerEconomyEntry, company_value,       SLE_INT64,                  2, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(PlayerEconomyEntry, delivered_cargo,     SLE_INT32),
 
	    SLE_VAR(PlayerEconomyEntry, performance_history, SLE_INT32),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _player_ai_desc[] = {
 
	    SLE_VAR(PlayerAI, state,             SLE_UINT8),
 
	    SLE_VAR(PlayerAI, tick,              SLE_UINT8),
 
	SLE_CONDVAR(PlayerAI, state_counter,     SLE_FILE_U16 | SLE_VAR_U32,  0, 12),
 
	SLE_CONDVAR(PlayerAI, state_counter,     SLE_UINT32,                 13, SL_MAX_VERSION),
 
	    SLE_VAR(PlayerAI, timeout_counter,   SLE_UINT16),
 

	
 
	    SLE_VAR(PlayerAI, state_mode,        SLE_UINT8),
 
	    SLE_VAR(PlayerAI, banned_tile_count, SLE_UINT8),
 
	    SLE_VAR(PlayerAI, railtype_to_use,   SLE_UINT8),
 

	
 
	    SLE_VAR(PlayerAI, cargo_type,        SLE_UINT8),
 
	    SLE_VAR(PlayerAI, num_wagons,        SLE_UINT8),
 
	    SLE_VAR(PlayerAI, build_kind,        SLE_UINT8),
 
	    SLE_VAR(PlayerAI, num_build_rec,     SLE_UINT8),
 
	    SLE_VAR(PlayerAI, num_loco_to_build, SLE_UINT8),
 
	    SLE_VAR(PlayerAI, num_want_fullload, SLE_UINT8),
 

	
 
	    SLE_VAR(PlayerAI, route_type_mask,   SLE_UINT8),
 

	
 
	SLE_CONDVAR(PlayerAI, start_tile_a,      SLE_FILE_U16 | SLE_VAR_U32,  0,  5),
 
	SLE_CONDVAR(PlayerAI, start_tile_a,      SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(PlayerAI, cur_tile_a,        SLE_FILE_U16 | SLE_VAR_U32,  0,  5),
 
	SLE_CONDVAR(PlayerAI, cur_tile_a,        SLE_UINT32,                  6, SL_MAX_VERSION),
 
	    SLE_VAR(PlayerAI, start_dir_a,       SLE_UINT8),
 
	    SLE_VAR(PlayerAI, cur_dir_a,         SLE_UINT8),
 

	
 
	SLE_CONDVAR(PlayerAI, start_tile_b,      SLE_FILE_U16 | SLE_VAR_U32,  0,  5),
 
	SLE_CONDVAR(PlayerAI, start_tile_b,      SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(PlayerAI, cur_tile_b,        SLE_FILE_U16 | SLE_VAR_U32,  0,  5),
 
	SLE_CONDVAR(PlayerAI, cur_tile_b,        SLE_UINT32,                  6, SL_MAX_VERSION),
 
	    SLE_VAR(PlayerAI, start_dir_b,       SLE_UINT8),
 
	    SLE_VAR(PlayerAI, cur_dir_b,         SLE_UINT8),
 

	
 
	    SLE_REF(PlayerAI, cur_veh,           REF_VEHICLE),
 

	
 
	    SLE_ARR(PlayerAI, wagon_list,        SLE_UINT16, 9),
 
	    SLE_ARR(PlayerAI, order_list_blocks, SLE_UINT8, 20),
 
	    SLE_ARR(PlayerAI, banned_tiles,      SLE_UINT16, 16),
 

	
 
	SLE_CONDNULL(64, 2, SL_MAX_VERSION),
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _player_ai_build_rec_desc[] = {
 
	SLE_CONDVAR(AiBuildRec, spec_tile,         SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(AiBuildRec, spec_tile,         SLE_UINT32,                 6, SL_MAX_VERSION),
 
	SLE_CONDVAR(AiBuildRec, use_tile,          SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(AiBuildRec, use_tile,          SLE_UINT32,                 6, SL_MAX_VERSION),
 
	    SLE_VAR(AiBuildRec, rand_rng,          SLE_UINT8),
 
	    SLE_VAR(AiBuildRec, cur_building_rule, SLE_UINT8),
 
	    SLE_VAR(AiBuildRec, unk6,              SLE_UINT8),
 
	    SLE_VAR(AiBuildRec, unk7,              SLE_UINT8),
 
	    SLE_VAR(AiBuildRec, buildcmd_a,        SLE_UINT8),
 
	    SLE_VAR(AiBuildRec, buildcmd_b,        SLE_UINT8),
 
	    SLE_VAR(AiBuildRec, direction,         SLE_UINT8),
 
	    SLE_VAR(AiBuildRec, cargo,             SLE_UINT8),
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _player_livery_desc[] = {
 
	SLE_CONDVAR(Livery, in_use,  SLE_BOOL,  34, SL_MAX_VERSION),
 
	SLE_CONDVAR(Livery, colour1, SLE_UINT8, 34, SL_MAX_VERSION),
 
	SLE_CONDVAR(Livery, colour2, SLE_UINT8, 34, SL_MAX_VERSION),
 
	SLE_END()
 
};
 

	
 
static void SaveLoad_PLYR(Player* p)
 
{
 
	int i;
 

	
 
	SlObject(p, _player_desc);
 

	
 
	// Write AI?
 
	if (!IsHumanPlayer(p->index)) {
 
		SlObject(&p->ai, _player_ai_desc);
 
		for (i = 0; i != p->ai.num_build_rec; i++) {
 
			SlObject(&p->ai.src + i, _player_ai_build_rec_desc);
 
		}
 
	}
 

	
 
	// Write economy
 
	SlObject(&p->cur_economy, _player_economy_desc);
 

	
 
	// Write old economy entries.
 
	for (i = 0; i < p->num_valid_stat_ent; i++) {
 
		SlObject(&p->old_economy[i], _player_economy_desc);
 
	}
 

	
 
	// Write each livery entry.
 
	for (i = 0; i < LS_END; i++) {
 
		SlObject(&p->livery[i], _player_livery_desc);
 
	}
 
}
 

	
 
static void Save_PLYR(void)
 
{
 
	Player *p;
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active) {
 
			SlSetArrayIndex(p->index);
 
			SlAutolength((AutolengthProc*)SaveLoad_PLYR, p);
 
		}
 
	}
 
}
 

	
 
static void Load_PLYR(void)
 
{
 
	int index;
 
	while ((index = SlIterateArray()) != -1) {
 
		Player *p = GetPlayer(index);
 
		SaveLoad_PLYR(p);
 
		_player_colors[index] = p->player_color;
 
		UpdatePlayerMoney32(p);
 

	
 
		/* This is needed so an AI is attached to a loaded AI */
 
		if (p->is_ai && (!_networking || _network_server) && _ai.enabled)
 
			AI_StartNewAI(p->index);
 
	}
 
}
 

	
 
const ChunkHandler _player_chunk_handlers[] = {
 
	{ 'PLYR', Save_PLYR, Load_PLYR, CH_ARRAY | CH_LAST},
 
};
src/queue.c
Show inline comments
 
deleted file
src/queue.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "queue.h"
 

	
 
static void Stack_Clear(Queue* q, bool free_values)
 
{
 
	if (free_values) {
 
		uint i;
 

	
 
		for (i = 0; i < q->data.stack.size; i++) free(q->data.stack.elements[i]);
 
	}
 
	q->data.stack.size = 0;
 
}
 

	
 
static void Stack_Free(Queue* q, bool free_values)
 
{
 
	q->clear(q, free_values);
 
	free(q->data.stack.elements);
 
	if (q->freeq) free(q);
 
}
 

	
 
static bool Stack_Push(Queue* q, void* item, int priority)
 
{
 
	if (q->data.stack.size == q->data.stack.max_size) return false;
 
	q->data.stack.elements[q->data.stack.size++] = item;
 
	return true;
 
}
 

	
 
static void* Stack_Pop(Queue* q)
 
{
 
	if (q->data.stack.size == 0) return NULL;
 
	return q->data.stack.elements[--q->data.stack.size];
 
}
 

	
 
static bool Stack_Delete(Queue* q, void* item, int priority)
 
{
 
	return false;
 
}
 

	
 
static Queue* init_stack(Queue* q, uint max_size)
 
{
 
	q->push = Stack_Push;
 
	q->pop = Stack_Pop;
 
	q->del = Stack_Delete;
 
	q->clear = Stack_Clear;
 
	q->free = Stack_Free;
 
	q->data.stack.max_size = max_size;
 
	q->data.stack.size = 0;
 
	q->data.stack.elements = malloc(max_size * sizeof(*q->data.stack.elements));
 
	q->freeq = false;
 
	return q;
 
}
 

	
 
Queue* new_Stack(uint max_size)
 
{
 
	Queue* q = malloc(sizeof(*q));
 

	
 
	init_stack(q, max_size);
 
	q->freeq = true;
 
	return q;
 
}
 

	
 
/*
 
 * Fifo
 
 */
 

	
 
static void Fifo_Clear(Queue* q, bool free_values)
 
{
 
	if (free_values) {
 
		uint head = q->data.fifo.head;
 
		uint tail = q->data.fifo.tail; /* cache for speed */
 

	
 
		while (head != tail) {
 
			free(q->data.fifo.elements[tail]);
 
			tail = (tail + 1) % q->data.fifo.max_size;
 
		}
 
	}
 
	q->data.fifo.head = 0;
 
	q->data.fifo.tail = 0;
 
}
 

	
 
static void Fifo_Free(Queue* q, bool free_values)
 
{
 
	q->clear(q, free_values);
 
	free(q->data.fifo.elements);
 
	if (q->freeq) free(q);
 
}
 

	
 
static bool Fifo_Push(Queue* q, void* item, int priority)
 
{
 
	uint next = (q->data.fifo.head + 1) % q->data.fifo.max_size;
 

	
 
	if (next == q->data.fifo.tail) return false;
 
	q->data.fifo.elements[q->data.fifo.head] = item;
 

	
 
	q->data.fifo.head = next;
 
	return true;
 
}
 

	
 
static void* Fifo_Pop(Queue* q)
 
{
 
	void* result;
 

	
 
	if (q->data.fifo.head == q->data.fifo.tail) return NULL;
 
	result = q->data.fifo.elements[q->data.fifo.tail];
 

	
 
	q->data.fifo.tail = (q->data.fifo.tail + 1) % q->data.fifo.max_size;
 
	return result;
 
}
 

	
 
static bool Fifo_Delete(Queue* q, void* item, int priority)
 
{
 
	return false;
 
}
 

	
 
static Queue* init_fifo(Queue* q, uint max_size)
 
{
 
	q->push = Fifo_Push;
 
	q->pop = Fifo_Pop;
 
	q->del = Fifo_Delete;
 
	q->clear = Fifo_Clear;
 
	q->free = Fifo_Free;
 
	q->data.fifo.max_size = max_size;
 
	q->data.fifo.head = 0;
 
	q->data.fifo.tail = 0;
 
	q->data.fifo.elements = malloc(max_size * sizeof(*q->data.fifo.elements));
 
	q->freeq = false;
 
	return q;
 
}
 

	
 
Queue* new_Fifo(uint max_size)
 
{
 
	Queue* q = malloc(sizeof(*q));
 

	
 
	init_fifo(q, max_size);
 
	q->freeq = true;
 
	return q;
 
}
 

	
 

	
 
/*
 
 * Insertion Sorter
 
 */
 

	
 
static void InsSort_Clear(Queue* q, bool free_values)
 
{
 
	InsSortNode* node = q->data.inssort.first;
 
	InsSortNode* prev;
 

	
 
	while (node != NULL) {
 
		if (free_values) free(node->item);
 
		prev = node;
 
		node = node->next;
 
		free(prev);
 
	}
 
	q->data.inssort.first = NULL;
 
}
 

	
 
static void InsSort_Free(Queue* q, bool free_values)
 
{
 
	q->clear(q, free_values);
 
	if (q->freeq) free(q);
 
}
 

	
 
static bool InsSort_Push(Queue* q, void* item, int priority)
 
{
 
	InsSortNode* newnode = malloc(sizeof(*newnode));
 

	
 
	if (newnode == NULL) return false;
 
	newnode->item = item;
 
	newnode->priority = priority;
 
	if (q->data.inssort.first == NULL ||
 
			q->data.inssort.first->priority >= priority) {
 
		newnode->next = q->data.inssort.first;
 
		q->data.inssort.first = newnode;
 
	} else {
 
		InsSortNode* node = q->data.inssort.first;
 
		while (node != NULL) {
 
			if (node->next == NULL || node->next->priority >= priority) {
 
				newnode->next = node->next;
 
				node->next = newnode;
 
				break;
 
			}
 
			node = node->next;
 
		}
 
	}
 
	return true;
 
}
 

	
 
static void* InsSort_Pop(Queue* q)
 
{
 
	InsSortNode* node = q->data.inssort.first;
 
	void* result;
 

	
 
	if (node == NULL) return NULL;
 
	result = node->item;
 
	q->data.inssort.first = q->data.inssort.first->next;
 
	assert(q->data.inssort.first == NULL || q->data.inssort.first->priority >= node->priority);
 
	free(node);
 
	return result;
 
}
 

	
 
static bool InsSort_Delete(Queue* q, void* item, int priority)
 
{
 
	return false;
 
}
 

	
 
void init_InsSort(Queue* q)
 
{
 
	q->push = InsSort_Push;
 
	q->pop = InsSort_Pop;
 
	q->del = InsSort_Delete;
 
	q->clear = InsSort_Clear;
 
	q->free = InsSort_Free;
 
	q->data.inssort.first = NULL;
 
	q->freeq = false;
 
}
 

	
 
Queue* new_InsSort(void)
 
{
 
	Queue* q = malloc(sizeof(*q));
 

	
 
	init_InsSort(q);
 
	q->freeq = true;
 
	return q;
 
}
 

	
 

	
 
/*
 
 * Binary Heap
 
 * For information, see: http://www.policyalmanac.org/games/binaryHeaps.htm
 
 */
 

	
 
#define BINARY_HEAP_BLOCKSIZE (1 << BINARY_HEAP_BLOCKSIZE_BITS)
 
#define BINARY_HEAP_BLOCKSIZE_MASK (BINARY_HEAP_BLOCKSIZE - 1)
 

	
 
// To make our life easy, we make the next define
 
//  Because Binary Heaps works with array from 1 to n,
 
//  and C with array from 0 to n-1, and we don't like typing
 
//  q->data.binaryheap.elements[i - 1] every time, we use this define.
 
#define BIN_HEAP_ARR(i) q->data.binaryheap.elements[((i) - 1) >> BINARY_HEAP_BLOCKSIZE_BITS][((i) - 1) & BINARY_HEAP_BLOCKSIZE_MASK]
 

	
 
static void BinaryHeap_Clear(Queue* q, bool free_values)
 
{
 
	/* Free all items if needed and free all but the first blocks of memory */
 
	uint i;
 
	uint j;
 

	
 
	for (i = 0; i < q->data.binaryheap.blocks; i++) {
 
		if (q->data.binaryheap.elements[i] == NULL) {
 
			/* No more allocated blocks */
 
			break;
 
		}
 
		/* For every allocated block */
 
		if (free_values) {
 
			for (j = 0; j < (1 << BINARY_HEAP_BLOCKSIZE_BITS); j++) {
 
				/* For every element in the block */
 
				if ((q->data.binaryheap.size >> BINARY_HEAP_BLOCKSIZE_BITS) == i &&
 
						(q->data.binaryheap.size & BINARY_HEAP_BLOCKSIZE_MASK) == j) {
 
					break; /* We're past the last element */
 
				}
 
				free(q->data.binaryheap.elements[i][j].item);
 
			}
 
		}
 
		if (i != 0) {
 
			/* Leave the first block of memory alone */
 
			free(q->data.binaryheap.elements[i]);
 
			q->data.binaryheap.elements[i] = NULL;
 
		}
 
	}
 
	q->data.binaryheap.size = 0;
 
	q->data.binaryheap.blocks = 1;
 
}
 

	
 
static void BinaryHeap_Free(Queue* q, bool free_values)
 
{
 
	uint i;
 

	
 
	q->clear(q, free_values);
 
	for (i = 0; i < q->data.binaryheap.blocks; i++) {
 
		if (q->data.binaryheap.elements[i] == NULL) break;
 
		free(q->data.binaryheap.elements[i]);
 
	}
 
	free(q->data.binaryheap.elements);
 
	if (q->freeq) free(q);
 
}
 

	
 
static bool BinaryHeap_Push(Queue* q, void* item, int priority)
 
{
 
#ifdef QUEUE_DEBUG
 
	printf("[BinaryHeap] Pushing an element. There are %d elements left\n", q->data.binaryheap.size);
 
#endif
 

	
 
	if (q->data.binaryheap.size == q->data.binaryheap.max_size) return false;
 
	assert(q->data.binaryheap.size < q->data.binaryheap.max_size);
 

	
 
	if (q->data.binaryheap.elements[q->data.binaryheap.size >> BINARY_HEAP_BLOCKSIZE_BITS] == NULL) {
 
		/* The currently allocated blocks are full, allocate a new one */
 
		assert((q->data.binaryheap.size & BINARY_HEAP_BLOCKSIZE_MASK) == 0);
 
		q->data.binaryheap.elements[q->data.binaryheap.size >> BINARY_HEAP_BLOCKSIZE_BITS] = malloc(BINARY_HEAP_BLOCKSIZE * sizeof(*q->data.binaryheap.elements[0]));
 
		q->data.binaryheap.blocks++;
 
#ifdef QUEUE_DEBUG
 
		printf("[BinaryHeap] Increasing size of elements to %d nodes\n", q->data.binaryheap.blocks *  BINARY_HEAP_BLOCKSIZE);
 
#endif
 
	}
 

	
 
	// Add the item at the end of the array
 
	BIN_HEAP_ARR(q->data.binaryheap.size + 1).priority = priority;
 
	BIN_HEAP_ARR(q->data.binaryheap.size + 1).item = item;
 
	q->data.binaryheap.size++;
 

	
 
	// Now we are going to check where it belongs. As long as the parent is
 
	// bigger, we switch with the parent
 
	{
 
		BinaryHeapNode temp;
 
		int i;
 
		int j;
 

	
 
		i = q->data.binaryheap.size;
 
		while (i > 1) {
 
			// Get the parent of this object (divide by 2)
 
			j = i / 2;
 
			// Is the parent bigger then the current, switch them
 
			if (BIN_HEAP_ARR(i).priority <= BIN_HEAP_ARR(j).priority) {
 
				temp = BIN_HEAP_ARR(j);
 
				BIN_HEAP_ARR(j) = BIN_HEAP_ARR(i);
 
				BIN_HEAP_ARR(i) = temp;
 
				i = j;
 
			} else {
 
				// It is not, we're done!
 
				break;
 
			}
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
static bool BinaryHeap_Delete(Queue* q, void* item, int priority)
 
{
 
	uint i = 0;
 

	
 
#ifdef QUEUE_DEBUG
 
	printf("[BinaryHeap] Deleting an element. There are %d elements left\n", q->data.binaryheap.size);
 
#endif
 

	
 
	// First, we try to find the item..
 
	do {
 
		if (BIN_HEAP_ARR(i + 1).item == item) break;
 
		i++;
 
	} while (i < q->data.binaryheap.size);
 
	// We did not find the item, so we return false
 
	if (i == q->data.binaryheap.size) return false;
 

	
 
	// Now we put the last item over the current item while decreasing the size of the elements
 
	q->data.binaryheap.size--;
 
	BIN_HEAP_ARR(i + 1) = BIN_HEAP_ARR(q->data.binaryheap.size + 1);
 

	
 
	// Now the only thing we have to do, is resort it..
 
	// On place i there is the item to be sorted.. let's start there
 
	{
 
		uint j;
 
		BinaryHeapNode temp;
 
		/* Because of the fact that Binary Heap uses array from 1 to n, we need to
 
		 * increase i by 1
 
		 */
 
		i++;
 

	
 
		for (;;) {
 
			j = i;
 
			// Check if we have 2 childs
 
			if (2 * j + 1 <= q->data.binaryheap.size) {
 
				// Is this child smaller than the parent?
 
				if (BIN_HEAP_ARR(j).priority >= BIN_HEAP_ARR(2 * j).priority) i = 2 * j;
 
				// Yes, we _need_ to use i here, not j, because we want to have the smallest child
 
				//  This way we get that straight away!
 
				if (BIN_HEAP_ARR(i).priority >= BIN_HEAP_ARR(2 * j + 1).priority) i = 2 * j + 1;
 
			// Do we have one child?
 
			} else if (2 * j <= q->data.binaryheap.size) {
 
				if (BIN_HEAP_ARR(j).priority >= BIN_HEAP_ARR(2 * j).priority) i = 2 * j;
 
			}
 

	
 
			// One of our childs is smaller than we are, switch
 
			if (i != j) {
 
				temp = BIN_HEAP_ARR(j);
 
				BIN_HEAP_ARR(j) = BIN_HEAP_ARR(i);
 
				BIN_HEAP_ARR(i) = temp;
 
			} else {
 
				// None of our childs is smaller, so we stay here.. stop :)
 
				break;
 
			}
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
static void* BinaryHeap_Pop(Queue* q)
 
{
 
	void* result;
 

	
 
#ifdef QUEUE_DEBUG
 
	printf("[BinaryHeap] Popping an element. There are %d elements left\n", q->data.binaryheap.size);
 
#endif
 

	
 
	if (q->data.binaryheap.size == 0) return NULL;
 

	
 
	// The best item is always on top, so give that as result
 
	result = BIN_HEAP_ARR(1).item;
 
	// And now we should get rid of this item...
 
	BinaryHeap_Delete(q, BIN_HEAP_ARR(1).item, BIN_HEAP_ARR(1).priority);
 

	
 
	return result;
 
}
 

	
 
void init_BinaryHeap(Queue* q, uint max_size)
 
{
 
	assert(q != NULL);
 
	q->push = BinaryHeap_Push;
 
	q->pop = BinaryHeap_Pop;
 
	q->del = BinaryHeap_Delete;
 
	q->clear = BinaryHeap_Clear;
 
	q->free = BinaryHeap_Free;
 
	q->data.binaryheap.max_size = max_size;
 
	q->data.binaryheap.size = 0;
 
	// We malloc memory in block of BINARY_HEAP_BLOCKSIZE
 
	//   It autosizes when it runs out of memory
 
	q->data.binaryheap.elements = calloc((max_size - 1) / BINARY_HEAP_BLOCKSIZE + 1, sizeof(*q->data.binaryheap.elements));
 
	q->data.binaryheap.elements[0] = malloc(BINARY_HEAP_BLOCKSIZE * sizeof(*q->data.binaryheap.elements[0]));
 
	q->data.binaryheap.blocks = 1;
 
	q->freeq = false;
 
#ifdef QUEUE_DEBUG
 
	printf("[BinaryHeap] Initial size of elements is %d nodes\n", BINARY_HEAP_BLOCKSIZE);
 
#endif
 
}
 

	
 
Queue* new_BinaryHeap(uint max_size)
 
{
 
	Queue* q = malloc(sizeof(*q));
 

	
 
	init_BinaryHeap(q, max_size);
 
	q->freeq = true;
 
	return q;
 
}
 

	
 
// Because we don't want anyone else to bother with our defines
 
#undef BIN_HEAP_ARR
 

	
 
/*
 
 * Hash
 
 */
 

	
 
void init_Hash(Hash* h, Hash_HashProc* hash, uint num_buckets)
 
{
 
	/* Allocate space for the Hash, the buckets and the bucket flags */
 
	uint i;
 

	
 
	assert(h != NULL);
 
#ifdef HASH_DEBUG
 
	debug("Allocated hash: %p", h);
 
#endif
 
	h->hash = hash;
 
	h->size = 0;
 
	h->num_buckets = num_buckets;
 
	h->buckets = malloc(num_buckets * (sizeof(*h->buckets) + sizeof(*h->buckets_in_use)));
 
#ifdef HASH_DEBUG
 
	debug("Buckets = %p", h->buckets);
 
#endif
 
	h->buckets_in_use = (bool*)(h->buckets + num_buckets);
 
	h->freeh = false;
 
	for (i = 0; i < num_buckets; i++) h->buckets_in_use[i] = false;
 
}
 

	
 
Hash* new_Hash(Hash_HashProc* hash, int num_buckets)
 
{
 
	Hash* h = malloc(sizeof(*h));
 

	
 
	init_Hash(h, hash, num_buckets);
 
	h->freeh = true;
 
	return h;
 
}
 

	
 
void delete_Hash(Hash* h, bool free_values)
 
{
 
	uint i;
 

	
 
	/* Iterate all buckets */
 
	for (i = 0; i < h->num_buckets; i++) {
 
		if (h->buckets_in_use[i]) {
 
			HashNode* node;
 

	
 
			/* Free the first value */
 
			if (free_values) free(h->buckets[i].value);
 
			node = h->buckets[i].next;
 
			while (node != NULL) {
 
				HashNode* prev = node;
 

	
 
				node = node->next;
 
				/* Free the value */
 
				if (free_values) free(prev->value);
 
				/* Free the node */
 
				free(prev);
 
			}
 
		}
 
	}
 
	free(h->buckets);
 
	/* No need to free buckets_in_use, it is always allocated in one
 
	 * malloc with buckets */
 
#ifdef HASH_DEBUG
 
	debug("Freeing Hash: %p", h);
 
#endif
 
	if (h->freeh) free(h);
 
}
 

	
 
#ifdef HASH_STATS
 
static void stat_Hash(const Hash* h)
 
{
 
	uint used_buckets = 0;
 
	uint max_collision = 0;
 
	uint max_usage = 0;
 
	uint usage[200];
 
	uint i;
 

	
 
	for (i = 0; i < lengthof(usage); i++) usage[i] = 0;
 
	for (i = 0; i < h->num_buckets; i++) {
 
		uint collision = 0;
 
		if (h->buckets_in_use[i]) {
 
			const HashNode* node;
 

	
 
			used_buckets++;
 
			for (node = &h->buckets[i]; node != NULL; node = node->next) collision++;
 
			if (collision > max_collision) max_collision = collision;
 
		}
 
		if (collision >= lengthof(usage)) collision = lengthof(usage) - 1;
 
		usage[collision]++;
 
		if (collision > 0 && usage[collision] >= max_usage) {
 
			max_usage = usage[collision];
 
		}
 
	}
 
	printf(
 
		"---\n"
 
		"Hash size: %d\n"
 
		"Nodes used: %d\n"
 
		"Non empty buckets: %d\n"
 
		"Max collision: %d\n",
 
		h->num_buckets, h->size, used_buckets, max_collision
 
	);
 
	printf("{ ");
 
	for (i = 0; i <= max_collision; i++) {
 
		if (usage[i] > 0) {
 
			printf("%d:%d ", i, usage[i]);
 
#if 0
 
			if (i > 0) {
 
				uint j;
 

	
 
				for (j = 0; j < usage[i] * 160 / 800; j++) putchar('#');
 
			}
 
			printf("\n");
 
#endif
 
		}
 
	}
 
	printf ("}\n");
 
}
 
#endif
 

	
 
void clear_Hash(Hash* h, bool free_values)
 
{
 
	uint i;
 

	
 
#ifdef HASH_STATS
 
	if (h->size > 2000) stat_Hash(h);
 
#endif
 

	
 
	/* Iterate all buckets */
 
	for (i = 0; i < h->num_buckets; i++) {
 
		if (h->buckets_in_use[i]) {
 
			HashNode* node;
 

	
 
			h->buckets_in_use[i] = false;
 
			/* Free the first value */
 
			if (free_values) free(h->buckets[i].value);
 
			node = h->buckets[i].next;
 
			while (node != NULL) {
 
				HashNode* prev = node;
 

	
 
				node = node->next;
 
				if (free_values) free(prev->value);
 
				free(prev);
 
			}
 
		}
 
	}
 
	h->size = 0;
 
}
 

	
 
/* Finds the node that that saves this key pair. If it is not
 
 * found, returns NULL. If it is found, *prev is set to the
 
 * node before the one found, or if the node found was the first in the bucket
 
 * to NULL. If it is not found, *prev is set to the last HashNode in the
 
 * bucket, or NULL if it is empty. prev can also be NULL, in which case it is
 
 * not used for output.
 
 */
 
static HashNode* Hash_FindNode(const Hash* h, uint key1, uint key2, HashNode** prev_out)
 
{
 
	uint hash = h->hash(key1, key2);
 
	HashNode* result = NULL;
 

	
 
#ifdef HASH_DEBUG
 
	debug("Looking for %u, %u", key1, key2);
 
#endif
 
	/* Check if the bucket is empty */
 
	if (!h->buckets_in_use[hash]) {
 
		if (prev_out != NULL) *prev_out = NULL;
 
		result = NULL;
 
	/* Check the first node specially */
 
	} else if (h->buckets[hash].key1 == key1 && h->buckets[hash].key2 == key2) {
 
		/* Save the value */
 
		result = h->buckets + hash;
 
		if (prev_out != NULL) *prev_out = NULL;
 
#ifdef HASH_DEBUG
 
		debug("Found in first node: %p", result);
 
#endif
 
	/* Check all other nodes */
 
	} else {
 
		HashNode* prev = h->buckets + hash;
 
		HashNode* node;
 

	
 
		for (node = prev->next; node != NULL; node = node->next) {
 
			if (node->key1 == key1 && node->key2 == key2) {
 
				/* Found it */
 
				result = node;
 
#ifdef HASH_DEBUG
 
				debug("Found in other node: %p", result);
 
#endif
 
				break;
 
			}
 
			prev = node;
 
		}
 
		if (prev_out != NULL) *prev_out = prev;
 
	}
 
#ifdef HASH_DEBUG
 
	if (result == NULL) debug("Not found");
 
#endif
 
	return result;
 
}
 

	
 
void* Hash_Delete(Hash* h, uint key1, uint key2)
 
{
 
	void* result;
 
	HashNode* prev; /* Used as output var for below function call */
 
	HashNode* node = Hash_FindNode(h, key1, key2, &prev);
 

	
 
	if (node == NULL) {
 
		/* not found */
 
		result = NULL;
 
	} else if (prev == NULL) {
 
		/* It is in the first node, we can't free that one, so we free
 
		 * the next one instead (if there is any)*/
 
		/* Save the value */
 
		result = node->value;
 
		if (node->next != NULL) {
 
			HashNode* next = node->next;
 
			/* Copy the second to the first */
 
			*node = *next;
 
			/* Free the second */
 
#ifndef NOFREE
 
			free(next);
 
#endif
 
		} else {
 
			/* This was the last in this bucket */
 
			/* Mark it as empty */
 
			uint hash = h->hash(key1, key2);
 
			h->buckets_in_use[hash] = false;
 
		}
 
	} else {
 
		/* It is in another node */
 
		/* Save the value */
 
		result = node->value;
 
		/* Link previous and next nodes */
 
		prev->next = node->next;
 
		/* Free the node */
 
#ifndef NOFREE
 
		free(node);
 
#endif
 
	}
 
	if (result != NULL) h->size--;
 
	return result;
 
}
 

	
 

	
 
void* Hash_Set(Hash* h, uint key1, uint key2, void* value)
 
{
 
	HashNode* prev;
 
	HashNode* node = Hash_FindNode(h, key1, key2, &prev);
 

	
 
	if (node != NULL) {
 
		/* Found it */
 
		void* result = node->value;
 

	
 
		node->value = value;
 
		return result;
 
	}
 
	/* It is not yet present, let's add it */
 
	if (prev == NULL) {
 
		/* The bucket is still empty */
 
		uint hash = h->hash(key1, key2);
 
		h->buckets_in_use[hash] = true;
 
		node = h->buckets + hash;
 
	} else {
 
		/* Add it after prev */
 
		node = malloc(sizeof(*node));
 
		prev->next = node;
 
	}
 
	node->next = NULL;
 
	node->key1 = key1;
 
	node->key2 = key2;
 
	node->value = value;
 
	h->size++;
 
	return NULL;
 
}
 

	
 
void* Hash_Get(const Hash* h, uint key1, uint key2)
 
{
 
	HashNode* node = Hash_FindNode(h, key1, key2, NULL);
 

	
 
#ifdef HASH_DEBUG
 
	debug("Found node: %p", node);
 
#endif
 
	return (node != NULL) ? node->value : NULL;
 
}
 

	
 
uint Hash_Size(const Hash* h)
 
{
 
	return h->size;
 
}
src/rail.c
Show inline comments
 
deleted file
src/rail.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "rail.h"
 
#include "station_map.h"
 
#include "tunnel_map.h"
 

	
 
/* XXX: Below 3 tables store duplicate data. Maybe remove some? */
 
/* Maps a trackdir to the bit that stores its status in the map arrays, in the
 
 * direction along with the trackdir */
 
const byte _signal_along_trackdir[] = {
 
	0x80, 0x80, 0x80, 0x20, 0x40, 0x10, 0, 0,
 
	0x40, 0x40, 0x40, 0x10, 0x80, 0x20
 
};
 

	
 
/* Maps a trackdir to the bit that stores its status in the map arrays, in the
 
 * direction against the trackdir */
 
const byte _signal_against_trackdir[] = {
 
	0x40, 0x40, 0x40, 0x10, 0x80, 0x20, 0, 0,
 
	0x80, 0x80, 0x80, 0x20, 0x40, 0x10
 
};
 

	
 
/* Maps a Track to the bits that store the status of the two signals that can
 
 * be present on the given track */
 
const byte _signal_on_track[] = {
 
	0xC0, 0xC0, 0xC0, 0x30, 0xC0, 0x30
 
};
 

	
 
/* Maps a diagonal direction to the all trackdirs that are connected to any
 
 * track entering in this direction (including those making 90 degree turns)
 
 */
 
const TrackdirBits _exitdir_reaches_trackdirs[] = {
 
	TRACKDIR_BIT_X_NE | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_LEFT_N,  /* DIAGDIR_NE */
 
	TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_LEFT_S  | TRACKDIR_BIT_UPPER_E, /* DIAGDIR_SE */
 
	TRACKDIR_BIT_X_SW | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_RIGHT_S, /* DIAGDIR_SW */
 
	TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_W  /* DIAGDIR_NW */
 
};
 

	
 
const Trackdir _next_trackdir[] = {
 
	TRACKDIR_X_NE,  TRACKDIR_Y_SE,  TRACKDIR_LOWER_E, TRACKDIR_UPPER_E, TRACKDIR_RIGHT_S, TRACKDIR_LEFT_S, INVALID_TRACKDIR, INVALID_TRACKDIR,
 
	TRACKDIR_X_SW,  TRACKDIR_Y_NW,  TRACKDIR_LOWER_W, TRACKDIR_UPPER_W, TRACKDIR_RIGHT_N, TRACKDIR_LEFT_N
 
};
 

	
 
/* Maps a trackdir to all trackdirs that make 90 deg turns with it. */
 
const TrackdirBits _track_crosses_trackdirs[] = {
 
	TRACKDIR_BIT_Y_SE     | TRACKDIR_BIT_Y_NW,                                                   /* TRACK_X     */
 
	TRACKDIR_BIT_X_NE     | TRACKDIR_BIT_X_SW,                                                   /* TRACK_Y     */
 
	TRACKDIR_BIT_RIGHT_N  | TRACKDIR_BIT_RIGHT_S  | TRACKDIR_BIT_LEFT_N  | TRACKDIR_BIT_LEFT_S,  /* TRACK_UPPER */
 
	TRACKDIR_BIT_RIGHT_N  | TRACKDIR_BIT_RIGHT_S  | TRACKDIR_BIT_LEFT_N  | TRACKDIR_BIT_LEFT_S,  /* TRACK_LOWER */
 
	TRACKDIR_BIT_UPPER_W  | TRACKDIR_BIT_UPPER_E  | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_LOWER_E, /* TRACK_LEFT  */
 
	TRACKDIR_BIT_UPPER_W  | TRACKDIR_BIT_UPPER_E  | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_LOWER_E  /* TRACK_RIGHT */
 
};
 

	
 
/* Maps a track to all tracks that make 90 deg turns with it. */
 
const TrackBits _track_crosses_tracks[] = {
 
	TRACK_BIT_Y,    /* TRACK_X     */
 
	TRACK_BIT_X,    /* TRACK_Y     */
 
	TRACK_BIT_VERT, /* TRACK_UPPER */
 
	TRACK_BIT_VERT, /* TRACK_LOWER */
 
	TRACK_BIT_HORZ, /* TRACK_LEFT  */
 
	TRACK_BIT_HORZ  /* TRACK_RIGHT */
 
};
 

	
 
/* Maps a trackdir to the (4-way) direction the tile is exited when following
 
 * that trackdir */
 
const DiagDirection _trackdir_to_exitdir[] = {
 
	DIAGDIR_NE,DIAGDIR_SE,DIAGDIR_NE,DIAGDIR_SE,DIAGDIR_SW,DIAGDIR_SE, DIAGDIR_NE,DIAGDIR_NE,
 
	DIAGDIR_SW,DIAGDIR_NW,DIAGDIR_NW,DIAGDIR_SW,DIAGDIR_NW,DIAGDIR_NE,
 
};
 

	
 
const Trackdir _track_exitdir_to_trackdir[][DIAGDIR_END] = {
 
	{TRACKDIR_X_NE,     INVALID_TRACKDIR,  TRACKDIR_X_SW,     INVALID_TRACKDIR},
 
	{INVALID_TRACKDIR,  TRACKDIR_Y_SE,     INVALID_TRACKDIR,  TRACKDIR_Y_NW},
 
	{TRACKDIR_UPPER_E,  INVALID_TRACKDIR,  INVALID_TRACKDIR,  TRACKDIR_UPPER_W},
 
	{INVALID_TRACKDIR,  TRACKDIR_LOWER_E,  TRACKDIR_LOWER_W,  INVALID_TRACKDIR},
 
	{INVALID_TRACKDIR,  INVALID_TRACKDIR,  TRACKDIR_LEFT_S,   TRACKDIR_LEFT_N},
 
	{TRACKDIR_RIGHT_N,  TRACKDIR_RIGHT_S,  INVALID_TRACKDIR,  INVALID_TRACKDIR}
 
};
 

	
 
const Trackdir _track_enterdir_to_trackdir[][DIAGDIR_END] = { // TODO: replace magic with enums
 
	{TRACKDIR_X_NE,     INVALID_TRACKDIR,  TRACKDIR_X_SW,     INVALID_TRACKDIR},
 
	{INVALID_TRACKDIR,  TRACKDIR_Y_SE,     INVALID_TRACKDIR,  TRACKDIR_Y_NW},
 
	{INVALID_TRACKDIR,  TRACKDIR_UPPER_E,  TRACKDIR_UPPER_W,  INVALID_TRACKDIR},
 
	{TRACKDIR_LOWER_E,  INVALID_TRACKDIR,  INVALID_TRACKDIR,  TRACKDIR_LOWER_W},
 
	{TRACKDIR_LEFT_N,   TRACKDIR_LEFT_S,   INVALID_TRACKDIR,  INVALID_TRACKDIR},
 
	{INVALID_TRACKDIR,  INVALID_TRACKDIR,  TRACKDIR_RIGHT_S,  TRACKDIR_RIGHT_N}
 
};
 

	
 
const Trackdir _track_direction_to_trackdir[][DIR_END] = {
 
	{INVALID_TRACKDIR, TRACKDIR_X_NE,     INVALID_TRACKDIR, INVALID_TRACKDIR,  INVALID_TRACKDIR, TRACKDIR_X_SW,     INVALID_TRACKDIR, INVALID_TRACKDIR},
 
	{INVALID_TRACKDIR, INVALID_TRACKDIR,  INVALID_TRACKDIR, TRACKDIR_Y_SE,     INVALID_TRACKDIR, INVALID_TRACKDIR,  INVALID_TRACKDIR, TRACKDIR_Y_NW},
 
	{INVALID_TRACKDIR, INVALID_TRACKDIR,  TRACKDIR_UPPER_E, INVALID_TRACKDIR,  INVALID_TRACKDIR, INVALID_TRACKDIR,  TRACKDIR_UPPER_W, INVALID_TRACKDIR},
 
	{INVALID_TRACKDIR, INVALID_TRACKDIR,  TRACKDIR_LOWER_E, INVALID_TRACKDIR,  INVALID_TRACKDIR, INVALID_TRACKDIR,  TRACKDIR_LOWER_W, INVALID_TRACKDIR},
 
	{TRACKDIR_LEFT_N,  INVALID_TRACKDIR,  INVALID_TRACKDIR, INVALID_TRACKDIR,  TRACKDIR_LEFT_S,  INVALID_TRACKDIR,  INVALID_TRACKDIR, INVALID_TRACKDIR},
 
	{TRACKDIR_RIGHT_N, INVALID_TRACKDIR,  INVALID_TRACKDIR, INVALID_TRACKDIR,  TRACKDIR_RIGHT_S, INVALID_TRACKDIR,  INVALID_TRACKDIR, INVALID_TRACKDIR}
 
};
 

	
 
const Trackdir _dir_to_diag_trackdir[] = {
 
	TRACKDIR_X_NE, TRACKDIR_Y_SE, TRACKDIR_X_SW, TRACKDIR_Y_NW,
 
};
 

	
 

	
 
RailType GetTileRailType(TileIndex tile, Trackdir trackdir)
 
{
 
	switch (GetTileType(tile)) {
 
		case MP_RAILWAY:
 
			return GetRailType(tile);
 

	
 
		case MP_STREET:
 
			/* rail/road crossing */
 
			if (IsLevelCrossing(tile)) return GetRailTypeCrossing(tile);
 
			break;
 

	
 
		case MP_STATION:
 
			if (IsRailwayStationTile(tile)) return GetRailType(tile);
 
			break;
 

	
 
		case MP_TUNNELBRIDGE:
 
			if (IsTunnel(tile)) {
 
				if (GetTunnelTransportType(tile) == TRANSPORT_RAIL) return GetRailType(tile);
 
			} else {
 
				if (GetBridgeTransportType(tile) == TRANSPORT_RAIL) return GetRailType(tile);
 
			}
 
			break;
 

	
 
		default:
 
			break;
 
	}
 
	return INVALID_RAILTYPE;
 
}
src/rail_cmd.c
Show inline comments
 
deleted file
src/rail_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.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 */
 
		return true;
 
	}
 
}
 

	
 

	
 
static const TrackBits _valid_tileh_slopes[][15] = {
 

	
 
// set of normal ones
 
{
 
	TRACK_BIT_ALL,
 
	TRACK_BIT_RIGHT,
 
	TRACK_BIT_UPPER,
 
	TRACK_BIT_X,
 

	
 
	TRACK_BIT_LEFT,
 
	0,
 
	TRACK_BIT_Y,
 
	TRACK_BIT_LOWER,
 

	
 
	TRACK_BIT_LOWER,
 
	TRACK_BIT_Y,
 
	0,
 
	TRACK_BIT_LEFT,
 

	
 
	TRACK_BIT_X,
 
	TRACK_BIT_UPPER,
 
	TRACK_BIT_RIGHT,
 
},
 

	
 
// allowed rail for an evenly raised platform
 
{
 
	0,
 
	TRACK_BIT_LEFT,
 
	TRACK_BIT_LOWER,
 
	TRACK_BIT_Y | TRACK_BIT_LOWER | TRACK_BIT_LEFT,
 

	
 
	TRACK_BIT_RIGHT,
 
	TRACK_BIT_ALL,
 
	TRACK_BIT_X | TRACK_BIT_LOWER | TRACK_BIT_RIGHT,
 
	TRACK_BIT_ALL,
 

	
 
	TRACK_BIT_UPPER,
 
	TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_LEFT,
 
	TRACK_BIT_ALL,
 
	TRACK_BIT_ALL,
 

	
 
	TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_RIGHT,
 
	TRACK_BIT_ALL,
 
	TRACK_BIT_ALL
 
}
 
};
 

	
 
uint GetRailFoundation(Slope tileh, TrackBits bits)
 
{
 
	uint i;
 

	
 
	if (!IsSteepSlope(tileh)) {
 
		if ((~_valid_tileh_slopes[0][tileh] & bits) == 0) return 0;
 
		if ((~_valid_tileh_slopes[1][tileh] & bits) == 0) return tileh;
 
	}
 

	
 
	switch (bits) {
 
		default: NOT_REACHED();
 
		case TRACK_BIT_X: i = 0; break;
 
		case TRACK_BIT_Y: i = 1; break;
 
		case TRACK_BIT_LEFT:  return 15 + 8 + (tileh == SLOPE_STEEP_W ? 4 : 0);
 
		case TRACK_BIT_LOWER: return 15 + 8 + (tileh == SLOPE_STEEP_S ? 5 : 1);
 
		case TRACK_BIT_RIGHT: return 15 + 8 + (tileh == SLOPE_STEEP_E ? 6 : 2);
 
		case TRACK_BIT_UPPER: return 15 + 8 + (tileh == SLOPE_STEEP_N ? 7 : 3);
 
	}
 
	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;
 
	}
 
	return i + 15;
 
}
 

	
 

	
 
static uint32 CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
 
{
 
	if (IsSteepSlope(tileh)) {
 
		if (existing == 0) {
 
			TrackBits valid = TRACK_BIT_CROSS | (HASBIT(1 << SLOPE_STEEP_W | 1 << SLOPE_STEEP_E, tileh) ? TRACK_BIT_VERT : TRACK_BIT_HORZ);
 
			if (valid & rail_bits) return _price.terraform;
 
		}
 
	} else {
 
		rail_bits |= existing;
 

	
 
		// don't allow building on the lower side of a coast
 
		if (IsTileType(tile, MP_WATER) &&
 
				~_valid_tileh_slopes[1][tileh] & rail_bits) {
 
			return_cmd_error(STR_3807_CAN_T_BUILD_ON_WATER);
 
		}
 

	
 
		// no special foundation
 
		if ((~_valid_tileh_slopes[0][tileh] & rail_bits) == 0)
 
			return 0;
 

	
 
		if ((~_valid_tileh_slopes[1][tileh] & rail_bits) == 0 || ( // whole tile is leveled up
 
					(rail_bits == TRACK_BIT_X || rail_bits == TRACK_BIT_Y) &&
 
					(tileh == SLOPE_W || tileh == SLOPE_S || tileh == SLOPE_E || tileh == SLOPE_N)
 
				)) { // partly up
 
			if (existing != 0) {
 
				return 0;
 
			} else if (!_patches.build_on_slopes || _is_old_ai_player) {
 
				return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 
			} else {
 
				return _price.terraform;
 
			}
 
		}
 
	}
 
	return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 
}
 

	
 
/* Validate functions for rail building */
 
static inline bool ValParamTrackOrientation(Track track) {return IsValidTrack(track);}
 

	
 
/** Build a single piece of rail
 
 * @param tile tile  to build on
 
 * @param p1 railtype of being built piece (normal, mono, maglev)
 
 * @param p2 rail track to build
 
 */
 
int32 CmdBuildSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Slope tileh;
 
	RailType railtype;
 
	Track track;
 
	TrackBits trackbit;
 
	int32 cost = 0;
 
	int32 ret;
 

	
 
	if (!ValParamRailtype(p1) || !ValParamTrackOrientation(p2)) return CMD_ERROR;
 
	railtype = (RailType)p1;
 
	track = (Track)p2;
 

	
 
	tileh = GetTileSlope(tile, NULL);
 
	trackbit = TrackToTrackBits(track);
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	switch (GetTileType(tile)) {
 
		case MP_RAILWAY:
 
			if (!CheckTrackCombination(tile, trackbit, flags) ||
 
					!EnsureNoVehicle(tile)) {
 
				return CMD_ERROR;
 
			}
 
			if (!IsTileOwner(tile, _current_player) ||
 
					!IsCompatibleRail(GetRailType(tile), railtype)) {
 
				// Get detailed error message
 
				return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
			}
 

	
 
			ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile);
 
			if (CmdFailed(ret)) return ret;
 
			cost += ret;
 

	
 
			/* If the rail types don't match, try to convert only if engines of
 
			 * the present rail type are powered on the new rail type. */
 
			if (GetRailType(tile) != railtype && HasPowerOnRail(GetRailType(tile), railtype)) {
 
				ret = DoCommand(tile, tile, railtype, flags, CMD_CONVERT_RAIL);
 
				if (CmdFailed(ret)) return ret;
 
				cost += ret;
 
			}
 

	
 
			if (flags & DC_EXEC) {
 
				SetRailGroundType(tile, RAIL_GROUND_BARREN);
 
				SetTrackBits(tile, GetTrackBits(tile) | trackbit);
 
			}
 
			break;
 

	
 
		case MP_STREET:
 
#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 (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
			if (GetRoadTileType(tile) == ROAD_TILE_NORMAL) {
 
				if (HasRoadWorks(tile)) return_cmd_error(STR_ROAD_WORKS_IN_PROGRESS);
 

	
 
				if ((track == TRACK_X && GetRoadBits(tile) == ROAD_Y) ||
 
						(track == TRACK_Y && GetRoadBits(tile) == ROAD_X)) {
 
					if (flags & DC_EXEC) {
 
						MakeRoadCrossing(tile, GetTileOwner(tile), _current_player, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, GetTownIndex(tile));
 
					}
 
					break;
 
				}
 
			}
 

	
 
			if (IsLevelCrossing(tile) && GetCrossingRailBits(tile) == trackbit) {
 
				return_cmd_error(STR_1007_ALREADY_BUILT);
 
			}
 
			/* FALLTHROUGH */
 

	
 
		default:
 
			ret = CheckRailSlope(tileh, trackbit, 0, tile);
 
			if (CmdFailed(ret)) return ret;
 
			cost += ret;
 

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

	
 
			if (flags & DC_EXEC) MakeRailNormal(tile, _current_player, trackbit, railtype);
 
			break;
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		MarkTileDirtyByTile(tile);
 
		SetSignalsOnBothDir(tile, track);
 
		YapfNotifyTrackLayoutChange(tile, track);
 
	}
 

	
 
	return cost + _price.build_rail;
 
}
 

	
 
/** Remove a single piece of track
 
 * @param tile tile to remove track from
 
 * @param p1 unused
 
 * @param p2 rail orientation
 
 */
 
int32 CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Track track = (Track)p2;
 
	TrackBits trackbit;
 
	int32 cost = _price.remove_rail;
 
	bool crossing = false;
 

	
 
	if (!ValParamTrackOrientation(p2)) return CMD_ERROR;
 
	trackbit = TrackToTrackBits(track);
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	switch (GetTileType(tile)) {
 
		case MP_STREET: {
 
			if (!IsLevelCrossing(tile) ||
 
					GetCrossingRailBits(tile) != trackbit ||
 
					(_current_player != OWNER_WATER && !CheckTileOwnership(tile)) ||
 
					!EnsureNoVehicle(tile)) {
 
				return CMD_ERROR;
 
			}
 

	
 
			if (flags & DC_EXEC) {
 
				MakeRoadNormal(tile, GetCrossingRoadOwner(tile), GetCrossingRoadBits(tile), GetTownIndex(tile));
 
			}
 
			break;
 
		}
 

	
 
		case MP_RAILWAY: {
 
			TrackBits present;
 

	
 
			if (!IsPlainRailTile(tile) ||
 
					(_current_player != OWNER_WATER && !CheckTileOwnership(tile)) ||
 
					!EnsureNoVehicle(tile)) {
 
				return CMD_ERROR;
 
			}
 

	
 
			present = GetTrackBits(tile);
 
			if ((present & trackbit) == 0) return CMD_ERROR;
 
			if (present == (TRACK_BIT_X | TRACK_BIT_Y)) crossing = true;
 

	
 
			/* Charge extra to remove signals on the track, if they are there */
 
			if (HasSignalOnTrack(tile, track))
 
				cost += DoCommand(tile, track, 0, flags, CMD_REMOVE_SIGNALS);
 

	
 
			if (flags & DC_EXEC) {
 
				present ^= trackbit;
 
				if (present == 0) {
 
					DoClearSquare(tile);
 
				} else {
 
					SetTrackBits(tile, present);
 
				}
 
			}
 
			break;
 
		}
 

	
 
		default: return CMD_ERROR;
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		MarkTileDirtyByTile(tile);
 
		if (crossing) {
 
			/* crossing is set when only TRACK_BIT_X and TRACK_BIT_Y are set. As we
 
			 * are removing one of these pieces, we'll need to update signals for
 
			 * both directions explicitly, as after the track is removed it won't
 
			 * 'connect' with the other piece. */
 
			SetSignalsOnBothDir(tile, TRACK_X);
 
			SetSignalsOnBothDir(tile, TRACK_Y);
 
			YapfNotifyTrackLayoutChange(tile, TRACK_X);
 
			YapfNotifyTrackLayoutChange(tile, TRACK_Y);
 
		} else {
 
			SetSignalsOnBothDir(tile, track);
 
			YapfNotifyTrackLayoutChange(tile, track);
 
		}
 
	}
 

	
 
	return cost;
 
}
 

	
 

	
 
static const TileIndexDiffC _trackdelta[] = {
 
	{ -1,  0 }, {  0,  1 }, { -1,  0 }, {  0,  1 }, {  1,  0 }, {  0,  1 },
 
	{  0,  0 },
 
	{  0,  0 },
 
	{  1,  0 }, {  0, -1 }, {  0, -1 }, {  1,  0 }, {  0, -1 }, { -1,  0 },
 
	{  0,  0 },
 
	{  0,  0 }
 
};
 

	
 

	
 
static int32 ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileIndex end)
 
{
 
	int x = TileX(start);
 
	int y = TileY(start);
 
	int ex = TileX(end);
 
	int ey = TileY(end);
 
	int dx, dy, trdx, trdy;
 

	
 
	if (!ValParamTrackOrientation(*trackdir)) return CMD_ERROR;
 

	
 
	// calculate delta x,y from start to end tile
 
	dx = ex - x;
 
	dy = ey - y;
 

	
 
	// calculate delta x,y for the first direction
 
	trdx = _trackdelta[*trackdir].x;
 
	trdy = _trackdelta[*trackdir].y;
 

	
 
	if (!IsDiagonalTrackdir(*trackdir)) {
 
		trdx += _trackdelta[*trackdir ^ 1].x;
 
		trdy += _trackdelta[*trackdir ^ 1].y;
 
	}
 

	
 
	// validate the direction
 
	while (
 
		(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
 
			SETBIT(*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)) trackdir ^= 1;
 
	}
 

	
 
	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)
 
 *
 
 * @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);
 

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

	
 
	tileh = GetTileSlope(tile, NULL);
 

	
 
	/* 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)
 
			)) {
 
		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) {
 
		MakeRailDepot(tile, _current_player, p2, p1);
 
		MarkTileDirtyByTile(tile);
 

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

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

	
 
	return cost + _price.build_train_depot;
 
}
 

	
 
/** Build signals, alternate between double/single, signal/semaphore,
 
 * pre/exit/combo-signals, and what-else not
 
 * @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)   - choose semaphores/signals or cycle normal/pre/exit/combo depending on context
 
 * @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)
 
{
 
	SignalVariant sigvar;
 
	bool pre_signal;
 
	Track track = (Track)(p1 & 0x7);
 
	int32 cost;
 

	
 
	// Same bit, used in different contexts
 
	sigvar = HASBIT(p1, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC;
 
	pre_signal = HASBIT(p1, 3);
 

	
 
	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 : 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 */
 
			_m[tile].m3 &= ~SignalOnTrack(track);
 
			_m[tile].m3 |= p2 & SignalOnTrack(track);
 
			SetSignalVariant(tile, sigvar);
 
		}
 

	
 
		MarkTileDirtyByTile(tile);
 
		SetSignalsOnBothDir(tile, track);
 
		YapfNotifyTrackLayoutChange(tile, track);
 
	}
 

	
 
	return cost;
 
}
 

	
 
/** Build many signals by dragging; AutoSignals
 
 * @param tile start tile of drag
 
 * @param p1  end tile of drag
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit  0)    - 0 = build, 1 = remove signals
 
 * - p2 = (bit  3)    - 0 = signals, 1 = semaphores
 
 * - p2 = (bit  4- 6) - track-orientation, valid values: 0-5 (Track enum)
 
 * - p2 = (bit 24-31) - user defined signals_density
 
 */
 
static int32 CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 ret, total_cost, signal_ctr;
 
	byte signals;
 
	bool error = true;
 
	TileIndex end_tile;
 

	
 
	int mode = p2 & 0x1;
 
	Track track = GB(p2, 4, 3);
 
	Trackdir trackdir = TrackToTrackdir(track);
 
	byte semaphores = (HASBIT(p2, 3) ? 8 : 0);
 
	byte signal_density = (p2 >> 24);
 

	
 
	if (p1 >= MapSize()) return CMD_ERROR;
 
	end_tile = p1;
 
	if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
 

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

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* for vertical/horizontal tracks, double the given signals density
 
	 * since the original amount will be too dense (shorter tracks) */
 
	if (!IsDiagonalTrack(track)) signal_density *= 2;
 

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

	
 
	track = TrackdirToTrack(trackdir); /* trackdir might have changed, keep track in sync */
 

	
 
	// copy the signal-style of the first rail-piece if existing
 
	if (HasSignals(tile)) {
 
		signals = _m[tile].m3 & SignalOnTrack(track);
 
		if (signals == 0) signals = SignalOnTrack(track); /* Can this actually occur? */
 

	
 
		// copy signal/semaphores style (independent of CTRL)
 
		semaphores = (GetSignalVariant(tile) == SIG_ELECTRIC ? 0 : 8);
 
	} else { // no signals exist, drag a two-way signal stretch
 
		signals = SignalOnTrack(track);
 
	}
 

	
 
	/* signal_ctr         - amount of tiles already processed
 
	 * signals_density    - patch setting to put signal on every Nth tile (double space on |, -- tracks)
 
	 **********
 
	 * trackdir   - trackdir to build with autorail
 
	 * semaphores - semaphores or signals
 
	 * signals    - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
 
	 *              and convert all others to semaphore/signal
 
	 * mode       - 1 remove signals, 0 build signals */
 
	signal_ctr = total_cost = 0;
 
	for (;;) {
 
		// only build/remove signals with the specified density
 
		if (signal_ctr % signal_density == 0) {
 
			ret = DoCommand(tile, TrackdirToTrack(trackdir) | semaphores, signals, flags, (mode == 1) ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS);
 

	
 
			/* Be user-friendly and try placing signals as much as possible */
 
			if (!CmdFailed(ret)) {
 
				error = false;
 
				total_cost += ret;
 
			}
 
		}
 

	
 
		if (tile == end_tile) break;
 

	
 
		tile += ToTileIndexDiff(_trackdelta[trackdir]);
 
		signal_ctr++;
 

	
 
		// toggle railbit for the non-diagonal tracks (|, -- tracks)
 
		if (!IsDiagonalTrackdir(trackdir)) trackdir ^= 1;
 
	}
 

	
 
	return error ? CMD_ERROR : total_cost;
 
}
 

	
 
/** Build signals on a stretch of track.
 
 * Stub for the unified signal builder/remover
 
 * @see CmdSignalTrackHelper
 
 */
 
int32 CmdBuildSignalTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	return CmdSignalTrackHelper(tile, flags, p1, p2);
 
}
 

	
 
/** Remove signals
 
 * @param tile coordinates where signal is being deleted from
 
 * @param p1 track to remove signal from (Track enum)
 
 */
 
int32 CmdRemoveSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Track track = (Track)(p1 & 0x7);
 

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

	
 
	/* Only water can remove signals from anyone */
 
	if (_current_player != OWNER_WATER && !CheckTileOwnership(tile)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* Do it? */
 
	if (flags & DC_EXEC) {
 
		_m[tile].m3 &= ~SignalOnTrack(track);
 

	
 
		/* removed last signal from tile? */
 
		if (GB(_m[tile].m3, 4, 4) == 0) {
 
			SB(_m[tile].m2, 4, 4, 0);
 
			SB(_m[tile].m5, 6, 2, RAIL_TILE_NORMAL >> 6); // XXX >> because the constant is meant for direct application, not use with SB
 
			SetSignalVariant(tile, SIG_ELECTRIC); // remove any possible semaphores
 
		}
 

	
 
		SetSignalsOnBothDir(tile, track);
 
		YapfNotifyTrackLayoutChange(tile, track);
 

	
 
		MarkTileDirtyByTile(tile);
 
	}
 

	
 
	return _price.remove_signals;
 
}
 

	
 
/** Remove signals on a stretch of track.
 
 * Stub for the unified signal builder/remover
 
 * @see CmdSignalTrackHelper
 
 */
 
int32 CmdRemoveSignalTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	return CmdSignalTrackHelper(tile, flags, p1, SETBIT(p2, 0));
 
}
 

	
 
typedef int32 DoConvertRailProc(TileIndex tile, RailType totype, bool exec);
 

	
 
static int32 DoConvertRail(TileIndex tile, RailType totype, bool exec)
 
{
 
	if (!CheckTileOwnership(tile)) return CMD_ERROR;
 

	
 
	if (!EnsureNoVehicle(tile) && (!IsCompatibleRail(GetRailType(tile), totype) || IsPlainRailTile(tile))) return CMD_ERROR;
 

	
 
	// tile is already of requested type?
 
	if (GetRailType(tile) == totype) return CMD_ERROR;
 

	
 
	// 'hidden' elrails can't be downgraded to normal rail when elrails are disabled
 
	if (_patches.disable_elrails && totype == RAILTYPE_RAIL && GetRailType(tile) == RAILTYPE_ELECTRIC) return CMD_ERROR;
 

	
 
	// change type.
 
	if (exec) {
 
		TrackBits tracks;
 
		SetRailType(tile, totype);
 
		MarkTileDirtyByTile(tile);
 

	
 
		// notify YAPF about the track layout change
 
		for (tracks = GetTrackBits(tile); tracks != TRACK_BIT_NONE; tracks = KILL_FIRST_BIT(tracks))
 
			YapfNotifyTrackLayoutChange(tile, FIND_FIRST_BIT(tracks));
 

	
 
		if (IsTileDepotType(tile, TRANSPORT_RAIL)) {
 
			Vehicle *v;
 

	
 
			/* Update build vehicle window related to this depot */
 
			InvalidateWindowData(WC_BUILD_VEHICLE, tile);
 

	
 
			/* update power of trains in this depot */
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->type == VEH_Train && IsFrontEngine(v) && v->tile == tile && v->u.rail.track == 0x80) {
 
					TrainPowerChanged(v);
 
				}
 
			}
 
		}
 
	}
 

	
 
	return _price.build_rail / 2;
 
}
 

	
 
extern int32 DoConvertStationRail(TileIndex tile, RailType totype, bool exec);
 
extern int32 DoConvertStreetRail(TileIndex tile, RailType totype, bool exec);
 
extern int32 DoConvertTunnelBridgeRail(TileIndex tile, RailType totype, bool exec);
 

	
 
/** Convert one rail type to the other. You can convert normal rail to
 
 * monorail/maglev easily or vice-versa.
 
 * @param tile end tile of rail conversion drag
 
 * @param p1 start tile of drag
 
 * @param p2 new railtype to convert to
 
 */
 
int32 CmdConvertRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 ret, cost, money;
 
	int ex;
 
	int ey;
 
	int sx, sy, x, y;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!ValParamRailtype(p2)) return CMD_ERROR;
 
	if (p1 >= MapSize()) return CMD_ERROR;
 

	
 
	// make sure sx,sy are smaller than ex,ey
 
	ex = TileX(tile);
 
	ey = TileY(tile);
 
	sx = TileX(p1);
 
	sy = TileY(p1);
 
	if (ex < sx) intswap(ex, sx);
 
	if (ey < sy) intswap(ey, sy);
 

	
 
	money = GetAvailableMoneyForCommand();
 
	cost = 0;
 
	ret = 0;
 

	
 
	for (x = sx; x <= ex; ++x) {
 
		for (y = sy; y <= ey; ++y) {
 
			TileIndex tile = TileXY(x, y);
 
			DoConvertRailProc* proc;
 

	
 
			switch (GetTileType(tile)) {
 
				case MP_RAILWAY:      proc = DoConvertRail;             break;
 
				case MP_STATION:      proc = DoConvertStationRail;      break;
 
				case MP_STREET:       proc = DoConvertStreetRail;       break;
 
				case MP_TUNNELBRIDGE: proc = DoConvertTunnelBridgeRail; break;
 
				default: continue;
 
			}
 

	
 
			ret = proc(tile, p2, false);
 
			if (CmdFailed(ret)) continue;
 
			cost += ret;
 

	
 
			if (flags & DC_EXEC) {
 
				money -= ret;
 
				if (money < 0) {
 
					_additional_cash_required = ret;
 
					return cost - ret;
 
				}
 
				proc(tile, p2, true);
 
			}
 
		}
 
	}
 

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

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

	
 
	if (!EnsureNoVehicle(tile))
 
		return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		DiagDirection dir = GetRailDepotDirection(tile);
 

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

	
 
	return _price.remove_train_depot;
 
}
 

	
 
static int32 ClearTile_Track(TileIndex tile, byte flags)
 
{
 
	int32 cost;
 
	int32 ret;
 

	
 
	if (flags & DC_AUTO) {
 
		if (!IsTileOwner(tile, _current_player))
 
			return_cmd_error(STR_1024_AREA_IS_OWNED_BY_ANOTHER);
 

	
 
		if (IsPlainRailTile(tile)) {
 
			return_cmd_error(STR_1008_MUST_REMOVE_RAILROAD_TRACK);
 
		} else {
 
			return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);
 
		}
 
	}
 

	
 
	cost = 0;
 

	
 
	switch (GetRailTileType(tile)) {
 
		case RAIL_TILE_SIGNALS:
 
		case RAIL_TILE_NORMAL: {
 
			TrackBits tracks = GetTrackBits(tile);
 
			uint i;
 

	
 
			for_each_bit (i, tracks) {
 
				ret = DoCommand(tile, 0, i, flags, CMD_REMOVE_SINGLE_RAIL);
 
				if (CmdFailed(ret)) return CMD_ERROR;
 
				cost += ret;
 
			}
 
			return cost;
 
		}
 

	
 
		case RAIL_TILE_DEPOT_WAYPOINT:
 
			if (GetRailTileSubtype(tile) == RAIL_SUBTYPE_DEPOT) {
 
				return RemoveTrainDepot(tile, flags);
 
			} else {
 
				return RemoveTrainWaypoint(tile, flags, false);
 
			}
 

	
 
		default:
 
			return CMD_ERROR;
 
	}
 
}
 

	
 
#include "table/track_land.h"
 

	
 
static void DrawSingleSignal(TileIndex tile, byte condition, uint image, uint pos)
 
{
 
	bool side = _opt.road_side & _patches.signal_side;
 
	static const Point SignalPositions[2][12] = {
 
		{      /* Signals on the left side */
 
		/*  LEFT      LEFT      RIGHT     RIGHT     UPPER     UPPER */
 
			{ 8,  5}, {14,  1}, { 1, 14}, { 9, 11}, { 1,  0}, { 3, 10},
 
		/*  LOWER     LOWER     X         X         Y         Y     */
 
			{11,  4}, {14, 14}, {11,  3}, { 4, 13}, { 3,  4}, {11, 13}
 
		}, {   /* Signals on the right side */
 
		/*  LEFT      LEFT      RIGHT     RIGHT     UPPER     UPPER */
 
			{14,  1}, {12, 10}, { 4,  6}, { 1, 14}, {10,  4}, { 0,  1},
 
		/*  LOWER     LOWER     X         X         Y         Y     */
 
			{14, 14}, { 5, 12}, {11, 13}, { 4,  3}, {13,  4}, { 3, 11}
 
		}
 
	};
 

	
 
	static const SpriteID SignalBase[2][2][4] = {
 
		{    /* Signals on left side */
 
			{  0x4FB, 0x1323, 0x1333, 0x1343}, /* light signals */
 
			{ 0x1353, 0x1363, 0x1373, 0x1383}  /* semaphores    */
 
		}, { /* Signals on right side */
 
			{  0x4FB, 0x1323, 0x1333, 0x1343}, /* light signals */
 
			{ 0x1446, 0x1456, 0x1466, 0x1476}  /* semaphores    */
 
		/*         |       |       |       |     */
 
		/*    normal,  entry,   exit,  combo     */
 
		}
 
	};
 

	
 
	uint x = TileX(tile) * TILE_SIZE + SignalPositions[side][pos].x;
 
	uint y = TileY(tile) * TILE_SIZE + SignalPositions[side][pos].y;
 

	
 
	SpriteID sprite;
 

	
 
	/* _signal_base is set by our NewGRF Action 5 loader. If it is 0 then we
 
	 * just draw the standard signals, else we get the offset from _signal_base
 
	 * and draw that sprite. All the signal sprites are loaded sequentially. */
 
	if (_signal_base == 0 || (GetSignalType(tile) == 0 && GetSignalVariant(tile) == SIG_ELECTRIC)) {
 
		sprite = SignalBase[side][GetSignalVariant(tile)][GetSignalType(tile)] + image + condition;
 
	} else {
 
		sprite = _signal_base + (GetSignalType(tile) - 1) * 16 + GetSignalVariant(tile) * 64 + image + condition;
 
	}
 

	
 
	AddSortableSpriteToDraw(sprite, x, y, 1, 1, 10, GetSlopeZ(x,y));
 
}
 

	
 
static uint32 _drawtile_track_palette;
 

	
 

	
 
static void DrawTrackFence_NW(const TileInfo *ti)
 
{
 
	uint32 image = 0x515;
 
	if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? 0x519 : 0x51B;
 
	AddSortableSpriteToDraw(image | _drawtile_track_palette,
 
		ti->x, ti->y + 1, 16, 1, 4, ti->z);
 
}
 

	
 
static void DrawTrackFence_SE(const TileInfo *ti)
 
{
 
	uint32 image = 0x515;
 
	if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? 0x519 : 0x51B;
 
	AddSortableSpriteToDraw(image | _drawtile_track_palette,
 
		ti->x, ti->y + TILE_SIZE - 1, 16, 1, 4, ti->z);
 
}
 

	
 
static void DrawTrackFence_NW_SE(const TileInfo *ti)
 
{
 
	DrawTrackFence_NW(ti);
 
	DrawTrackFence_SE(ti);
 
}
 

	
 
static void DrawTrackFence_NE(const TileInfo *ti)
 
{
 
	uint32 image = 0x516;
 
	if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? 0x51A : 0x51C;
 
	AddSortableSpriteToDraw(image | _drawtile_track_palette,
 
		ti->x + 1, ti->y, 1, 16, 4, ti->z);
 
}
 

	
 
static void DrawTrackFence_SW(const TileInfo *ti)
 
{
 
	uint32 image = 0x516;
 
	if (ti->tileh != SLOPE_FLAT) image = (ti->tileh & SLOPE_S) ? 0x51A : 0x51C;
 
	AddSortableSpriteToDraw(image | _drawtile_track_palette,
 
		ti->x + TILE_SIZE - 1, ti->y, 1, 16, 4, ti->z);
 
}
 

	
 
static void DrawTrackFence_NE_SW(const TileInfo *ti)
 
{
 
	DrawTrackFence_NE(ti);
 
	DrawTrackFence_SW(ti);
 
}
 

	
 
static void DrawTrackFence_NS_1(const TileInfo *ti)
 
{
 
	int z = ti->z;
 
	if (ti->tileh & SLOPE_W) z += TILE_HEIGHT;
 
	AddSortableSpriteToDraw(0x517 | _drawtile_track_palette,
 
		ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z);
 
}
 

	
 
static void DrawTrackFence_NS_2(const TileInfo *ti)
 
{
 
	int z = ti->z;
 
	if (ti->tileh & SLOPE_E) z += TILE_HEIGHT;
 
	AddSortableSpriteToDraw(0x517 | _drawtile_track_palette,
 
		ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z);
 
}
 

	
 
static void DrawTrackFence_WE_1(const TileInfo *ti)
 
{
 
	int z = ti->z;
 
	if (ti->tileh & SLOPE_N) z += TILE_HEIGHT;
 
	AddSortableSpriteToDraw(0x518 | _drawtile_track_palette,
 
		ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z);
 
}
 

	
 
static void DrawTrackFence_WE_2(const TileInfo *ti)
 
{
 
	int z = ti->z;
 
	if (ti->tileh & SLOPE_S) z += TILE_HEIGHT;
 
	AddSortableSpriteToDraw(0x518 | _drawtile_track_palette,
 
		ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 4, z);
 
}
 

	
 

	
 
static void DrawTrackDetails(const TileInfo* ti)
 
{
 
	switch (GetRailGroundType(ti->tile)) {
 
		case RAIL_GROUND_FENCE_NW:     DrawTrackFence_NW(ti);    break;
 
		case RAIL_GROUND_FENCE_SE:     DrawTrackFence_SE(ti);    break;
 
		case RAIL_GROUND_FENCE_SENW:   DrawTrackFence_NW_SE(ti); break;
 
		case RAIL_GROUND_FENCE_NE:     DrawTrackFence_NE(ti);    break;
 
		case RAIL_GROUND_FENCE_SW:     DrawTrackFence_SW(ti);    break;
 
		case RAIL_GROUND_FENCE_NESW:   DrawTrackFence_NE_SW(ti); break;
 
		case RAIL_GROUND_FENCE_VERT1:  DrawTrackFence_NS_1(ti);  break;
 
		case RAIL_GROUND_FENCE_VERT2:  DrawTrackFence_NS_2(ti);  break;
 
		case RAIL_GROUND_FENCE_HORIZ1: DrawTrackFence_WE_1(ti);  break;
 
		case RAIL_GROUND_FENCE_HORIZ2: DrawTrackFence_WE_2(ti);  break;
 
		default: break;
 
	}
 
}
 

	
 

	
 
/**
 
 * Draw ground sprite and track bits
 
 * @param ti TileInfo
 
 * @param track TrackBits to draw
 
 * @param earth Draw as earth
 
 * @param snow Draw as snow
 
 * @param flat Always draw foundation
 
 */
 
static void DrawTrackBits(TileInfo* ti, TrackBits track)
 
{
 
	const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
 
	PalSpriteID image;
 
	bool junction = false;
 

	
 
	// Select the sprite to use.
 
	(image = rti->base_sprites.track_y, track == TRACK_BIT_Y) ||
 
	(image++,                           track == TRACK_BIT_X) ||
 
	(image++,                           track == TRACK_BIT_UPPER) ||
 
	(image++,                           track == TRACK_BIT_LOWER) ||
 
	(image++,                           track == TRACK_BIT_RIGHT) ||
 
	(image++,                           track == TRACK_BIT_LEFT) ||
 
	(image++,                           track == TRACK_BIT_CROSS) ||
 

	
 
	(image = rti->base_sprites.track_ns, track == TRACK_BIT_HORZ) ||
 
	(image++,                            track == TRACK_BIT_VERT) ||
 

	
 
	(junction = true, false) ||
 
	(image = rti->base_sprites.ground, (track & TRACK_BIT_3WAY_NE) == 0) ||
 
	(image++,                          (track & TRACK_BIT_3WAY_SW) == 0) ||
 
	(image++,                          (track & TRACK_BIT_3WAY_NW) == 0) ||
 
	(image++,                          (track & TRACK_BIT_3WAY_SE) == 0) ||
 
	(image++, true);
 

	
 
	if (ti->tileh != SLOPE_FLAT) {
 
		uint foundation = GetRailFoundation(ti->tileh, track);
 

	
 
		if (foundation != 0) DrawFoundation(ti, foundation);
 

	
 
		// DrawFoundation() modifies ti.
 
		// Default sloped sprites..
 
		if (ti->tileh != SLOPE_FLAT)
 
			image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y;
 
	}
 

	
 
	switch (GetRailGroundType(ti->tile)) {
 
		case RAIL_GROUND_BARREN:     image |= PALETTE_TO_BARE_LAND; break;
 
		case RAIL_GROUND_ICE_DESERT: image += rti->snow_offset; break;
 
		default: break;
 
	}
 

	
 
	DrawGroundSprite(image);
 

	
 
	// Draw track pieces individually for junction tiles
 
	if (junction) {
 
		if (track & TRACK_BIT_X)     DrawGroundSprite(rti->base_sprites.single_y);
 
		if (track & TRACK_BIT_Y)     DrawGroundSprite(rti->base_sprites.single_x);
 
		if (track & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n);
 
		if (track & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s);
 
		if (track & TRACK_BIT_LEFT)  DrawGroundSprite(rti->base_sprites.single_w);
 
		if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e);
 
	}
 

	
 
	if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti);
 

	
 
}
 

	
 
static void DrawSignals(TileIndex tile, TrackBits rails)
 
{
 
#define MAYBE_DRAW_SIGNAL(x,y,z) if (IsSignalPresent(tile, x)) DrawSingleSignal(tile, GetSingleSignalState(tile, x), y - 0x4FB, z)
 

	
 
	if (!(rails & TRACK_BIT_Y)) {
 
		if (!(rails & TRACK_BIT_X)) {
 
			if (rails & TRACK_BIT_LEFT) {
 
				MAYBE_DRAW_SIGNAL(2, 0x509, 0);
 
				MAYBE_DRAW_SIGNAL(3, 0x507, 1);
 
			}
 
			if (rails & TRACK_BIT_RIGHT) {
 
				MAYBE_DRAW_SIGNAL(0, 0x509, 2);
 
				MAYBE_DRAW_SIGNAL(1, 0x507, 3);
 
			}
 
			if (rails & TRACK_BIT_UPPER) {
 
				MAYBE_DRAW_SIGNAL(3, 0x505, 4);
 
				MAYBE_DRAW_SIGNAL(2, 0x503, 5);
 
			}
 
			if (rails & TRACK_BIT_LOWER) {
 
				MAYBE_DRAW_SIGNAL(1, 0x505, 6);
 
				MAYBE_DRAW_SIGNAL(0, 0x503, 7);
 
			}
 
		} else {
 
			MAYBE_DRAW_SIGNAL(3, 0x4FB, 8);
 
			MAYBE_DRAW_SIGNAL(2, 0x4FD, 9);
 
		}
 
	} else {
 
		MAYBE_DRAW_SIGNAL(3, 0x4FF, 10);
 
		MAYBE_DRAW_SIGNAL(2, 0x501, 11);
 
	}
 
}
 

	
 
static void DrawTile_Track(TileInfo *ti)
 
{
 
	const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
 
	PalSpriteID image;
 

	
 
	_drawtile_track_palette = SPRITE_PALETTE(PLAYER_SPRITE_COLOR(GetTileOwner(ti->tile)));
 

	
 
	if (IsPlainRailTile(ti->tile)) {
 
		TrackBits rails = GetTrackBits(ti->tile);
 

	
 
		DrawTrackBits(ti, rails);
 

	
 
		if (_display_opt & DO_FULL_DETAIL) DrawTrackDetails(ti);
 

	
 
		if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails);
 
	} else {
 
		// draw depot/waypoint
 
		const DrawTileSprites* dts;
 
		const DrawTileSeqStruct* dtss;
 
		uint32 relocation;
 

	
 
		if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh);
 

	
 
		if (GetRailTileSubtype(ti->tile) == RAIL_SUBTYPE_DEPOT) {
 
			dts = &_depot_gfx_table[GetRailDepotDirection(ti->tile)];
 

	
 
			relocation = rti->total_offset;
 

	
 
			image = dts->ground_sprite;
 
			if (image != SPR_FLAT_GRASS_TILE) image += rti->total_offset;
 

	
 
			// adjust ground tile for desert
 
			// don't adjust for snow, because snow in depots looks weird
 
			if (IsSnowRailGround(ti->tile) && _opt.landscape == LT_DESERT) {
 
				if (image != SPR_FLAT_GRASS_TILE) {
 
					image += rti->snow_offset; // tile with tracks
 
				} else {
 
					image = SPR_FLAT_SNOWY_TILE; // flat ground
 
				}
 
			}
 
		} else {
 
			// look for customization
 
			byte stat_id = GetWaypointByTile(ti->tile)->stat_id;
 
			const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, stat_id);
 

	
 
			if (statspec != NULL) {
 
				// emulate station tile - open with building
 
				const Station* st = ComposeWaypointStation(ti->tile);
 
				uint gfx = 2;
 

	
 
				if (HASBIT(statspec->callbackmask, CBM_CUSTOM_LAYOUT)) {
 
					uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
 
					if (callback != CALLBACK_FAILED) gfx = callback;
 
				}
 

	
 
				if (statspec->renderdata == NULL) {
 
					dts = GetStationTileLayout(gfx);
 
				} else {
 
					dts = &statspec->renderdata[(gfx < statspec->tiles ? gfx : 0) + GetWaypointAxis(ti->tile)];
 
				}
 

	
 
				if (dts != NULL && dts->seq != NULL) {
 
					relocation = GetCustomStationRelocation(statspec, st, ti->tile);
 

	
 
					image = dts->ground_sprite;
 
					if (HASBIT(image, 31)) {
 
						CLRBIT(image, 31);
 
						image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
 
						image += rti->custom_ground_offset;
 
					} else {
 
						image += rti->total_offset;
 
					}
 
				} else {
 
					goto default_waypoint;
 
				}
 
			} else {
 
default_waypoint:
 
				// There is no custom layout, fall back to the default graphics
 
				dts = &_waypoint_gfx_table[GetWaypointAxis(ti->tile)];
 
				relocation = 0;
 
				image = dts->ground_sprite + rti->total_offset;
 
				if (IsSnowRailGround(ti->tile)) image += rti->snow_offset;
 
			}
 
		}
 

	
 
		DrawGroundSprite(image);
 

	
 
		if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti);
 

	
 
		foreach_draw_tile_seq(dtss, dts->seq) {
 
			uint32 image = dtss->image + relocation;
 

	
 
			if (_display_opt & DO_TRANS_BUILDINGS) {
 
				MAKE_TRANSPARENT(image);
 
			} else if (image & PALETTE_MODIFIER_COLOR) {
 
				image |= _drawtile_track_palette;
 
			}
 
			AddSortableSpriteToDraw(
 
				image,
 
				ti->x + dtss->delta_x, ti->y + dtss->delta_y,
 
				dtss->size_x, dtss->size_y,
 
				dtss->size_z, ti->z + dtss->delta_z
 
			);
 
		}
 
	}
 
	DrawBridgeMiddle(ti);
 
}
 

	
 

	
 
static void DrawTileSequence(int x, int y, uint32 ground, const DrawTileSeqStruct* dtss, uint32 offset)
 
{
 
	uint32 palette = PLAYER_SPRITE_COLOR(_local_player);
 

	
 
	DrawSprite(ground, x, y);
 
	for (; dtss->image != 0; dtss++) {
 
		Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z);
 
		uint32 image = dtss->image + offset;
 

	
 
		if (image & PALETTE_MODIFIER_COLOR) image |= palette;
 
		DrawSprite(image, x + pt.x, y + pt.y);
 
	}
 
}
 

	
 
void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype)
 
{
 
	const DrawTileSprites* dts = &_depot_gfx_table[dir];
 
	uint32 image = dts->ground_sprite;
 
	uint32 offset = GetRailTypeInfo(railtype)->total_offset;
 

	
 
	if (image != SPR_FLAT_GRASS_TILE) image += offset;
 
	DrawTileSequence(x + 33, y + 17, image, dts->seq, offset);
 
}
 

	
 
void DrawDefaultWaypointSprite(int x, int y, RailType railtype)
 
{
 
	uint32 offset = GetRailTypeInfo(railtype)->total_offset;
 
	const DrawTileSprites* dts = &_waypoint_gfx_table[AXIS_X];
 

	
 
	DrawTileSequence(x, y, dts->ground_sprite + offset, dts->seq, 0);
 
}
 

	
 
typedef struct SetSignalsData {
 
	int cur;
 
	int cur_stack;
 
	bool stop;
 
	bool has_presignal;
 

	
 
	// presignal info
 
	int presignal_exits;
 
	int presignal_exits_free;
 

	
 
	// these are used to keep track of the signals that change.
 
	byte bit[NUM_SSD_ENTRY];
 
	TileIndex tile[NUM_SSD_ENTRY];
 

	
 
	// these are used to keep track of the stack that modifies presignals recursively
 
	TileIndex next_tile[NUM_SSD_STACK];
 
	byte next_dir[NUM_SSD_STACK];
 

	
 
} SetSignalsData;
 

	
 
static bool SetSignalsEnumProc(TileIndex tile, void* data, int track, uint length, byte* state)
 
{
 
	SetSignalsData* ssd = data;
 

	
 
	if (!IsTileType(tile, MP_RAILWAY)) return false;
 

	
 
	// the tile has signals?
 
	if (HasSignalOnTrack(tile, TrackdirToTrack(track))) {
 
		if (HasSignalOnTrackdir(tile, ReverseTrackdir(track))) {
 
			// yes, add the signal to the list of signals
 
			if (ssd->cur != NUM_SSD_ENTRY) {
 
				ssd->tile[ssd->cur] = tile; // remember the tile index
 
				ssd->bit[ssd->cur] = track; // and the controlling bit number
 
				ssd->cur++;
 
			}
 

	
 
			// remember if this block has a presignal.
 
			ssd->has_presignal |= IsPresignalEntry(tile);
 
		}
 

	
 
		if (HasSignalOnTrackdir(tile, track) && IsPresignalExit(tile)) {
 
			// this is an exit signal that points out from the segment
 
			ssd->presignal_exits++;
 
			if (GetSignalStateByTrackdir(tile, track) != SIGNAL_STATE_RED)
 
				ssd->presignal_exits_free++;
 
		}
 

	
 
		return true;
 
	} else if (IsTileDepotType(tile, TRANSPORT_RAIL)) {
 
		return true; // don't look further if the tile is a depot
 
	}
 

	
 
	return false;
 
}
 

	
 
/* Struct to parse data from VehicleFromPos to SignalVehicleCheckProc */
 
typedef struct SignalVehicleCheckStruct {
 
	TileIndex tile;
 
	uint track;
 
} SignalVehicleCheckStruct;
 

	
 
static void *SignalVehicleCheckProc(Vehicle *v, void *data)
 
{
 
	const SignalVehicleCheckStruct* dest = data;
 

	
 
	if (v->type != VEH_Train) return NULL;
 

	
 
	/* Wrong tile, or no train? Not a match */
 
	if (v->tile != dest->tile) return NULL;
 

	
 
	/* Are we on the same piece of track? */
 
	if (dest->track & v->u.rail.track * 0x101) return v;
 

	
 
	return NULL;
 
}
 

	
 
/* Special check for SetSignalsAfterProc, to see if there is a vehicle on this tile */
 
static bool SignalVehicleCheck(TileIndex tile, uint track)
 
{
 
	SignalVehicleCheckStruct dest;
 

	
 
	dest.tile = tile;
 
	dest.track = track;
 

	
 
	/* Locate vehicles in tunnels or on bridges */
 
	if (IsTunnelTile(tile) || IsBridgeTile(tile)) {
 
		TileIndex end;
 
		DiagDirection direction;
 

	
 
		if (IsTunnelTile(tile)) {
 
			end = GetOtherTunnelEnd(tile);
 
			direction = GetTunnelDirection(tile);
 
		} else {
 
			end = GetOtherBridgeEnd(tile);
 
			direction = GetBridgeRampDirection(tile);
 
		}
 

	
 
		dest.track = 1 << (direction & 1); // get the trackbit the vehicle would have if it has not entered the tunnel yet (ie is still visible)
 

	
 
		// check for a vehicle with that trackdir on the start tile of the tunnel
 
		if (VehicleFromPos(tile, &dest, SignalVehicleCheckProc) != NULL) return true;
 

	
 
		// check for a vehicle with that trackdir on the end tile of the tunnel
 
		if (VehicleFromPos(end, &dest, SignalVehicleCheckProc) != NULL) return true;
 

	
 
		// now check all tiles from start to end for a warping vehicle
 
		// NOTE: the hashes for tiles may overlap, so this could maybe be optimised a bit by not checking every tile?
 
		dest.track = 0x40;   //Vehicle inside a tunnel or on a bridge
 
		for (; tile != end; tile += TileOffsByDiagDir(direction)) {
 
			if (VehicleFromPos(tile, &dest, SignalVehicleCheckProc) != NULL)
 
				return true;
 
		}
 

	
 
		// no vehicle found
 
		return false;
 
	}
 

	
 
	return VehicleFromPos(tile, &dest, SignalVehicleCheckProc) != NULL;
 
}
 

	
 
static void SetSignalsAfterProc(TrackPathFinder *tpf)
 
{
 
	SetSignalsData *ssd = tpf->userdata;
 
	const TrackPathFinderLink* link;
 
	uint offs;
 
	uint i;
 

	
 
	ssd->stop = false;
 

	
 
	/* Go through all the PF tiles */
 
	for (i = 0; i < lengthof(tpf->hash_head); i++) {
 
		/* Empty hash item */
 
		if (tpf->hash_head[i] == 0) continue;
 

	
 
		/* If 0x8000 is not set, there is only 1 item */
 
		if (!(tpf->hash_head[i] & 0x8000)) {
 
			/* Check if there is a vehicle on this tile */
 
			if (SignalVehicleCheck(tpf->hash_tile[i], tpf->hash_head[i])) {
 
				ssd->stop = true;
 
				return;
 
			}
 
		} else {
 
			/* There are multiple items, where hash_tile points to the first item in the list */
 
			offs = tpf->hash_tile[i];
 
			do {
 
				/* Find the next item */
 
				link = PATHFIND_GET_LINK_PTR(tpf, offs);
 
				/* Check if there is a vehicle on this tile */
 
				if (SignalVehicleCheck(link->tile, link->flags)) {
 
					ssd->stop = true;
 
					return;
 
				}
 
				/* Goto the next item */
 
			} while ((offs = link->next) != 0xFFFF);
 
		}
 
	}
 
}
 

	
 
static const byte _dir_from_track[14] = {
 
	0,1,0,1,2,1, 0,0,
 
	2,3,3,2,3,0,
 
};
 

	
 

	
 
static void ChangeSignalStates(SetSignalsData *ssd)
 
{
 
	int i;
 

	
 
	// thinking about presignals...
 
	// the presignal is green if,
 
	//   if no train is in the segment AND
 
	//   there is at least one green exit signal OR
 
	//   there are no exit signals in the segment
 

	
 
	// then mark the signals in the segment accordingly
 
	for (i = 0; i != ssd->cur; i++) {
 
		TileIndex tile = ssd->tile[i];
 
		byte bit = SignalAgainstTrackdir(ssd->bit[i]);
 
		uint16 m2 = _m[tile].m2;
 

	
 
		// presignals don't turn green if there is at least one presignal exit and none are free
 
		if (IsPresignalEntry(tile)) {
 
			int ex = ssd->presignal_exits, exfree = ssd->presignal_exits_free;
 

	
 
			// subtract for dual combo signals so they don't count themselves
 
			if (IsPresignalExit(tile) && HasSignalOnTrackdir(tile, ssd->bit[i])) {
 
				ex--;
 
				if (GetSignalStateByTrackdir(tile, ssd->bit[i]) != SIGNAL_STATE_RED) exfree--;
 
			}
 

	
 
			// if we have exits and none are free, make red.
 
			if (ex && !exfree) goto make_red;
 
		}
 

	
 
		// check if the signal is unaffected.
 
		if (ssd->stop) {
 
make_red:
 
			// turn red
 
			if ((bit & m2) == 0) continue;
 
		} else {
 
			// turn green
 
			if ((bit & m2) != 0) continue;
 
		}
 

	
 
		/* Update signals on the other side of this exit-combo signal; it changed. */
 
		if (IsPresignalExit(tile)) {
 
			if (ssd->cur_stack != NUM_SSD_STACK) {
 
				ssd->next_tile[ssd->cur_stack] = tile;
 
				ssd->next_dir[ssd->cur_stack] = _dir_from_track[ssd->bit[i]];
 
				ssd->cur_stack++;
 
			} else {
 
				DEBUG(misc, 0, "NUM_SSD_STACK too small"); /// @todo WTF is this???
 
			}
 
		}
 

	
 
		// it changed, so toggle it
 
		_m[tile].m2 = m2 ^ bit;
 
		MarkTileDirtyByTile(tile);
 
	}
 
}
 

	
 

	
 
bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection direction)
 
{
 
	SetSignalsData ssd;
 
	int result = -1;
 

	
 
	ssd.cur_stack = 0;
 

	
 
	for (;;) {
 
		// go through one segment and update all signals pointing into that segment.
 
		ssd.cur = ssd.presignal_exits = ssd.presignal_exits_free = 0;
 
		ssd.has_presignal = false;
 

	
 
		FollowTrack(tile, 0xC000 | TRANSPORT_RAIL, direction, SetSignalsEnumProc, SetSignalsAfterProc, &ssd);
 
		ChangeSignalStates(&ssd);
 

	
 
		// remember the result only for the first iteration.
 
		if (result < 0) {
 
			// stay in depot while segment is occupied or while all presignal exits are blocked
 
			result = ssd.stop || (ssd.presignal_exits > 0 && ssd.presignal_exits_free == 0);
 
		}
 

	
 
		// if any exit signals were changed, we need to keep going to modify the stuff behind those.
 
		if (ssd.cur_stack == 0) break;
 

	
 
		// one or more exit signals were changed, so we need to update another segment too.
 
		tile = ssd.next_tile[--ssd.cur_stack];
 
		direction = ssd.next_dir[ssd.cur_stack];
 
	}
 

	
 
	return result != 0;
 
}
 

	
 
void SetSignalsOnBothDir(TileIndex tile, byte track)
 
{
 
	static const DiagDirection _search_dir_1[] = {
 
		DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE
 
	};
 
	static const DiagDirection _search_dir_2[] = {
 
		DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
 
	};
 

	
 
	UpdateSignalsOnSegment(tile, _search_dir_1[track]);
 
	UpdateSignalsOnSegment(tile, _search_dir_2[track]);
 
}
 

	
 
static uint GetSlopeZ_Track(TileIndex tile, uint x, uint y)
 
{
 
	uint z;
 
	Slope tileh = GetTileSlope(tile, &z);
 

	
 
	if (tileh == SLOPE_FLAT) return z;
 
	if (IsPlainRailTile(tile)) {
 
		uint f = GetRailFoundation(tileh, GetTrackBits(tile));
 

	
 
		if (f != 0) {
 
			if (IsSteepSlope(tileh)) {
 
				z += TILE_HEIGHT;
 
			} else if (f < 15) {
 
				return z + TILE_HEIGHT; // leveled foundation
 
			}
 
			tileh = _inclined_tileh[f - 15]; // inclined foundation
 
		}
 
		return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
 
	} else {
 
		return z + TILE_HEIGHT;
 
	}
 
}
 

	
 
static Slope GetSlopeTileh_Track(TileIndex tile, Slope tileh)
 
{
 
	if (tileh == SLOPE_FLAT) return SLOPE_FLAT;
 
	if (IsPlainRailTile(tile)) {
 
		uint f = GetRailFoundation(tileh, GetTrackBits(tile));
 

	
 
		if (f == 0) return tileh;
 
		if (f < 15) return SLOPE_FLAT; // leveled foundation
 
		return _inclined_tileh[f - 15]; // inclined foundation
 
	} else {
 
		return SLOPE_FLAT;
 
	}
 
}
 

	
 
static void GetAcceptedCargo_Track(TileIndex tile, AcceptedCargo ac)
 
{
 
	/* not used */
 
}
 

	
 
static void AnimateTile_Track(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static void TileLoop_Track(TileIndex tile)
 
{
 
	RailGroundType old_ground = GetRailGroundType(tile);
 
	RailGroundType new_ground;
 

	
 
	switch (_opt.landscape) {
 
		case LT_HILLY:
 
			if (GetTileZ(tile) > _opt.snow_line) {
 
				new_ground = RAIL_GROUND_ICE_DESERT;
 
				goto set_ground;
 
			}
 
			break;
 

	
 
		case LT_DESERT:
 
			if (GetTropicZone(tile) == TROPICZONE_DESERT) {
 
				new_ground = RAIL_GROUND_ICE_DESERT;
 
				goto set_ground;
 
			}
 
			break;
 
	}
 

	
 
	if (!IsPlainRailTile(tile)) return;
 

	
 
	new_ground = RAIL_GROUND_GRASS;
 

	
 
	if (old_ground != RAIL_GROUND_BARREN) { /* wait until bottom is green */
 
		/* determine direction of fence */
 
		TrackBits rail = GetTrackBits(tile);
 

	
 
		switch (rail) {
 
			case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
 
			case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
 
			case TRACK_BIT_LEFT:  new_ground = RAIL_GROUND_FENCE_VERT1;  break;
 
			case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2;  break;
 

	
 
			default: {
 
				PlayerID owner = GetTileOwner(tile);
 

	
 
				if (rail == (TRACK_BIT_LOWER | TRACK_BIT_RIGHT) || (
 
							(rail & TRACK_BIT_3WAY_NW) == 0 &&
 
							(rail & TRACK_BIT_X)
 
						)) {
 
					TileIndex n = tile + TileDiffXY(0, -1);
 
					TrackBits nrail = GetTrackBits(n);
 

	
 
					if (!IsTileType(n, MP_RAILWAY) ||
 
							!IsTileOwner(n, owner) ||
 
							nrail == TRACK_BIT_UPPER ||
 
							nrail == TRACK_BIT_LEFT) {
 
						new_ground = RAIL_GROUND_FENCE_NW;
 
					}
 
				}
 

	
 
				if (rail == (TRACK_BIT_UPPER | TRACK_BIT_LEFT) || (
 
							(rail & TRACK_BIT_3WAY_SE) == 0 &&
 
							(rail & TRACK_BIT_X)
 
						)) {
 
					TileIndex n = tile + TileDiffXY(0, 1);
 
					TrackBits nrail = GetTrackBits(n);
 

	
 
					if (!IsTileType(n, MP_RAILWAY) ||
 
							!IsTileOwner(n, owner) ||
 
							nrail == TRACK_BIT_LOWER ||
 
							nrail == TRACK_BIT_RIGHT) {
 
						new_ground = (new_ground == RAIL_GROUND_FENCE_NW) ?
 
							RAIL_GROUND_FENCE_SENW : RAIL_GROUND_FENCE_SE;
 
					}
 
				}
 

	
 
				if (rail == (TRACK_BIT_LOWER | TRACK_BIT_LEFT) || (
 
							(rail & TRACK_BIT_3WAY_NE) == 0 &&
 
							(rail & TRACK_BIT_Y)
 
						)) {
 
					TileIndex n = tile + TileDiffXY(-1, 0);
 
					TrackBits nrail = GetTrackBits(n);
 

	
 
					if (!IsTileType(n, MP_RAILWAY) ||
 
							!IsTileOwner(n, owner) ||
 
							nrail == TRACK_BIT_UPPER ||
 
							nrail == TRACK_BIT_RIGHT) {
 
						new_ground = RAIL_GROUND_FENCE_NE;
 
					}
 
				}
 

	
 
				if (rail == (TRACK_BIT_UPPER | TRACK_BIT_RIGHT) || (
 
							(rail & TRACK_BIT_3WAY_SW) == 0 &&
 
							(rail & TRACK_BIT_Y)
 
						)) {
 
					TileIndex n = tile + TileDiffXY(1, 0);
 
					TrackBits nrail = GetTrackBits(n);
 

	
 
					if (!IsTileType(n, MP_RAILWAY) ||
 
							!IsTileOwner(n, owner) ||
 
							nrail == TRACK_BIT_LOWER ||
 
							nrail == TRACK_BIT_LEFT) {
 
						new_ground = (new_ground == RAIL_GROUND_FENCE_NE) ?
 
							RAIL_GROUND_FENCE_NESW : RAIL_GROUND_FENCE_SW;
 
					}
 
				}
 
				break;
 
			}
 
		}
 
	}
 

	
 
set_ground:
 
	if (old_ground != new_ground) {
 
		SetRailGroundType(tile, new_ground);
 
		MarkTileDirtyByTile(tile);
 
	}
 
}
 

	
 

	
 
static uint32 GetTileTrackStatus_Track(TileIndex tile, TransportType mode)
 
{
 
	byte a;
 
	uint16 b;
 

	
 
	if (mode != TRANSPORT_RAIL) return 0;
 

	
 
	if (IsPlainRailTile(tile)) {
 
		TrackBits rails = GetTrackBits(tile);
 
		uint32 ret = rails * 0x101;
 

	
 
		if (HasSignals(tile)) {
 
			a = _m[tile].m3;
 
			b = _m[tile].m2;
 

	
 
			b &= a;
 

	
 
			/* When signals are not present (in neither
 
			 * direction), we pretend them to be green. (So if
 
			 * signals are only one way, the other way will
 
			 * implicitely become `red' */
 
			if ((a & 0xC0) == 0) b |= 0xC0;
 
			if ((a & 0x30) == 0) b |= 0x30;
 

	
 
			if ((b & 0x80) == 0) ret |= 0x10070000;
 
			if ((b & 0x40) == 0) ret |= 0x07100000;
 
			if ((b & 0x20) == 0) ret |= 0x20080000;
 
			if ((b & 0x10) == 0) ret |= 0x08200000;
 
		} else {
 
			if (rails == TRACK_BIT_CROSS) ret |= 0x40;
 
		}
 
		return ret;
 
	} else {
 
		if (GetRailTileSubtype(tile) == RAIL_SUBTYPE_DEPOT) {
 
			return AxisToTrackBits(DiagDirToAxis(GetRailDepotDirection(tile))) * 0x101;
 
		} else {
 
			return GetRailWaypointBits(tile) * 0x101;
 
		}
 
	}
 
}
 

	
 
static void ClickTile_Track(TileIndex tile)
 
{
 
	if (IsTileDepotType(tile, TRANSPORT_RAIL)) {
 
		ShowDepotWindow(tile, VEH_Train);
 
	} else if (IsRailWaypoint(tile)) {
 
		ShowRenameWaypointWindow(GetWaypointByTile(tile));
 
	}
 
}
 

	
 
static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
 
{
 
	td->owner = GetTileOwner(tile);
 
	switch (GetRailTileType(tile)) {
 
		case RAIL_TILE_NORMAL:
 
			td->str = STR_1021_RAILROAD_TRACK;
 
			break;
 

	
 
		case RAIL_TILE_SIGNALS: {
 
			const StringID signal_type[] = {
 
				STR_RAILROAD_TRACK_WITH_NORMAL_SIGNALS,
 
				STR_RAILROAD_TRACK_WITH_PRESIGNALS,
 
				STR_RAILROAD_TRACK_WITH_EXITSIGNALS,
 
				STR_RAILROAD_TRACK_WITH_COMBOSIGNALS
 
			};
 

	
 
			td->str = signal_type[GetSignalType(tile)];
 
			break;
 
		}
 

	
 
		case RAIL_TILE_DEPOT_WAYPOINT:
 
		default:
 
			td->str = (GetRailTileSubtype(tile) == RAIL_SUBTYPE_DEPOT) ?
 
				STR_1023_RAILROAD_TRAIN_DEPOT : STR_LANDINFO_WAYPOINT;
 
			break;
 
	}
 
}
 

	
 
static void ChangeTileOwner_Track(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	if (!IsTileOwner(tile, old_player)) return;
 

	
 
	if (new_player != PLAYER_SPECTATOR) {
 
		SetTileOwner(tile, new_player);
 
	} else {
 
		DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
	}
 
}
 

	
 
static const byte _fractcoords_behind[4] = { 0x8F, 0x8, 0x80, 0xF8 };
 
static const byte _fractcoords_enter[4] = { 0x8A, 0x48, 0x84, 0xA8 };
 
static const byte _deltacoord_leaveoffset[8] = {
 
	-1,  0,  1,  0, /* x */
 
	 0,  1,  0, -1  /* y */
 
};
 

	
 
static uint32 VehicleEnter_Track(Vehicle *v, TileIndex tile, int x, int y)
 
{
 
	byte fract_coord;
 
	byte fract_coord_leave;
 
	DiagDirection dir;
 
	int length;
 

	
 
	// this routine applies only to trains in depot tiles
 
	if (v->type != VEH_Train || !IsTileDepotType(tile, TRANSPORT_RAIL)) return 0;
 

	
 
	/* depot direction */
 
	dir = GetRailDepotDirection(tile);
 

	
 
	/* calculate the point where the following wagon should be activated */
 
	/* this depends on the length of the current vehicle */
 
	length = v->u.rail.cached_veh_length;
 

	
 
	fract_coord_leave =
 
		((_fractcoords_enter[dir] & 0x0F) + // x
 
			(length + 1) * _deltacoord_leaveoffset[dir]) +
 
		(((_fractcoords_enter[dir] >> 4) +  // y
 
			((length + 1) * _deltacoord_leaveoffset[dir+4])) << 4);
 

	
 
	fract_coord = (x & 0xF) + ((y & 0xF) << 4);
 

	
 
	if (_fractcoords_behind[dir] == fract_coord) {
 
		/* make sure a train is not entering the tile from behind */
 
		return 8;
 
	} else if (_fractcoords_enter[dir] == fract_coord) {
 
		if (DiagDirToDir(ReverseDiagDir(dir)) == v->direction) {
 
			/* enter the depot */
 
			v->u.rail.track = 0x80,
 
			v->vehstatus |= VS_HIDDEN; /* hide it */
 
			v->direction = ReverseDir(v->direction);
 
			if (v->next == NULL) VehicleEnterDepot(v);
 
			v->tile = tile;
 

	
 
			InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
			return 4;
 
		}
 
	} else if (fract_coord_leave == fract_coord) {
 
		if (DiagDirToDir(dir) == v->direction) {
 
			/* leave the depot? */
 
			if ((v = v->next) != NULL) {
 
				v->vehstatus &= ~VS_HIDDEN;
 
				v->u.rail.track = (DiagDirToAxis(dir) == AXIS_X ? 1 : 2);
 
			}
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
const TileTypeProcs _tile_type_rail_procs = {
 
	DrawTile_Track,           /* draw_tile_proc */
 
	GetSlopeZ_Track,          /* get_slope_z_proc */
 
	ClearTile_Track,          /* clear_tile_proc */
 
	GetAcceptedCargo_Track,   /* get_accepted_cargo_proc */
 
	GetTileDesc_Track,        /* get_tile_desc_proc */
 
	GetTileTrackStatus_Track, /* get_tile_track_status_proc */
 
	ClickTile_Track,          /* click_tile_proc */
 
	AnimateTile_Track,        /* animate_tile_proc */
 
	TileLoop_Track,           /* tile_loop_clear */
 
	ChangeTileOwner_Track,    /* change_tile_owner_clear */
 
	NULL,                     /* get_produced_cargo_proc */
 
	VehicleEnter_Track,       /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_Track,      /* get_slope_tileh_proc */
 
};
src/rail_gui.c
Show inline comments
 
deleted file
src/rail_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/** @file rail_gui.c File for dealing with rail construction user interface */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "sound.h"
 
#include "command.h"
 
#include "vehicle.h"
 
#include "station.h"
 
#include "waypoint.h"
 
#include "debug.h"
 
#include "variables.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_station.h"
 
#include "train.h"
 

	
 
static RailType _cur_railtype;
 
static bool _remove_button_clicked;
 
static DiagDirection _build_depot_direction;
 
static byte _waypoint_count = 1;
 
static byte _cur_waypoint_type;
 

	
 
static struct {
 
	byte orientation;
 
	byte numtracks;
 
	byte platlength;
 
	bool dragdrop;
 

	
 
	bool newstations;
 
	byte station_class;
 
	byte station_type;
 
	byte station_count;
 
} _railstation;
 

	
 

	
 
static void HandleStationPlacement(TileIndex start, TileIndex end);
 
static void ShowBuildTrainDepotPicker(void);
 
static void ShowBuildWaypointPicker(void);
 
static void ShowStationBuilder(void);
 

	
 
void CcPlaySound1E(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) SndPlayTileFx(SND_20_SPLAT_2, tile);
 
}
 

	
 
static void GenericPlaceRail(TileIndex tile, int cmd)
 
{
 
	DoCommandP(tile, _cur_railtype, cmd, CcPlaySound1E,
 
		_remove_button_clicked ?
 
		CMD_REMOVE_SINGLE_RAIL | CMD_MSG(STR_1012_CAN_T_REMOVE_RAILROAD_TRACK) | CMD_AUTO | CMD_NO_WATER :
 
		CMD_BUILD_SINGLE_RAIL | CMD_MSG(STR_1011_CAN_T_BUILD_RAILROAD_TRACK) | CMD_AUTO | CMD_NO_WATER
 
	);
 
}
 

	
 
static void PlaceRail_N(TileIndex tile)
 
{
 
	int cmd = _tile_fract_coords.x > _tile_fract_coords.y ? 4 : 5;
 
	GenericPlaceRail(tile, cmd);
 
}
 

	
 
static void PlaceRail_NE(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_FIX_Y);
 
}
 

	
 
static void PlaceRail_E(TileIndex tile)
 
{
 
	int cmd = _tile_fract_coords.x + _tile_fract_coords.y <= 15 ? 2 : 3;
 
	GenericPlaceRail(tile, cmd);
 
}
 

	
 
static void PlaceRail_NW(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_FIX_X);
 
}
 

	
 
static void PlaceRail_AutoRail(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_RAILDIRS);
 
}
 

	
 
static void PlaceExtraDepotRail(TileIndex tile, uint16 extra)
 
{
 
	if (GetRailTileType(tile) != RAIL_TILE_NORMAL) return;
 
	if ((GetTrackBits(tile) & GB(extra, 8, 8)) == 0) return;
 

	
 
	DoCommandP(tile, _cur_railtype, extra & 0xFF, NULL, CMD_BUILD_SINGLE_RAIL | CMD_AUTO | CMD_NO_WATER);
 
}
 

	
 
static const uint16 _place_depot_extra[12] = {
 
	0x0604, 0x2102, 0x1202, 0x0505,
 
	0x2400, 0x2801, 0x1800, 0x1401,
 
	0x2203, 0x0904, 0x0A05, 0x1103,
 
};
 

	
 

	
 
void CcRailDepot(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		DiagDirection dir = p2;
 

	
 
		SndPlayTileFx(SND_20_SPLAT_2, tile);
 
		ResetObjectToPlace();
 

	
 
		tile += TileOffsByDiagDir(dir);
 

	
 
		if (IsTileType(tile, MP_RAILWAY)) {
 
			PlaceExtraDepotRail(tile, _place_depot_extra[dir]);
 
			PlaceExtraDepotRail(tile, _place_depot_extra[dir + 4]);
 
			PlaceExtraDepotRail(tile, _place_depot_extra[dir + 8]);
 
		}
 
	}
 
}
 

	
 
static void PlaceRail_Depot(TileIndex tile)
 
{
 
	DoCommandP(tile, _cur_railtype, _build_depot_direction, CcRailDepot,
 
		CMD_BUILD_TRAIN_DEPOT | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_100E_CAN_T_BUILD_TRAIN_DEPOT));
 
}
 

	
 
static void PlaceRail_Waypoint(TileIndex tile)
 
{
 
	if (!_remove_button_clicked) {
 
		DoCommandP(tile, _cur_waypoint_type, 0, CcPlaySound1E, CMD_BUILD_TRAIN_WAYPOINT | CMD_MSG(STR_CANT_BUILD_TRAIN_WAYPOINT));
 
	} else {
 
		DoCommandP(tile, 0, 0, CcPlaySound1E, CMD_REMOVE_TRAIN_WAYPOINT | CMD_MSG(STR_CANT_REMOVE_TRAIN_WAYPOINT));
 
	}
 
}
 

	
 
void CcStation(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_20_SPLAT_2, tile);
 
		/* Only close the station builder window if the default station is chosen. */
 
		if (_railstation.station_class == STAT_CLASS_DFLT && _railstation.station_type == 0) ResetObjectToPlace();
 
	}
 
}
 

	
 
static void PlaceRail_Station(TileIndex tile)
 
{
 
	if (_remove_button_clicked) {
 
		DoCommandP(tile, 0, 0, CcPlaySound1E, CMD_REMOVE_FROM_RAILROAD_STATION | CMD_MSG(STR_CANT_REMOVE_PART_OF_STATION));
 
	} else if (_railstation.dragdrop) {
 
		VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED);
 
		VpSetPlaceSizingLimit(_patches.station_spread);
 
	} else {
 
		DoCommandP(tile,
 
				_railstation.orientation | (_railstation.numtracks << 8) | (_railstation.platlength << 16),
 
				_cur_railtype | (_railstation.station_class << 8) | (_railstation.station_type << 16), CcStation,
 
				CMD_BUILD_RAILROAD_STATION | CMD_NO_WATER | CMD_AUTO | CMD_MSG(STR_100F_CAN_T_BUILD_RAILROAD_STATION));
 
	}
 
}
 

	
 
static void GenericPlaceSignals(TileIndex tile)
 
{
 
	uint trackstat;
 
	uint i;
 

	
 
	trackstat = (byte)GetTileTrackStatus(tile, TRANSPORT_RAIL);
 

	
 
	if ((trackstat & 0x30)) // N-S direction
 
		trackstat = (_tile_fract_coords.x <= _tile_fract_coords.y) ? 0x20 : 0x10;
 

	
 
	if ((trackstat & 0x0C)) // E-W direction
 
		trackstat = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? 4 : 8;
 

	
 
	// Lookup the bit index
 
	i = 0;
 
	if (trackstat != 0) {
 
		for (; !(trackstat & 1); trackstat >>= 1) i++;
 
	}
 

	
 
	if (!_remove_button_clicked) {
 
		DoCommandP(tile, i + (_ctrl_pressed ? 8 : 0), 0, CcPlaySound1E,
 
			CMD_BUILD_SIGNALS | CMD_AUTO | CMD_MSG(STR_1010_CAN_T_BUILD_SIGNALS_HERE));
 
	} else {
 
		DoCommandP(tile, i, 0, CcPlaySound1E,
 
			CMD_REMOVE_SIGNALS | CMD_AUTO | CMD_MSG(STR_1013_CAN_T_REMOVE_SIGNALS_FROM));
 
	}
 
}
 

	
 
static void PlaceRail_Bridge(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_X_OR_Y);
 
}
 

	
 
void CcBuildRailTunnel(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_20_SPLAT_2, tile);
 
		ResetObjectToPlace();
 
	} else {
 
		SetRedErrorSquare(_build_tunnel_endtile);
 
	}
 
}
 

	
 
static void PlaceRail_Tunnel(TileIndex tile)
 
{
 
	DoCommandP(tile, _cur_railtype, 0, CcBuildRailTunnel,
 
		CMD_BUILD_TUNNEL | CMD_AUTO | CMD_MSG(STR_5016_CAN_T_BUILD_TUNNEL_HERE));
 
}
 

	
 
void PlaceProc_BuyLand(TileIndex tile)
 
{
 
	DoCommandP(tile, 0, 0, CcPlaySound1E, CMD_PURCHASE_LAND_AREA | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_5806_CAN_T_PURCHASE_THIS_LAND));
 
}
 

	
 
static void PlaceRail_ConvertRail(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_X_AND_Y | GUI_PlaceProc_ConvertRailArea);
 
}
 

	
 
static void PlaceRail_AutoSignals(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_SIGNALDIRS);
 
}
 

	
 

	
 
/** Enum referring to the widgets of the build rail toolbar */
 
enum {
 
	RTW_CAPTION        =  1,
 
	RTW_BUILD_NS       =  4,
 
	RTW_BUILD_X        =  5,
 
	RTW_BUILD_EW       =  6,
 
	RTW_BUILD_Y        =  7,
 
	RTW_AUTORAIL       =  8,
 
	RTW_DEMOLISH       =  9,
 
	RTW_BUILD_DEPOT    = 10,
 
	RTW_BUILD_WAYPOINT = 11,
 
	RTW_BUILD_STATION  = 12,
 
	RTW_BUILD_SIGNALS  = 13,
 
	RTW_BUILD_BRIDGE   = 14,
 
	RTW_BUILD_TUNNEL   = 15,
 
	RTW_REMOVE         = 16,
 
	RTW_CONVERT_RAIL   = 17
 
};
 

	
 

	
 
static void BuildRailClick_N(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_BUILD_NS, GetRailTypeInfo(_cur_railtype)->cursor.rail_ns, 1, PlaceRail_N);
 
}
 

	
 
static void BuildRailClick_NE(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_BUILD_X, GetRailTypeInfo(_cur_railtype)->cursor.rail_swne, 1, PlaceRail_NE);
 
}
 

	
 
static void BuildRailClick_E(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_BUILD_EW, GetRailTypeInfo(_cur_railtype)->cursor.rail_ew, 1, PlaceRail_E);
 
}
 

	
 
static void BuildRailClick_NW(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_BUILD_Y, GetRailTypeInfo(_cur_railtype)->cursor.rail_nwse, 1, PlaceRail_NW);
 
}
 

	
 
static void BuildRailClick_AutoRail(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_AUTORAIL, GetRailTypeInfo(_cur_railtype)->cursor.autorail, VHM_RAIL, PlaceRail_AutoRail);
 
}
 

	
 
static void BuildRailClick_Demolish(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_DEMOLISH, ANIMCURSOR_DEMOLISH, 1, PlaceProc_DemolishArea);
 
}
 

	
 
static void BuildRailClick_Depot(Window *w)
 
{
 
	if (HandlePlacePushButton(w, RTW_BUILD_DEPOT, GetRailTypeInfo(_cur_railtype)->cursor.depot, 1, PlaceRail_Depot)) {
 
		ShowBuildTrainDepotPicker();
 
	}
 
}
 

	
 
static void BuildRailClick_Waypoint(Window *w)
 
{
 
	_waypoint_count = GetNumCustomStations(STAT_CLASS_WAYP);
 
	if (HandlePlacePushButton(w, RTW_BUILD_WAYPOINT, SPR_CURSOR_WAYPOINT, 1, PlaceRail_Waypoint) &&
 
			_waypoint_count > 1) {
 
		ShowBuildWaypointPicker();
 
	}
 
}
 

	
 
static void BuildRailClick_Station(Window *w)
 
{
 
	if (HandlePlacePushButton(w, RTW_BUILD_STATION, SPR_CURSOR_RAIL_STATION, 1, PlaceRail_Station)) ShowStationBuilder();
 
}
 

	
 
static void BuildRailClick_AutoSignals(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_BUILD_SIGNALS, ANIMCURSOR_BUILDSIGNALS, VHM_RECT, PlaceRail_AutoSignals);
 
}
 

	
 
static void BuildRailClick_Bridge(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_BUILD_BRIDGE, SPR_CURSOR_BRIDGE, 1, PlaceRail_Bridge);
 
}
 

	
 
static void BuildRailClick_Tunnel(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_BUILD_TUNNEL, GetRailTypeInfo(_cur_railtype)->cursor.tunnel, 3, PlaceRail_Tunnel);
 
}
 

	
 
static void BuildRailClick_Remove(Window *w)
 
{
 
	if (IsWindowWidgetDisabled(w, RTW_REMOVE)) return;
 
	SetWindowDirty(w);
 
	SndPlayFx(SND_15_BEEP);
 

	
 
	ToggleWidgetLoweredState(w, RTW_REMOVE);
 
	_remove_button_clicked = IsWindowWidgetLowered(w, RTW_REMOVE);
 
	SetSelectionRed(_remove_button_clicked);
 

	
 
	// handle station builder
 
	if (_remove_button_clicked) {
 
		SetTileSelectSize(1, 1);
 
	}
 
}
 

	
 
static void BuildRailClick_Convert(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_CONVERT_RAIL, GetRailTypeInfo(_cur_railtype)->cursor.convert, 1, PlaceRail_ConvertRail);
 
}
 

	
 
static void BuildRailClick_Landscaping(Window *w)
 
{
 
	ShowTerraformToolbar();
 
}
 

	
 
static void DoRailroadTrack(int mode)
 
{
 
	DoCommandP(TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), _cur_railtype | (mode << 4), NULL,
 
		_remove_button_clicked ?
 
		CMD_REMOVE_RAILROAD_TRACK | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1012_CAN_T_REMOVE_RAILROAD_TRACK) :
 
		CMD_BUILD_RAILROAD_TRACK  | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1011_CAN_T_BUILD_RAILROAD_TRACK)
 
	);
 
}
 

	
 
static void HandleAutodirPlacement(void)
 
{
 
	TileHighlightData *thd = &_thd;
 
	int trackstat = thd->drawstyle & 0xF; // 0..5
 

	
 
	if (thd->drawstyle & HT_RAIL) { // one tile case
 
		GenericPlaceRail(TileVirtXY(thd->selend.x, thd->selend.y), trackstat);
 
		return;
 
	}
 

	
 
	DoRailroadTrack(trackstat);
 
}
 

	
 
static void HandleAutoSignalPlacement(void)
 
{
 
	TileHighlightData *thd = &_thd;
 
	byte trackstat = thd->drawstyle & 0xF; // 0..5
 

	
 
	if (thd->drawstyle == HT_RECT) { // one tile case
 
		GenericPlaceSignals(TileVirtXY(thd->selend.x, thd->selend.y));
 
		return;
 
	}
 

	
 
	// _patches.drag_signals_density is given as a parameter such that each user in a network
 
	// game can specify his/her own signal density
 
	DoCommandP(
 
		TileVirtXY(thd->selstart.x, thd->selstart.y),
 
		TileVirtXY(thd->selend.x, thd->selend.y),
 
		(_ctrl_pressed ? 1 << 3 : 0) | (trackstat << 4) | (_patches.drag_signals_density << 24),
 
		CcPlaySound1E,
 
		_remove_button_clicked ?
 
			CMD_REMOVE_SIGNAL_TRACK | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1013_CAN_T_REMOVE_SIGNALS_FROM) :
 
			CMD_BUILD_SIGNAL_TRACK  | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1010_CAN_T_BUILD_SIGNALS_HERE)
 
	);
 
}
 

	
 

	
 
typedef void OnButtonClick(Window *w);
 

	
 
static OnButtonClick * const _build_railroad_button_proc[] = {
 
	BuildRailClick_N,
 
	BuildRailClick_NE,
 
	BuildRailClick_E,
 
	BuildRailClick_NW,
 
	BuildRailClick_AutoRail,
 
	BuildRailClick_Demolish,
 
	BuildRailClick_Depot,
 
	BuildRailClick_Waypoint,
 
	BuildRailClick_Station,
 
	BuildRailClick_AutoSignals,
 
	BuildRailClick_Bridge,
 
	BuildRailClick_Tunnel,
 
	BuildRailClick_Remove,
 
	BuildRailClick_Convert,
 
	BuildRailClick_Landscaping,
 
};
 

	
 
static const uint16 _rail_keycodes[] = {
 
	'1',
 
	'2',
 
	'3',
 
	'4',
 
	'5',
 
	'6',
 
	'7', // depot
 
	'8', // waypoint
 
	'9', // station
 
	'S', // signals
 
	'B', // bridge
 
	'T', // tunnel
 
	'R', // remove
 
	'C', // convert rail
 
	'L', // landscaping
 
};
 

	
 

	
 
static void UpdateRemoveWidgetStatus(Window *w, int clicked_widget)
 
{
 
	/* If it is the removal button that has been clicked, do nothing,
 
	 * as it is up to the other buttons to drive removal status */
 
	if (clicked_widget == RTW_REMOVE) return;
 

	
 
	switch (clicked_widget) {
 
		case RTW_BUILD_NS:
 
		case RTW_BUILD_X:
 
		case RTW_BUILD_EW:
 
		case RTW_BUILD_Y:
 
		case RTW_AUTORAIL:
 
		case RTW_BUILD_WAYPOINT:
 
		case RTW_BUILD_STATION:
 
		case RTW_BUILD_SIGNALS:
 
			/* Removal button is enabled only if the rail/signal/waypoint/station
 
			 * button is still lowered.  Once raised, it has to be disabled */
 
			SetWindowWidgetDisabledState(w, RTW_REMOVE, !IsWindowWidgetLowered(w, clicked_widget));
 
			break;
 

	
 
		default:
 
			/* When any other buttons than rail/signal/waypoint/station, raise and
 
			 * disable the removal button*/
 
			DisableWindowWidget(w, RTW_REMOVE);
 
			RaiseWindowWidget(w, RTW_REMOVE);
 
			break;
 
	}
 
}
 

	
 
static void BuildRailToolbWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: DisableWindowWidget(w, RTW_REMOVE); break;
 

	
 
	case WE_PAINT: DrawWindowWidgets(w); break;
 

	
 
	case WE_CLICK:
 
		if (e->we.click.widget >= 4) {
 
			_remove_button_clicked = false;
 
			_build_railroad_button_proc[e->we.click.widget - 4](w);
 
		}
 
		UpdateRemoveWidgetStatus(w, e->we.click.widget);
 
		break;
 

	
 
	case WE_KEYPRESS: {
 
		uint i;
 

	
 
		for (i = 0; i != lengthof(_rail_keycodes); i++) {
 
			if (e->we.keypress.keycode == _rail_keycodes[i]) {
 
				e->we.keypress.cont = false;
 
				_remove_button_clicked = false;
 
				_build_railroad_button_proc[i](w);
 
				UpdateRemoveWidgetStatus(w, i + 4);
 
				break;
 
			}
 
		}
 
		MarkTileDirty(_thd.pos.x, _thd.pos.y); // redraw tile selection
 
		break;
 
	}
 

	
 
	case WE_PLACE_OBJ:
 
		_place_proc(e->we.place.tile);
 
		return;
 

	
 
	case WE_PLACE_DRAG: {
 
		VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata & 0xF);
 
		return;
 
	}
 

	
 
	case WE_PLACE_MOUSEUP:
 
		if (e->we.place.pt.x != -1) {
 
			TileIndex start_tile = e->we.place.starttile;
 
			TileIndex end_tile = e->we.place.tile;
 

	
 
			if (e->we.place.userdata == VPM_X_OR_Y) {
 
				ResetObjectToPlace();
 
				ShowBuildBridgeWindow(start_tile, end_tile, _cur_railtype);
 
			} else if (e->we.place.userdata == VPM_RAILDIRS) {
 
				bool old = _remove_button_clicked;
 
				if (_ctrl_pressed) _remove_button_clicked = true;
 
				HandleAutodirPlacement();
 
				_remove_button_clicked = old;
 
			} else if (e->we.place.userdata == VPM_SIGNALDIRS) {
 
				HandleAutoSignalPlacement();
 
			} else if ((e->we.place.userdata & 0xF) == VPM_X_AND_Y) {
 
				if (GUIPlaceProcDragXY(e)) break;
 

	
 
				if ((e->we.place.userdata >> 4) == GUI_PlaceProc_ConvertRailArea >> 4)
 
					DoCommandP(end_tile, start_tile, _cur_railtype, CcPlaySound10, CMD_CONVERT_RAIL | CMD_MSG(STR_CANT_CONVERT_RAIL));
 
			} else if (e->we.place.userdata == VPM_X_AND_Y_LIMITED) {
 
				HandleStationPlacement(start_tile, end_tile);
 
			} else {
 
				DoRailroadTrack(e->we.place.userdata & 1);
 
			}
 
		}
 
		break;
 

	
 
	case WE_ABORT_PLACE_OBJ:
 
		RaiseWindowButtons(w);
 
		DisableWindowWidget(w, RTW_REMOVE);
 
		InvalidateWidget(w, RTW_REMOVE);
 

	
 
		w = FindWindowById(WC_BUILD_STATION, 0);
 
		if (w != NULL) WP(w,def_d).close = true;
 
		w = FindWindowById(WC_BUILD_DEPOT, 0);
 
		if (w != NULL) WP(w,def_d).close = true;
 
		break;
 

	
 
	case WE_PLACE_PRESIZE: {
 
		TileIndex tile = e->we.place.tile;
 

	
 
		DoCommand(tile, 0, 0, DC_AUTO, CMD_BUILD_TUNNEL);
 
		VpSetPresizeRange(tile, _build_tunnel_endtile == 0 ? tile : _build_tunnel_endtile);
 
	} break;
 

	
 
	case WE_DESTROY:
 
		if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0);
 
		break;
 
	}
 
}
 

	
 

	
 
static const Widget _build_rail_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   359,     0,    13, STR_100A_RAILROAD_CONSTRUCTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,     7,   360,   371,     0,    13, 0x0,                            STR_STICKY_BUTTON},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,     7,   110,   113,    14,    35, 0x0,                            STR_NULL},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    0,     21,    14,    35, SPR_IMG_RAIL_NS,                STR_1018_BUILD_RAILROAD_TRACK},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    22,    43,    14,    35, SPR_IMG_RAIL_NE,                STR_1018_BUILD_RAILROAD_TRACK},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    44,    65,    14,    35, SPR_IMG_RAIL_EW,                STR_1018_BUILD_RAILROAD_TRACK},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    66,    87,    14,    35, SPR_IMG_RAIL_NW,                STR_1018_BUILD_RAILROAD_TRACK},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    88,   109,    14,    35, SPR_IMG_AUTORAIL,               STR_BUILD_AUTORAIL_TIP},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   114,   135,    14,    35, SPR_IMG_DYNAMITE,               STR_018D_DEMOLISH_BUILDINGS_ETC},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   136,   157,    14,    35, SPR_IMG_DEPOT_RAIL,             STR_1019_BUILD_TRAIN_DEPOT_FOR_BUILDING},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   158,   179,    14,    35, SPR_IMG_WAYPOINT,               STR_CONVERT_RAIL_TO_WAYPOINT_TIP},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   180,   221,    14,    35, SPR_IMG_RAIL_STATION,           STR_101A_BUILD_RAILROAD_STATION},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   222,   243,    14,    35, SPR_IMG_RAIL_SIGNALS,           STR_101B_BUILD_RAILROAD_SIGNALS},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   244,   285,    14,    35, SPR_IMG_BRIDGE,                 STR_101C_BUILD_RAILROAD_BRIDGE},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   286,   305,    14,    35, SPR_IMG_TUNNEL_RAIL,            STR_101D_BUILD_RAILROAD_TUNNEL},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   306,   327,    14,    35, SPR_IMG_REMOVE,                 STR_101E_TOGGLE_BUILD_REMOVE_FOR},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   328,   349,    14,    35, SPR_IMG_CONVERT_RAIL,           STR_CONVERT_RAIL_TIP},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   350,   371,    14,    35, SPR_IMG_LANDSCAPING,            STR_LANDSCAPING_TOOLBAR_TIP},
 

	
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_rail_desc = {
 
	WDP_ALIGN_TBR, 22, 372, 36,
 
	WC_BUILD_TOOLBAR, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
 
	_build_rail_widgets,
 
	BuildRailToolbWndProc
 
};
 

	
 

	
 
/** Configures the rail toolbar for railtype given
 
 * @param railtype the railtype to display
 
 * @param w the window to modify
 
 */
 
static void SetupRailToolbar(RailType railtype, Window *w)
 
{
 
	const RailtypeInfo *rti = GetRailTypeInfo(railtype);
 

	
 
	assert(railtype < RAILTYPE_END);
 
	w->widget[RTW_CAPTION].data = rti->strings.toolbar_caption;
 
	w->widget[RTW_BUILD_NS].data = rti->gui_sprites.build_ns_rail;
 
	w->widget[RTW_BUILD_X].data = rti->gui_sprites.build_x_rail;
 
	w->widget[RTW_BUILD_EW].data = rti->gui_sprites.build_ew_rail;
 
	w->widget[RTW_BUILD_Y].data = rti->gui_sprites.build_y_rail;
 
	w->widget[RTW_AUTORAIL].data = rti->gui_sprites.auto_rail;
 
	w->widget[RTW_BUILD_DEPOT].data = rti->gui_sprites.build_depot;
 
	w->widget[RTW_CONVERT_RAIL].data = rti->gui_sprites.convert_rail;
 
	w->widget[RTW_BUILD_TUNNEL].data = rti->gui_sprites.build_tunnel;
 
}
 

	
 
void ShowBuildRailToolbar(RailType railtype, int button)
 
{
 
	Window *w;
 

	
 
	if (!IsValidPlayer(_current_player)) return;
 
	if (!ValParamRailtype(railtype)) return;
 

	
 
	// don't recreate the window if we're clicking on a button and the window exists.
 
	if (button < 0 || !(w = FindWindowById(WC_BUILD_TOOLBAR, 0)) || w->wndproc != BuildRailToolbWndProc) {
 
		DeleteWindowById(WC_BUILD_TOOLBAR, 0);
 
		_cur_railtype = railtype;
 
		w = AllocateWindowDesc(&_build_rail_desc);
 
		SetupRailToolbar(railtype, w);
 
	}
 

	
 
	_remove_button_clicked = false;
 
	if (w != NULL && button >= 0) {
 
		_build_railroad_button_proc[button](w);
 
		UpdateRemoveWidgetStatus(w, button + 4);
 
	}
 
	if (_patches.link_terraform_toolbar) ShowTerraformToolbar();
 
}
 

	
 
/* TODO: For custom stations, respect their allowed platforms/lengths bitmasks!
 
 * --pasky */
 

	
 
static void HandleStationPlacement(TileIndex start, TileIndex end)
 
{
 
	uint sx = TileX(start);
 
	uint sy = TileY(start);
 
	uint ex = TileX(end);
 
	uint ey = TileY(end);
 
	uint w,h;
 

	
 
	if (sx > ex) uintswap(sx,ex);
 
	if (sy > ey) uintswap(sy,ey);
 
	w = ex - sx + 1;
 
	h = ey - sy + 1;
 
	if (!_railstation.orientation) uintswap(w,h);
 

	
 
	DoCommandP(TileXY(sx, sy),
 
			_railstation.orientation | (w << 8) | (h << 16),
 
			_cur_railtype | (_railstation.station_class << 8) | (_railstation.station_type << 16), CcStation,
 
			CMD_BUILD_RAILROAD_STATION | CMD_NO_WATER | CMD_AUTO | CMD_MSG(STR_100F_CAN_T_BUILD_RAILROAD_STATION));
 
}
 

	
 
/* Check if the currently selected station size is allowed */
 
static void CheckSelectedSize(Window *w, const StationSpec *statspec)
 
{
 
	if (statspec == NULL || _railstation.dragdrop) return;
 

	
 
	if (HASBIT(statspec->disallowed_platforms, _railstation.numtracks - 1)) {
 
		RaiseWindowWidget(w, _railstation.numtracks + 4);
 
		_railstation.numtracks = 1;
 
		while (HASBIT(statspec->disallowed_platforms, _railstation.numtracks - 1)) {
 
			_railstation.numtracks++;
 
		}
 
		LowerWindowWidget(w, _railstation.numtracks + 4);
 
	}
 

	
 
	if (HASBIT(statspec->disallowed_lengths, _railstation.platlength - 1)) {
 
		RaiseWindowWidget(w, _railstation.platlength + 11);
 
		_railstation.platlength = 1;
 
		while (HASBIT(statspec->disallowed_lengths, _railstation.platlength - 1)) {
 
			_railstation.platlength++;
 
		}
 
		LowerWindowWidget(w, _railstation.platlength + 11);
 
	}
 
}
 

	
 
static void StationBuildWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE:
 
		LowerWindowWidget(w, _railstation.orientation + 3);
 
		if (_railstation.dragdrop) {
 
			LowerWindowWidget(w, 19);
 
		} else {
 
			LowerWindowWidget(w, _railstation.numtracks + 4);
 
			LowerWindowWidget(w, _railstation.platlength + 11);
 
		}
 
		SetWindowWidgetLoweredState(w, 20, !_station_show_coverage);
 
		SetWindowWidgetLoweredState(w, 21, _station_show_coverage);
 
		break;
 

	
 
	case WE_PAINT: {
 
		int rad;
 
		uint bits;
 
		bool newstations = _railstation.newstations;
 
		int y_offset;
 
		DrawPixelInfo tmp_dpi, *old_dpi;
 
		const StationSpec *statspec = newstations ? GetCustomStationSpec(_railstation.station_class, _railstation.station_type) : NULL;
 

	
 
		if (WP(w,def_d).close) return;
 

	
 
		if (_railstation.dragdrop) {
 
			SetTileSelectSize(1, 1);
 
		} else {
 
			int x = _railstation.numtracks;
 
			int y = _railstation.platlength;
 
			if (_railstation.orientation == 0) intswap(x,y);
 
			if (!_remove_button_clicked)
 
				SetTileSelectSize(x, y);
 
		}
 

	
 
		rad = (_patches.modified_catchment) ? CA_TRAIN : 4;
 

	
 
		if (_station_show_coverage)
 
			SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
 

	
 
		for (bits = 0; bits < 7; bits++) {
 
			bool disable = bits >= _patches.station_spread;
 
			if (statspec == NULL) {
 
				SetWindowWidgetDisabledState(w, bits +  5, disable);
 
				SetWindowWidgetDisabledState(w, bits + 12, disable);
 
			} else {
 
				SetWindowWidgetDisabledState(w, bits +  5, HASBIT(statspec->disallowed_platforms, bits) || disable);
 
				SetWindowWidgetDisabledState(w, bits + 12, HASBIT(statspec->disallowed_lengths,   bits) || disable);
 
			}
 
		}
 

	
 
		SetDParam(0, GetStationClassName(_railstation.station_class));
 
		DrawWindowWidgets(w);
 

	
 
		y_offset = newstations ? 90 : 0;
 

	
 
		/* Set up a clipping area for the '/' station preview */
 
		if (FillDrawPixelInfo(&tmp_dpi, 7, 26 + y_offset, 66, 48)) {
 
			old_dpi = _cur_dpi;
 
			_cur_dpi = &tmp_dpi;
 
			if (!DrawStationTile(32, 16, _cur_railtype, AXIS_X, _railstation.station_class, _railstation.station_type)) {
 
				StationPickerDrawSprite(32, 16, _cur_railtype, 2);
 
			}
 
			_cur_dpi = old_dpi;
 
		}
 

	
 
		/* Set up a clipping area for the '\' station preview */
 
		if (FillDrawPixelInfo(&tmp_dpi, 75, 26 + y_offset, 66, 48)) {
 
			old_dpi = _cur_dpi;
 
			_cur_dpi = &tmp_dpi;
 
			if (!DrawStationTile(32, 16, _cur_railtype, AXIS_Y, _railstation.station_class, _railstation.station_type)) {
 
				StationPickerDrawSprite(32, 16, _cur_railtype, 3);
 
			}
 
			_cur_dpi = old_dpi;
 
		}
 

	
 
		DrawStringCentered(74, 15 + y_offset, STR_3002_ORIENTATION, 0);
 
		DrawStringCentered(74, 76 + y_offset, STR_3003_NUMBER_OF_TRACKS, 0);
 
		DrawStringCentered(74, 101 + y_offset, STR_3004_PLATFORM_LENGTH, 0);
 
		DrawStringCentered(74, 141 + y_offset, STR_3066_COVERAGE_AREA_HIGHLIGHT, 0);
 

	
 
		DrawStationCoverageAreaText(2, 166 + y_offset, (uint)-1, rad);
 

	
 
		if (newstations) {
 
			uint16 i;
 
			uint y = 35;
 

	
 
			for (i = w->vscroll.pos; i < _railstation.station_count && i < (uint)(w->vscroll.pos + w->vscroll.cap); i++) {
 
				const StationSpec *statspec = GetCustomStationSpec(_railstation.station_class, i);
 

	
 
				if (statspec != NULL && statspec->name != 0) {
 
					if (HASBIT(statspec->callbackmask, CBM_STATION_AVAIL) && GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE) == 0) {
 
						GfxFillRect(8, y - 2, 127, y + 10, PALETTE_MODIFIER_GREYOUT);
 
					}
 

	
 
					DrawStringTruncated(9, y, statspec->name, i == _railstation.station_type ? 12 : 16, 118);
 
				} else {
 
					DrawStringTruncated(9, y, STR_STAT_CLASS_DFLT, i == _railstation.station_type ? 12 : 16, 118);
 
				}
 

	
 
				y += 14;
 
			}
 
		}
 
	} break;
 

	
 
	case WE_CLICK: {
 
		switch (e->we.click.widget) {
 
		case 3:
 
		case 4:
 
			RaiseWindowWidget(w, _railstation.orientation + 3);
 
			_railstation.orientation = e->we.click.widget - 3;
 
			LowerWindowWidget(w, _railstation.orientation + 3);
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 5:
 
		case 6:
 
		case 7:
 
		case 8:
 
		case 9:
 
		case 10:
 
		case 11:
 
			RaiseWindowWidget(w, _railstation.numtracks + 4);
 
			RaiseWindowWidget(w, 19);
 
			_railstation.numtracks = (e->we.click.widget - 5) + 1;
 
			_railstation.dragdrop = false;
 
			LowerWindowWidget(w, _railstation.platlength + 11);
 
			LowerWindowWidget(w, _railstation.numtracks + 4);
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 12:
 
		case 13:
 
		case 14:
 
		case 15:
 
		case 16:
 
		case 17:
 
		case 18:
 
			RaiseWindowWidget(w, _railstation.platlength + 11);
 
			RaiseWindowWidget(w, 19);
 
			_railstation.platlength = (e->we.click.widget - 12) + 1;
 
			_railstation.dragdrop = false;
 
			LowerWindowWidget(w, _railstation.platlength + 11);
 
			LowerWindowWidget(w, _railstation.numtracks + 4);
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 19:
 
			_railstation.dragdrop ^= true;
 
			ToggleWidgetLoweredState(w, 19);
 
			SetWindowWidgetLoweredState(w, _railstation.numtracks + 4, !_railstation.dragdrop);
 
			SetWindowWidgetLoweredState(w, _railstation.platlength + 11, !_railstation.dragdrop);
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 20:
 
		case 21:
 
			_station_show_coverage = e->we.click.widget - 20;
 
			SetWindowWidgetLoweredState(w, 20, !_station_show_coverage);
 
			SetWindowWidgetLoweredState(w, 21, _station_show_coverage);
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 22:
 
		case 23:
 
			ShowDropDownMenu(w, BuildStationClassDropdown(), _railstation.station_class, 23, 0, 1 << STAT_CLASS_WAYP);
 
			break;
 

	
 
		case 24: {
 
			const StationSpec *statspec;
 
			int y = (e->we.click.pt.y - 32) / 14;
 

	
 
			if (y >= w->vscroll.cap) return;
 
			y += w->vscroll.pos;
 
			if (y >= _railstation.station_count) return;
 

	
 
			/* Check station availability callback */
 
			statspec = GetCustomStationSpec(_railstation.station_class, y);
 
			if (statspec != NULL &&
 
				HASBIT(statspec->callbackmask, CBM_STATION_AVAIL) &&
 
				GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE) == 0) return;
 

	
 
			_railstation.station_type = y;
 

	
 
			CheckSelectedSize(w, statspec);
 

	
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
		}
 
	} break;
 

	
 
	case WE_DROPDOWN_SELECT:
 
		if (_railstation.station_class != e->we.dropdown.index) {
 
			_railstation.station_class = e->we.dropdown.index;
 
			_railstation.station_type  = 0;
 
			_railstation.station_count = GetNumCustomStations(_railstation.station_class);
 

	
 
			CheckSelectedSize(w, GetCustomStationSpec(_railstation.station_class, _railstation.station_type));
 

	
 
			w->vscroll.count = _railstation.station_count;
 
			w->vscroll.pos   = _railstation.station_type;
 
		}
 

	
 
		SndPlayFx(SND_15_BEEP);
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		if (WP(w,def_d).close) {
 
			DeleteWindow(w);
 
			return;
 
		}
 
		CheckRedrawStationCoverage(w);
 
		break;
 

	
 
	case WE_DESTROY:
 
		if (!WP(w,def_d).close) ResetObjectToPlace();
 
		break;
 
	}
 
}
 

	
 
static const Widget _station_builder_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   147,     0,    13, STR_3000_RAIL_STATION_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   147,    14,   199, 0x0,                             STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     7,    72,    26,    73, 0x0,                             STR_304E_SELECT_RAILROAD_STATION},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    75,   140,    26,    73, 0x0,                             STR_304E_SELECT_RAILROAD_STATION},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    22,    36,    87,    98, STR_00CB_1,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    37,    51,    87,    98, STR_00CC_2,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    52,    66,    87,    98, STR_00CD_3,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    67,    81,    87,    98, STR_00CE_4,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    82,    96,    87,    98, STR_00CF_5,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    97,   111,    87,    98, STR_0335_6,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   112,   126,    87,    98, STR_0336_7,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    22,    36,   112,   123, STR_00CB_1,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    37,    51,   112,   123, STR_00CC_2,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    52,    66,   112,   123, STR_00CD_3,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    67,    81,   112,   123, STR_00CE_4,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    82,    96,   112,   123, STR_00CF_5,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    97,   111,   112,   123, STR_0335_6,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   112,   126,   112,   123, STR_0336_7,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    37,   111,   126,   137, STR_DRAG_DROP,                   STR_STATION_DRAG_DROP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    14,    73,   152,   163, STR_02DB_OFF,                    STR_3065_DON_T_HIGHLIGHT_COVERAGE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    74,   133,   152,   163, STR_02DA_ON,                     STR_3064_HIGHLIGHT_COVERAGE_AREA},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _newstation_builder_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   147,     0,    13, STR_3000_RAIL_STATION_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   147,    14,   289, 0x0,                             STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     7,    72,   116,   163, 0x0,                             STR_304E_SELECT_RAILROAD_STATION},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    75,   140,   116,   163, 0x0,                             STR_304E_SELECT_RAILROAD_STATION},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    22,    36,   177,   188, STR_00CB_1,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    37,    51,   177,   188, STR_00CC_2,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    52,    66,   177,   188, STR_00CD_3,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    67,    81,   177,   188, STR_00CE_4,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    82,    96,   177,   188, STR_00CF_5,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    97,   111,   177,   188, STR_0335_6,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   112,   126,   177,   188, STR_0336_7,                      STR_304F_SELECT_NUMBER_OF_PLATFORMS},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    22,    36,   202,   213, STR_00CB_1,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    37,    51,   202,   213, STR_00CC_2,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    52,    66,   202,   213, STR_00CD_3,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    67,    81,   202,   213, STR_00CE_4,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    82,    96,   202,   213, STR_00CF_5,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    97,   111,   202,   213, STR_0335_6,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   112,   126,   202,   213, STR_0336_7,                      STR_3050_SELECT_LENGTH_OF_RAILROAD},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    37,   111,   216,   227, STR_DRAG_DROP,                   STR_STATION_DRAG_DROP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    14,    73,   242,   253, STR_02DB_OFF,                    STR_3065_DON_T_HIGHLIGHT_COVERAGE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    74,   133,   242,   253, STR_02DA_ON,                     STR_3064_HIGHLIGHT_COVERAGE_AREA},
 

	
 
/* newstations gui additions */
 
{      WWT_INSET,   RESIZE_NONE,    14,     7,   140,    17,    28, STR_02BD,                        STR_SELECT_STATION_CLASS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   129,   139,    18,    27, STR_0225,                        STR_SELECT_STATION_CLASS_TIP},
 
{     WWT_MATRIX,   RESIZE_NONE,    14,     7,   128,    32,   102, 0x501,                           STR_SELECT_STATION_TYPE_TIP},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,    14,   129,   140,    32,   102, 0x0,                             STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _station_builder_desc = {
 
	WDP_AUTO, WDP_AUTO, 148, 200,
 
	WC_BUILD_STATION, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_station_builder_widgets,
 
	StationBuildWndProc
 
};
 

	
 
static const WindowDesc _newstation_builder_desc = {
 
	WDP_AUTO, WDP_AUTO, 148, 290,
 
	WC_BUILD_STATION, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_newstation_builder_widgets,
 
	StationBuildWndProc
 
};
 

	
 
static void ShowStationBuilder(void)
 
{
 
	Window *w;
 
	if (GetNumStationClasses() <= 2 && GetNumCustomStations(STAT_CLASS_DFLT) == 1) {
 
		w = AllocateWindowDesc(&_station_builder_desc);
 
		_railstation.newstations = false;
 
	} else {
 
		w = AllocateWindowDesc(&_newstation_builder_desc);
 
		_railstation.newstations = true;
 
		_railstation.station_count = GetNumCustomStations(_railstation.station_class);
 

	
 
		w->vscroll.count = _railstation.station_count;
 
		w->vscroll.cap   = 5;
 
		w->vscroll.pos   = clamp(_railstation.station_type - 2, 0, w->vscroll.count - w->vscroll.cap);
 
	}
 
}
 

	
 
static void BuildTrainDepotWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: LowerWindowWidget(w, _build_depot_direction + 3); break;
 

	
 
	case WE_PAINT: {
 
		RailType r;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		r = _cur_railtype;
 
		DrawTrainDepotSprite(70, 17, 0, r);
 
		DrawTrainDepotSprite(70, 69, 1, r);
 
		DrawTrainDepotSprite( 2, 69, 2, r);
 
		DrawTrainDepotSprite( 2, 17, 3, r);
 
		break;
 
		}
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
			case 3:
 
			case 4:
 
			case 5:
 
			case 6:
 
				RaiseWindowWidget(w, _build_depot_direction + 3);
 
				_build_depot_direction = e->we.click.widget - 3;
 
				LowerWindowWidget(w, _build_depot_direction + 3);
 
				SndPlayFx(SND_15_BEEP);
 
				SetWindowDirty(w);
 
				break;
 
		}
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		if (WP(w,def_d).close) DeleteWindow(w);
 
		return;
 

	
 
	case WE_DESTROY:
 
		if (!WP(w,def_d).close) ResetObjectToPlace();
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_depot_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   139,     0,    13, STR_1014_TRAIN_DEPOT_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   139,    14,   121, 0x0,                              STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    17,    66, 0x0,                              STR_1020_SELECT_RAILROAD_DEPOT_ORIENTATIO},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    69,   118, 0x0,                              STR_1020_SELECT_RAILROAD_DEPOT_ORIENTATIO},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    69,   118, 0x0,                              STR_1020_SELECT_RAILROAD_DEPOT_ORIENTATIO},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    17,    66, 0x0,                              STR_1020_SELECT_RAILROAD_DEPOT_ORIENTATIO},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_depot_desc = {
 
	WDP_AUTO, WDP_AUTO, 140, 122,
 
	WC_BUILD_DEPOT, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_depot_widgets,
 
	BuildTrainDepotWndProc
 
};
 

	
 
static void ShowBuildTrainDepotPicker(void)
 
{
 
	AllocateWindowDesc(&_build_depot_desc);
 
}
 

	
 

	
 
static void BuildWaypointWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		uint i;
 

	
 
		for (i = 0; i < w->hscroll.cap; i++) {
 
			SetWindowWidgetLoweredState(w, i + 3, (w->hscroll.pos + i) == _cur_waypoint_type);
 
		}
 

	
 
		DrawWindowWidgets(w);
 

	
 
		for (i = 0; i < w->hscroll.cap; i++) {
 
			if (w->hscroll.pos + i < w->hscroll.count) {
 
				const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, w->hscroll.pos + i);
 

	
 
				DrawWaypointSprite(2 + i * 68, 25, w->hscroll.pos + i, _cur_railtype);
 

	
 
				if (statspec != NULL &&
 
						HASBIT(statspec->callbackmask, CBM_STATION_AVAIL) &&
 
						GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE) == 0) {
 
					GfxFillRect(4 + i * 68, 18, 67 + i * 68, 75, PALETTE_MODIFIER_GREYOUT);
 
				}
 
			}
 
		}
 
		break;
 
	}
 
	case WE_CLICK: {
 
		switch (e->we.click.widget) {
 
		case 3: case 4: case 5: case 6: case 7: {
 
			byte type = e->we.click.widget - 3 + w->hscroll.pos;
 

	
 
			/* Check station availability callback */
 
			const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, type);
 
			if (statspec != NULL &&
 
					HASBIT(statspec->callbackmask, CBM_STATION_AVAIL) &&
 
					GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE) == 0) return;
 

	
 
			_cur_waypoint_type = type;
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
		}
 
		break;
 
	}
 

	
 
	case WE_MOUSELOOP:
 
		if (WP(w,def_d).close) DeleteWindow(w);
 
		break;
 

	
 
	case WE_DESTROY:
 
		if (!WP(w,def_d).close) ResetObjectToPlace();
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_waypoint_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,     STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   343,     0,    13, STR_WAYPOINT, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   343,    14,    91, 0x0,          0},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,     7,     3,    68,    17,    76, 0x0,          STR_WAYPOINT_GRAPHICS_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,     7,    71,   136,    17,    76, 0x0,          STR_WAYPOINT_GRAPHICS_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,     7,   139,   204,    17,    76, 0x0,          STR_WAYPOINT_GRAPHICS_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,     7,   207,   272,    17,    76, 0x0,          STR_WAYPOINT_GRAPHICS_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,     7,   275,   340,    17,    76, 0x0,          STR_WAYPOINT_GRAPHICS_TIP},
 

	
 
{ WWT_HSCROLLBAR,   RESIZE_NONE,    7,     1,   343,     80,    91, 0x0,          STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{    WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_waypoint_desc = {
 
	WDP_AUTO, WDP_AUTO, 344, 92,
 
	WC_BUILD_DEPOT, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_waypoint_widgets,
 
	BuildWaypointWndProc
 
};
 

	
 
static void ShowBuildWaypointPicker(void)
 
{
 
	Window *w = AllocateWindowDesc(&_build_waypoint_desc);
 
	w->hscroll.cap = 5;
 
	w->hscroll.count = _waypoint_count;
 
}
 

	
 

	
 
void InitializeRailGui(void)
 
{
 
	_build_depot_direction = DIAGDIR_NW;
 
	_railstation.numtracks = 1;
 
	_railstation.platlength = 1;
 
	_railstation.dragdrop = true;
 
}
 

	
 
void ReinitGuiAfterToggleElrail(bool disable)
 
{
 
	extern RailType _last_built_railtype;
 
	if (disable && _last_built_railtype == RAILTYPE_ELECTRIC) {
 
		Window *w;
 
		_last_built_railtype = _cur_railtype = RAILTYPE_RAIL;
 
		w = FindWindowById(WC_BUILD_TOOLBAR, 0);
 
		if (w != NULL && w->wndproc == BuildRailToolbWndProc) {
 
			SetupRailToolbar(_cur_railtype, w);
 
			SetWindowDirty(w);
 
		}
 
	}
 
	MarkWholeScreenDirty();
 
}
 

	
 

	
src/road_cmd.c
Show inline comments
 
deleted file
src/road_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.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;
 
}
 

	
 

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

	
 
	if (_game_mode == GM_EDITOR) return true;
 

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

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

	
 
	// 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 = 0;
 
	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;
 
}
 

	
 

	
 
/** Delete a piece of road.
 
 * @param tile tile where to remove road from
 
 * @param p1 road piece flags
 
 * @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 = 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;
 
	}
 

	
 
	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 |= (c & 0xC) >> 2;
 
				c |= (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, FIND_FIRST_BIT(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, 0, 0,
 
		ROAD_X,   0, 0,  // 3, 4, 5
 
		ROAD_Y,   0, 0,
 
		ROAD_Y,   0, 0,  // 9, 10, 11
 
		ROAD_X,   0, 0
 
	},
 
	// allowed road for an evenly raised platform
 
	{
 
		0,
 
		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 |= (*pieces & 0xC) >> 2;
 
			*pieces |= (*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 |= (*pieces & 0xC) >> 2;
 
		*pieces |= (*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 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 = 0;
 
	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) || !IsValidTownID(p2)) return CMD_ERROR;
 
	pieces = 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, FIND_FIRST_BIT(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);
 
		}
 

	
 
		MarkTileDirtyByTile(tile);
 
	}
 
	return cost;
 
}
 

	
 
int32 DoConvertStreetRail(TileIndex tile, RailType totype, bool exec)
 
{
 
	// not a railroad crossing?
 
	if (!IsLevelCrossing(tile)) return CMD_ERROR;
 

	
 
	// not owned by me?
 
	if (!CheckTileOwnership(tile) || !EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	if (GetRailTypeCrossing(tile) == totype) return CMD_ERROR;
 

	
 
	// 'hidden' elrails can't be downgraded to normal rail when elrails are disabled
 
	if (_patches.disable_elrails && totype == RAILTYPE_RAIL && GetRailTypeCrossing(tile) == RAILTYPE_ELECTRIC) return CMD_ERROR;
 

	
 
	if (exec) {
 
		SetRailTypeCrossing(tile, totype);
 
		MarkTileDirtyByTile(tile);
 
		YapfNotifyTrackLayoutChange(tile, FIND_FIRST_BIT(GetCrossingRailBits(tile)));
 
	}
 

	
 
	return _price.build_rail >> 1;
 
}
 

	
 

	
 
/** Build 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 CmdBuildLongRoad(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;
 

	
 
		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 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
 

	
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	tileh = GetTileSlope(tile, NULL);
 
	if (tileh != SLOPE_FLAT && (
 
				!_patches.build_on_slopes ||
 
				IsSteepSlope(tileh) ||
 
				!CanBuildDepotByTileh(p1, 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, p1);
 
		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;
 
}
 

	
 
#define M(x) (1<<(x))
 

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

	
 
			if (!((1 << b) & (M(1)|M(2)|M(4)|M(8))) &&
 
					(!(flags & DC_AI_BUILDING) || !IsTileOwner(tile, OWNER_TOWN)) &&
 
					flags & DC_AUTO) {
 
				return_cmd_error(STR_1801_MUST_REMOVE_ROAD_FIRST);
 
			}
 
			return DoCommand(tile, b, 0, flags, CMD_REMOVE_ROAD);
 
		}
 

	
 
		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;
 
	}
 
	return i + 15;
 
}
 

	
 
const byte _road_sloped_sprites[14] = {
 
	0,  0,  2,  0,
 
	0,  1,  0,  0,
 
	3,  0,  0,  0,
 
	0,  0
 
};
 

	
 
/**
 
 * Draw ground sprite and road pieces
 
 * @param ti TileInfo
 
 * @param road RoadBits to draw
 
 */
 
static void DrawRoadBits(TileInfo* ti)
 
{
 
	RoadBits road = GetRoadBits(ti->tile);
 
	const DrawRoadTileStruct *drts;
 
	PalSpriteID image = 0;
 
	Roadside roadside;
 

	
 
	if (ti->tileh != SLOPE_FLAT) {
 
		int foundation = GetRoadFoundation(ti->tileh, road);
 

	
 
		if (foundation != 0) DrawFoundation(ti, foundation);
 

	
 
		// DrawFoundation() modifies ti.
 
		// Default sloped sprites..
 
		if (ti->tileh != SLOPE_FLAT) image = _road_sloped_sprites[ti->tileh - 1] + 0x53F;
 
	}
 

	
 
	if (image == 0) image = _road_tile_sprites_1[road];
 

	
 
	roadside = GetRoadside(ti->tile);
 

	
 
	if (IsOnSnow(ti->tile)) {
 
		image += 19;
 
	} else {
 
		switch (roadside) {
 
			case ROADSIDE_BARREN:           image |= PALETTE_TO_BARE_LAND; break;
 
			case ROADSIDE_GRASS:            break;
 
			case ROADSIDE_GRASS_ROAD_WORKS: break;
 
			default:                        image -= 19; break; // Paved
 
		}
 
	}
 

	
 
	DrawGroundSprite(image);
 

	
 
	if (HasRoadWorks(ti->tile)) {
 
		// Road works
 
		DrawGroundSprite(road & ROAD_X ? SPR_EXCAVATION_X : SPR_EXCAVATION_Y);
 
		return;
 
	}
 

	
 
	// Return if full detail is disabled, or we are zoomed fully out.
 
	if (!(_display_opt & DO_FULL_DETAIL) || _cur_dpi->zoom == 2) return;
 

	
 
	// Draw extra details.
 
	for (drts = _road_display_table[roadside][road]; drts->image != 0; drts++) {
 
		int x = ti->x | drts->subcoord_x;
 
		int y = ti->y | drts->subcoord_y;
 
		byte z = ti->z;
 
		if (ti->tileh != SLOPE_FLAT) z = GetSlopeZ(x, y);
 
		AddSortableSpriteToDraw(drts->image, x, y, 2, 2, 0x10, z);
 
	}
 
}
 

	
 
static void DrawTile_Road(TileInfo *ti)
 
{
 
	switch (GetRoadTileType(ti->tile)) {
 
		case ROAD_TILE_NORMAL:
 
			DrawRoadBits(ti);
 
			break;
 

	
 
		case ROAD_TILE_CROSSING: {
 
			PalSpriteID image;
 

	
 
			if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh);
 

	
 
			image = GetRailTypeInfo(GetRailTypeCrossing(ti->tile))->base_sprites.crossing;
 

	
 
			if (GetCrossingRoadAxis(ti->tile) == AXIS_X) image++;
 
			if (IsCrossingBarred(ti->tile)) image += 2;
 

	
 
			if (IsOnSnow(ti->tile)) {
 
				image += 8;
 
			} else {
 
				switch (GetRoadside(ti->tile)) {
 
					case ROADSIDE_BARREN: image |= PALETTE_TO_BARE_LAND; break;
 
					case ROADSIDE_GRASS:  break;
 
					default:              image += 4; break; // Paved
 
				}
 
			}
 

	
 
			DrawGroundSprite(image);
 
			if (GetRailTypeCrossing(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti);
 
			break;
 
		}
 

	
 
		default:
 
		case ROAD_TILE_DEPOT: {
 
			const DrawTileSprites* dts;
 
			const DrawTileSeqStruct* dtss;
 
			uint32 palette;
 

	
 
			if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh);
 

	
 
			palette = PLAYER_SPRITE_COLOR(GetTileOwner(ti->tile));
 

	
 
			dts =  &_road_depot[GetRoadDepotDirection(ti->tile)];
 
			DrawGroundSprite(dts->ground_sprite);
 

	
 
			for (dtss = dts->seq; dtss->image != 0; dtss++) {
 
				uint32 image = dtss->image;
 

	
 
				if (_display_opt & DO_TRANS_BUILDINGS) {
 
					MAKE_TRANSPARENT(image);
 
				} else if (image & PALETTE_MODIFIER_COLOR) {
 
					image |= palette;
 
				}
 

	
 
				AddSortableSpriteToDraw(
 
					image,
 
					ti->x + dtss->delta_x, ti->y + dtss->delta_y,
 
					dtss->size_x, dtss->size_y,
 
					dtss->size_z, ti->z
 
				);
 
			}
 
			break;
 
		}
 
	}
 
	DrawBridgeMiddle(ti);
 
}
 

	
 
void DrawRoadDepotSprite(int x, int y, DiagDirection dir)
 
{
 
	uint32 palette = PLAYER_SPRITE_COLOR(_local_player);
 
	const DrawTileSprites* dts =  &_road_depot[dir];
 
	const DrawTileSeqStruct* dtss;
 

	
 
	x += 33;
 
	y += 17;
 

	
 
	DrawSprite(dts->ground_sprite, x, y);
 

	
 
	for (dtss = dts->seq; dtss->image != 0; dtss++) {
 
		Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z);
 
		uint32 image = dtss->image;
 

	
 
		if (image & PALETTE_MODIFIER_COLOR) image |= palette;
 

	
 
		DrawSprite(image, x + pt.x, y + pt.y);
 
	}
 
}
 

	
 
static uint GetSlopeZ_Road(TileIndex tile, uint x, uint y)
 
{
 
	uint z;
 
	Slope tileh = GetTileSlope(tile, &z);
 

	
 
	if (tileh == SLOPE_FLAT) return z;
 
	if (GetRoadTileType(tile) == ROAD_TILE_NORMAL) {
 
		uint f = GetRoadFoundation(tileh, GetRoadBits(tile));
 

	
 
		if (f != 0) {
 
			if (IsSteepSlope(tileh)) {
 
				z += TILE_HEIGHT;
 
			} else if (f < 15) {
 
				return z + TILE_HEIGHT; // leveled foundation
 
			}
 
			tileh = _inclined_tileh[f - 15]; // inclined foundation
 
		}
 
		return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
 
	} else {
 
		return z + TILE_HEIGHT;
 
	}
 
}
 

	
 
static Slope GetSlopeTileh_Road(TileIndex tile, Slope tileh)
 
{
 
	if (tileh == SLOPE_FLAT) return SLOPE_FLAT;
 
	if (GetRoadTileType(tile) == ROAD_TILE_NORMAL) {
 
		uint f = GetRoadFoundation(tileh, GetRoadBits(tile));
 

	
 
		if (f == 0) return tileh;
 
		if (f < 15) return SLOPE_FLAT; // leveled foundation
 
		return _inclined_tileh[f - 15]; // inclined foundation
 
	} else {
 
		return SLOPE_FLAT;
 
	}
 
}
 

	
 
static void GetAcceptedCargo_Road(TileIndex tile, AcceptedCargo ac)
 
{
 
	/* not used */
 
}
 

	
 
static void AnimateTile_Road(TileIndex tile)
 
{
 
	if (IsLevelCrossing(tile)) MarkTileDirtyByTile(tile);
 
}
 

	
 

	
 
static const Roadside _town_road_types[][2] = {
 
	{ ROADSIDE_GRASS,         ROADSIDE_GRASS },
 
	{ ROADSIDE_PAVED,         ROADSIDE_PAVED },
 
	{ ROADSIDE_PAVED,         ROADSIDE_PAVED },
 
	{ ROADSIDE_TREES,         ROADSIDE_TREES },
 
	{ ROADSIDE_STREET_LIGHTS, ROADSIDE_PAVED }
 
};
 

	
 
static const Roadside _town_road_types_2[][2] = {
 
	{ ROADSIDE_GRASS,         ROADSIDE_GRASS },
 
	{ ROADSIDE_PAVED,         ROADSIDE_PAVED },
 
	{ ROADSIDE_STREET_LIGHTS, ROADSIDE_PAVED },
 
	{ ROADSIDE_STREET_LIGHTS, ROADSIDE_PAVED },
 
	{ ROADSIDE_STREET_LIGHTS, ROADSIDE_PAVED }
 
};
 

	
 

	
 
static void TileLoop_Road(TileIndex tile)
 
{
 
	switch (_opt.landscape) {
 
		case LT_HILLY:
 
			if (IsOnSnow(tile) != (GetTileZ(tile) > _opt.snow_line)) {
 
				ToggleSnow(tile);
 
				MarkTileDirtyByTile(tile);
 
			}
 
			break;
 

	
 
		case LT_DESERT:
 
			if (GetTropicZone(tile) == TROPICZONE_DESERT && !IsOnDesert(tile)) {
 
				ToggleDesert(tile);
 
				MarkTileDirtyByTile(tile);
 
			}
 
			break;
 
	}
 

	
 
	if (GetRoadTileType(tile) == ROAD_TILE_DEPOT) return;
 

	
 
	if (!HasRoadWorks(tile)) {
 
		const Town* t = ClosestTownFromTile(tile, (uint)-1);
 
		int grp = 0;
 

	
 
		if (t != NULL) {
 
			grp = GetTownRadiusGroup(t, tile);
 

	
 
			// Show an animation to indicate road work
 
			if (t->road_build_months != 0 &&
 
					(DistanceManhattan(t->xy, tile) < 8 || grp != 0) &&
 
					GetRoadTileType(tile) == ROAD_TILE_NORMAL && (GetRoadBits(tile) == ROAD_X || GetRoadBits(tile) == ROAD_Y)) {
 
				if (GetTileSlope(tile, NULL) == SLOPE_FLAT && EnsureNoVehicle(tile) && CHANCE16(1, 20)) {
 
					StartRoadWorks(tile);
 

	
 
					SndPlayTileFx(SND_21_JACKHAMMER, tile);
 
					CreateEffectVehicleAbove(
 
						TileX(tile) * TILE_SIZE + 7,
 
						TileY(tile) * TILE_SIZE + 7,
 
						0,
 
						EV_BULLDOZER);
 
					MarkTileDirtyByTile(tile);
 
					return;
 
				}
 
			}
 
		}
 

	
 
		{
 
			/* Adjust road ground type depending on 'grp' (grp is the distance to the center) */
 
			const Roadside* new_rs = (_opt.landscape == LT_CANDY) ? _town_road_types_2[grp] : _town_road_types[grp];
 
			Roadside cur_rs = GetRoadside(tile);
 

	
 
			/* We have our desired type, do nothing */
 
			if (cur_rs == new_rs[0]) return;
 

	
 
			/* We have the pre-type of the desired type, switch to the desired type */
 
			if (cur_rs == new_rs[1]) {
 
				cur_rs = new_rs[0];
 
			/* We have barren land, install the pre-type */
 
			} else if (cur_rs == ROADSIDE_BARREN) {
 
				cur_rs = new_rs[1];
 
			/* We're totally off limits, remove any installation and make barren land */
 
			} else {
 
				cur_rs = ROADSIDE_BARREN;
 
			}
 
			SetRoadside(tile, cur_rs);
 
			MarkTileDirtyByTile(tile);
 
		}
 
	} else if (IncreaseRoadWorksCounter(tile)) {
 
		TerminateRoadWorks(tile);
 
		MarkTileDirtyByTile(tile);
 
	}
 
}
 

	
 
static void ClickTile_Road(TileIndex tile)
 
{
 
	if (GetRoadTileType(tile) == ROAD_TILE_DEPOT) ShowDepotWindow(tile, VEH_Road);
 
}
 

	
 
static const byte _road_trackbits[16] = {
 
	0x0, 0x0, 0x0, 0x10, 0x0, 0x2, 0x8, 0x1A, 0x0, 0x4, 0x1, 0x15, 0x20, 0x26, 0x29, 0x3F,
 
};
 

	
 
static uint32 GetTileTrackStatus_Road(TileIndex tile, TransportType mode)
 
{
 
	switch (mode) {
 
		case TRANSPORT_RAIL:
 
			if (!IsLevelCrossing(tile)) return 0;
 
			return GetCrossingRailBits(tile) * 0x101;
 

	
 
		case TRANSPORT_ROAD:
 
			switch (GetRoadTileType(tile)) {
 
				case ROAD_TILE_NORMAL:
 
					return HasRoadWorks(tile) ? 0 : _road_trackbits[GetRoadBits(tile)] * 0x101;
 

	
 
				case ROAD_TILE_CROSSING: {
 
					uint32 r = AxisToTrackBits(GetCrossingRoadAxis(tile)) * 0x101;
 

	
 
					if (IsCrossingBarred(tile)) r *= 0x10001;
 
					return r;
 
				}
 

	
 
				default:
 
				case ROAD_TILE_DEPOT:
 
					return AxisToTrackBits(DiagDirToAxis(GetRoadDepotDirection(tile))) * 0x101;
 
			}
 
			break;
 

	
 
		default: break;
 
	}
 
	return 0;
 
}
 

	
 
static const StringID _road_tile_strings[] = {
 
	STR_1814_ROAD,
 
	STR_1814_ROAD,
 
	STR_1814_ROAD,
 
	STR_1815_ROAD_WITH_STREETLIGHTS,
 
	STR_1814_ROAD,
 
	STR_1816_TREE_LINED_ROAD,
 
	STR_1814_ROAD,
 
	STR_1814_ROAD,
 
};
 

	
 
static void GetTileDesc_Road(TileIndex tile, TileDesc *td)
 
{
 
	td->owner = GetTileOwner(tile);
 
	switch (GetRoadTileType(tile)) {
 
		case ROAD_TILE_CROSSING: td->str = STR_1818_ROAD_RAIL_LEVEL_CROSSING; break;
 
		case ROAD_TILE_DEPOT: td->str = STR_1817_ROAD_VEHICLE_DEPOT; break;
 
		default: td->str = _road_tile_strings[GetRoadside(tile)]; break;
 
	}
 
}
 

	
 
static const byte _roadveh_enter_depot_unk0[4] = {
 
	8, 9, 0, 1
 
};
 

	
 
static uint32 VehicleEnter_Road(Vehicle *v, TileIndex tile, int x, int y)
 
{
 
	switch (GetRoadTileType(tile)) {
 
		case ROAD_TILE_CROSSING:
 
			if (v->type == VEH_Train && !IsCrossingBarred(tile)) {
 
				/* train crossing a road */
 
				SndPlayVehicleFx(SND_0E_LEVEL_CROSSING, v);
 
				BarCrossing(tile);
 
				MarkTileDirtyByTile(tile);
 
			}
 
			break;
 

	
 
		case ROAD_TILE_DEPOT:
 
			if (v->type == VEH_Road &&
 
					v->u.road.frame == 11 &&
 
					_roadveh_enter_depot_unk0[GetRoadDepotDirection(tile)] == v->u.road.state) {
 
				VehicleEnterDepot(v);
 
				return 4;
 
			}
 
			break;
 

	
 
		default: break;
 
	}
 
	return 0;
 
}
 

	
 

	
 
static void ChangeTileOwner_Road(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	if (IsLevelCrossing(tile) && GetCrossingRoadOwner(tile) == old_player) {
 
		SetCrossingRoadOwner(tile, new_player == PLAYER_SPECTATOR ? OWNER_NONE : new_player);
 
	}
 

	
 
	if (!IsTileOwner(tile, old_player)) return;
 

	
 
	if (new_player != PLAYER_SPECTATOR) {
 
		SetTileOwner(tile, new_player);
 
	} else {
 
		switch (GetRoadTileType(tile)) {
 
			case ROAD_TILE_NORMAL:
 
				SetTileOwner(tile, OWNER_NONE);
 
				break;
 

	
 
			case ROAD_TILE_CROSSING:
 
				MakeRoadNormal(tile, GetCrossingRoadOwner(tile), GetCrossingRoadBits(tile), GetTownIndex(tile));
 
				break;
 

	
 
			default:
 
			case ROAD_TILE_DEPOT:
 
				DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				break;
 
		}
 
	}
 
}
 

	
 

	
 
const TileTypeProcs _tile_type_road_procs = {
 
	DrawTile_Road,           /* draw_tile_proc */
 
	GetSlopeZ_Road,          /* get_slope_z_proc */
 
	ClearTile_Road,          /* clear_tile_proc */
 
	GetAcceptedCargo_Road,   /* get_accepted_cargo_proc */
 
	GetTileDesc_Road,        /* get_tile_desc_proc */
 
	GetTileTrackStatus_Road, /* get_tile_track_status_proc */
 
	ClickTile_Road,          /* click_tile_proc */
 
	AnimateTile_Road,        /* animate_tile_proc */
 
	TileLoop_Road,           /* tile_loop_clear */
 
	ChangeTileOwner_Road,    /* change_tile_owner_clear */
 
	NULL,                    /* get_produced_cargo_proc */
 
	VehicleEnter_Road,       /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_Road,      /* get_slope_tileh_proc */
 
};
src/road_gui.c
Show inline comments
 
deleted file
src/road_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "road_cmd.h"
 
#include "road_map.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "sound.h"
 
#include "command.h"
 
#include "variables.h"
 
//needed for catchments
 
#include "station.h"
 

	
 

	
 
static void ShowBusStationPicker(void);
 
static void ShowTruckStationPicker(void);
 
static void ShowRoadDepotPicker(void);
 

	
 
static bool _remove_button_clicked;
 

	
 
static byte _place_road_flag;
 

	
 
static byte _road_depot_orientation;
 
static byte _road_station_picker_orientation;
 

	
 
void CcPlaySound1D(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) SndPlayTileFx(SND_1F_SPLAT, tile);
 
}
 

	
 
static void PlaceRoad_NE(TileIndex tile)
 
{
 
	_place_road_flag = (_tile_fract_coords.y >= 8) + 4;
 
	VpStartPlaceSizing(tile, VPM_FIX_X);
 
}
 

	
 
static void PlaceRoad_NW(TileIndex tile)
 
{
 
	_place_road_flag = (_tile_fract_coords.x >= 8) + 0;
 
	VpStartPlaceSizing(tile, VPM_FIX_Y);
 
}
 

	
 
static void PlaceRoad_Bridge(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_X_OR_Y);
 
}
 

	
 

	
 
void CcBuildRoadTunnel(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_20_SPLAT_2, tile);
 
		ResetObjectToPlace();
 
	} else {
 
		SetRedErrorSquare(_build_tunnel_endtile);
 
	}
 
}
 

	
 
static void PlaceRoad_Tunnel(TileIndex tile)
 
{
 
	DoCommandP(tile, 0x200, 0, CcBuildRoadTunnel, CMD_BUILD_TUNNEL | CMD_AUTO | CMD_MSG(STR_5016_CAN_T_BUILD_TUNNEL_HERE));
 
}
 

	
 
static void BuildRoadOutsideStation(TileIndex tile, DiagDirection direction)
 
{
 
	tile += TileOffsByDiagDir(direction);
 
	// if there is a roadpiece just outside of the station entrance, build a connecting route
 
	if (IsTileType(tile, MP_STREET) && GetRoadTileType(tile) == ROAD_TILE_NORMAL) {
 
		DoCommandP(tile, DiagDirToRoadBits(ReverseDiagDir(direction)), 0, NULL, CMD_BUILD_ROAD);
 
	}
 
}
 

	
 
void CcRoadDepot(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_1F_SPLAT, tile);
 
		ResetObjectToPlace();
 
		BuildRoadOutsideStation(tile, p1);
 
	}
 
}
 

	
 
static void PlaceRoad_Depot(TileIndex tile)
 
{
 
	DoCommandP(tile, _road_depot_orientation, 0, CcRoadDepot, CMD_BUILD_ROAD_DEPOT | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1807_CAN_T_BUILD_ROAD_VEHICLE));
 
}
 

	
 
static void PlaceRoad_BusStation(TileIndex tile)
 
{
 
	DoCommandP(tile, _road_station_picker_orientation, RS_BUS, CcRoadDepot, CMD_BUILD_ROAD_STOP | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1808_CAN_T_BUILD_BUS_STATION));
 
}
 

	
 
static void PlaceRoad_TruckStation(TileIndex tile)
 
{
 
	DoCommandP(tile, _road_station_picker_orientation, RS_TRUCK, CcRoadDepot, CMD_BUILD_ROAD_STOP | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1809_CAN_T_BUILD_TRUCK_STATION));
 
}
 

	
 
static void PlaceRoad_DemolishArea(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, 4);
 
}
 

	
 

	
 
enum {
 
	RTW_ROAD_X        =  3,
 
	RTW_ROAD_Y        =  4,
 
	RTW_DEMOLISH      =  5,
 
	RTW_DEPOT         =  6,
 
	RTW_BUS_STATION   =  7,
 
	RTW_TRUCK_STATION =  8,
 
	RTW_BUILD_BRIDGE  =  9,
 
	RTW_BUILD_TUNNEL  = 10,
 
	RTW_REMOVE        = 11
 
};
 

	
 

	
 
typedef void OnButtonClick(Window *w);
 

	
 
static void BuildRoadClick_NE(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_ROAD_X, SPR_CURSOR_ROAD_NESW, 1, PlaceRoad_NE);
 
}
 

	
 
static void BuildRoadClick_NW(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_ROAD_Y, SPR_CURSOR_ROAD_NWSE, 1, PlaceRoad_NW);
 
}
 

	
 

	
 
static void BuildRoadClick_Demolish(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_DEMOLISH, ANIMCURSOR_DEMOLISH, 1, PlaceRoad_DemolishArea);
 
}
 

	
 
static void BuildRoadClick_Depot(Window *w)
 
{
 
	if (_game_mode == GM_EDITOR) return;
 
	if (HandlePlacePushButton(w, RTW_DEPOT, SPR_CURSOR_ROAD_DEPOT, 1, PlaceRoad_Depot)) ShowRoadDepotPicker();
 
}
 

	
 
static void BuildRoadClick_BusStation(Window *w)
 
{
 
	if (_game_mode == GM_EDITOR) return;
 
	if (HandlePlacePushButton(w, RTW_BUS_STATION, SPR_CURSOR_BUS_STATION, 1, PlaceRoad_BusStation)) ShowBusStationPicker();
 
}
 

	
 
static void BuildRoadClick_TruckStation(Window *w)
 
{
 
	if (_game_mode == GM_EDITOR) return;
 
	if (HandlePlacePushButton(w, RTW_TRUCK_STATION, SPR_CURSOR_TRUCK_STATION, 1, PlaceRoad_TruckStation)) ShowTruckStationPicker();
 
}
 

	
 
static void BuildRoadClick_Bridge(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_BUILD_BRIDGE, SPR_CURSOR_BRIDGE, 1, PlaceRoad_Bridge);
 
}
 

	
 
static void BuildRoadClick_Tunnel(Window *w)
 
{
 
	HandlePlacePushButton(w, RTW_BUILD_TUNNEL, SPR_CURSOR_ROAD_TUNNEL, 3, PlaceRoad_Tunnel);
 
}
 

	
 
static void BuildRoadClick_Remove(Window *w)
 
{
 
	if (IsWindowWidgetDisabled(w, RTW_REMOVE)) return;
 
	SetWindowDirty(w);
 
	SndPlayFx(SND_15_BEEP);
 
	ToggleWidgetLoweredState(w, RTW_REMOVE);
 
	SetSelectionRed(IsWindowWidgetLowered(w, RTW_REMOVE));
 
}
 

	
 
static void BuildRoadClick_Landscaping(Window *w)
 
{
 
	ShowTerraformToolbar();
 
}
 

	
 
static OnButtonClick* const _build_road_button_proc[] = {
 
	BuildRoadClick_NE,
 
	BuildRoadClick_NW,
 
	BuildRoadClick_Demolish,
 
	BuildRoadClick_Depot,
 
	BuildRoadClick_BusStation,
 
	BuildRoadClick_TruckStation,
 
	BuildRoadClick_Bridge,
 
	BuildRoadClick_Tunnel,
 
	BuildRoadClick_Remove,
 
	BuildRoadClick_Landscaping,
 
};
 

	
 
static void BuildRoadToolbWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: DisableWindowWidget(w, RTW_REMOVE); break;
 

	
 
	case WE_PAINT:
 
		if (IsWindowWidgetLowered(w, RTW_ROAD_X) || IsWindowWidgetLowered(w, RTW_ROAD_Y)) {
 
			EnableWindowWidget(w, RTW_REMOVE);
 
		}
 
		DrawWindowWidgets(w);
 
		break;
 

	
 
	case WE_CLICK: {
 
		if (e->we.click.widget >= 3) _build_road_button_proc[e->we.click.widget - 3](w);
 
	}	break;
 

	
 
	case WE_KEYPRESS:
 
		switch (e->we.keypress.keycode) {
 
			case '1': BuildRoadClick_NE(w);           break;
 
			case '2': BuildRoadClick_NW(w);           break;
 
			case '3': BuildRoadClick_Demolish(w);     break;
 
			case '4': BuildRoadClick_Depot(w);        break;
 
			case '5': BuildRoadClick_BusStation(w);   break;
 
			case '6': BuildRoadClick_TruckStation(w); break;
 
			case 'B': BuildRoadClick_Bridge(w);       break;
 
			case 'T': BuildRoadClick_Tunnel(w);       break;
 
			case 'R': BuildRoadClick_Remove(w);       break;
 
			case 'L': BuildRoadClick_Landscaping(w);  break;
 
			default: return;
 
		}
 
		MarkTileDirty(_thd.pos.x, _thd.pos.y); // redraw tile selection
 
		e->we.keypress.cont = false;
 
		break;
 

	
 
	case WE_PLACE_OBJ:
 
		_remove_button_clicked = IsWindowWidgetLowered(w, RTW_REMOVE);
 
		_place_proc(e->we.place.tile);
 
		break;
 

	
 
	case WE_ABORT_PLACE_OBJ:
 
		RaiseWindowButtons(w);
 
		DisableWindowWidget(w, RTW_REMOVE);
 
		InvalidateWidget(w, RTW_REMOVE);
 

	
 
		w = FindWindowById(WC_BUS_STATION, 0);
 
		if (w != NULL) WP(w,def_d).close = true;
 
		w = FindWindowById(WC_TRUCK_STATION, 0);
 
		if (w != NULL) WP(w,def_d).close = true;
 
		w = FindWindowById(WC_BUILD_DEPOT, 0);
 
		if (w != NULL) WP(w,def_d).close = true;
 
		break;
 

	
 
	case WE_PLACE_DRAG: {
 
		int sel_method;
 
		switch (e->we.place.userdata) {
 
			case 1:
 
				sel_method = VPM_FIX_X;
 
				_place_road_flag = (_place_road_flag & ~2) | ((e->we.place.pt.y & 8) >> 2);
 
				break;
 

	
 
			case 2:
 
				sel_method = VPM_FIX_Y;
 
				_place_road_flag = (_place_road_flag & ~2) | ((e->we.place.pt.x & 8) >> 2);
 
				break;
 

	
 
			case 4:
 
				sel_method = VPM_X_AND_Y;
 
				break;
 

	
 
			default:
 
				sel_method = VPM_X_OR_Y;
 
				break;
 
		}
 

	
 
		VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, sel_method);
 
		return;
 
	}
 

	
 
	case WE_PLACE_MOUSEUP:
 
		if (e->we.place.pt.x != -1) {
 
			TileIndex start_tile = e->we.place.starttile;
 
			TileIndex end_tile = e->we.place.tile;
 

	
 
			if (e->we.place.userdata == 0) {
 
				ResetObjectToPlace();
 
				ShowBuildBridgeWindow(start_tile, end_tile, 0x80);
 
			} else if (e->we.place.userdata != 4) {
 
				DoCommandP(end_tile, start_tile, _place_road_flag, CcPlaySound1D,
 
					_remove_button_clicked ?
 
					CMD_REMOVE_LONG_ROAD | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1805_CAN_T_REMOVE_ROAD_FROM) :
 
					CMD_BUILD_LONG_ROAD | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1804_CAN_T_BUILD_ROAD_HERE));
 
			} else {
 
				DoCommandP(end_tile, start_tile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA));
 
			}
 
		}
 
		break;
 

	
 
	case WE_PLACE_PRESIZE: {
 
		TileIndex tile = e->we.place.tile;
 

	
 
		DoCommand(tile, 0x200, 0, DC_AUTO, CMD_BUILD_TUNNEL);
 
		VpSetPresizeRange(tile, _build_tunnel_endtile == 0 ? tile : _build_tunnel_endtile);
 
		break;
 
	}
 

	
 
	case WE_DESTROY:
 
		if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0);
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_road_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                   STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   227,     0,    13, STR_1802_ROAD_CONSTRUCTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,     7,   228,   239,     0,    13, 0x0,                        STR_STICKY_BUTTON},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,     0,    21,    14,    35, SPR_IMG_ROAD_NW,            STR_180B_BUILD_ROAD_SECTION},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    22,    43,    14,    35, SPR_IMG_ROAD_NE,            STR_180B_BUILD_ROAD_SECTION},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    44,    65,    14,    35, SPR_IMG_DYNAMITE,           STR_018D_DEMOLISH_BUILDINGS_ETC},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    66,    87,    14,    35, SPR_IMG_ROAD_DEPOT,         STR_180C_BUILD_ROAD_VEHICLE_DEPOT},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    88,   109,    14,    35, SPR_IMG_BUS_STATION,        STR_180D_BUILD_BUS_STATION},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   110,   131,    14,    35, SPR_IMG_TRUCK_BAY,          STR_180E_BUILD_TRUCK_LOADING_BAY},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   132,   173,    14,    35, SPR_IMG_BRIDGE,             STR_180F_BUILD_ROAD_BRIDGE},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   174,   195,    14,    35, SPR_IMG_ROAD_TUNNEL,        STR_1810_BUILD_ROAD_TUNNEL},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   196,   217,    14,    35, SPR_IMG_REMOVE,             STR_1811_TOGGLE_BUILD_REMOVE_FOR},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   218,   239,    14,    35, SPR_IMG_LANDSCAPING,        STR_LANDSCAPING_TOOLBAR_TIP},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_road_desc = {
 
	WDP_ALIGN_TBR, 22, 240, 36,
 
	WC_BUILD_TOOLBAR, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
 
	_build_road_widgets,
 
	BuildRoadToolbWndProc
 
};
 

	
 
void ShowBuildRoadToolbar(void)
 
{
 
	if (!IsValidPlayer(_current_player)) return;
 

	
 
	DeleteWindowById(WC_BUILD_TOOLBAR, 0);
 
	AllocateWindowDesc(&_build_road_desc);
 
	if (_patches.link_terraform_toolbar) ShowTerraformToolbar();
 
}
 

	
 
static const Widget _build_road_scen_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                   STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   140,     0,    13, STR_1802_ROAD_CONSTRUCTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,     7,   141,   152,     0,    13, 0x0,                        STR_STICKY_BUTTON},
 

	
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,     0,    21,    14,    35, SPR_IMG_ROAD_NW,            STR_180B_BUILD_ROAD_SECTION},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    22,    43,    14,    35, SPR_IMG_ROAD_NE,            STR_180B_BUILD_ROAD_SECTION},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    44,    65,    14,    35, SPR_IMG_DYNAMITE,           STR_018D_DEMOLISH_BUILDINGS_ETC},
 
{      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,                        STR_NULL},
 
{      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,                        STR_NULL},
 
{      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,                        STR_NULL},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,    66,   107,    14,    35, SPR_IMG_BRIDGE,             STR_180F_BUILD_ROAD_BRIDGE},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   108,   129,    14,    35, SPR_IMG_ROAD_TUNNEL,        STR_1810_BUILD_ROAD_TUNNEL},
 
{     WWT_IMGBTN,   RESIZE_NONE,     7,   130,   151,    14,    35, SPR_IMG_REMOVE,             STR_1811_TOGGLE_BUILD_REMOVE_FOR},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_road_scen_desc = {
 
	WDP_AUTO, WDP_AUTO, 152, 36,
 
	WC_SCEN_BUILD_ROAD, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
 
	_build_road_scen_widgets,
 
	BuildRoadToolbWndProc
 
};
 

	
 
void ShowBuildRoadScenToolbar(void)
 
{
 
	AllocateWindowDescFront(&_build_road_scen_desc, 0);
 
}
 

	
 
static void BuildRoadDepotWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: LowerWindowWidget(w, _road_depot_orientation + 3); break;
 

	
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 

	
 
		DrawRoadDepotSprite(70, 17, DIAGDIR_NE);
 
		DrawRoadDepotSprite(70, 69, DIAGDIR_SE);
 
		DrawRoadDepotSprite( 2, 69, DIAGDIR_SW);
 
		DrawRoadDepotSprite( 2, 17, DIAGDIR_NW);
 
		break;
 

	
 
	case WE_CLICK: {
 
		switch (e->we.click.widget) {
 
		case 3: case 4: case 5: case 6:
 
			RaiseWindowWidget(w, _road_depot_orientation + 3);
 
			_road_depot_orientation = e->we.click.widget - 3;
 
			LowerWindowWidget(w, _road_depot_orientation + 3);
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
	}	break;
 

	
 
	case WE_MOUSELOOP:
 
		if (WP(w,def_d).close) DeleteWindow(w);
 
		break;
 

	
 
	case WE_DESTROY:
 
		if (!WP(w,def_d).close) ResetObjectToPlace();
 
		break;
 
	}
 
}
 

	
 
static const Widget _build_road_depot_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   139,     0,    13, STR_1806_ROAD_DEPOT_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   139,    14,   121, 0x0,                             STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    17,    66, 0x0,                             STR_1813_SELECT_ROAD_VEHICLE_DEPOT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    69,   118, 0x0,                             STR_1813_SELECT_ROAD_VEHICLE_DEPOT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    69,   118, 0x0,                             STR_1813_SELECT_ROAD_VEHICLE_DEPOT},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    17,    66, 0x0,                             STR_1813_SELECT_ROAD_VEHICLE_DEPOT},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _build_road_depot_desc = {
 
	WDP_AUTO, WDP_AUTO, 140, 122,
 
	WC_BUILD_DEPOT, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_road_depot_widgets,
 
	BuildRoadDepotWndProc
 
};
 

	
 
static void ShowRoadDepotPicker(void)
 
{
 
	AllocateWindowDesc(&_build_road_depot_desc);
 
}
 

	
 
static void RoadStationPickerWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE:
 
		LowerWindowWidget(w, _road_station_picker_orientation + 3);
 
		LowerWindowWidget(w, _station_show_coverage + 7);
 
		break;
 

	
 
	case WE_PAINT: {
 
		int image;
 

	
 
		if (WP(w,def_d).close) return;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		if (_station_show_coverage) {
 
			int rad = _patches.modified_catchment ? CA_TRUCK /* = CA_BUS */ : 4;
 
			SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
 
		} else {
 
			SetTileSelectSize(1, 1);
 
		}
 

	
 
		image = (w->window_class == WC_BUS_STATION) ? 0x47 : 0x43;
 

	
 
		StationPickerDrawSprite(103, 35, 0, image);
 
		StationPickerDrawSprite(103, 85, 0, image+1);
 
		StationPickerDrawSprite(35, 85, 0, image+2);
 
		StationPickerDrawSprite(35, 35, 0, image+3);
 

	
 
		DrawStationCoverageAreaText(2, 146,
 
			((w->window_class == WC_BUS_STATION) ? (1<<CT_PASSENGERS) : ~(1<<CT_PASSENGERS)),
 
			3);
 

	
 
	} break;
 

	
 
	case WE_CLICK: {
 
		switch (e->we.click.widget) {
 
		case 3: case 4: case 5: case 6:
 
			RaiseWindowWidget(w, _road_station_picker_orientation + 3);
 
			_road_station_picker_orientation = e->we.click.widget - 3;
 
			LowerWindowWidget(w, _road_station_picker_orientation + 3);
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 
		case 7: case 8:
 
			RaiseWindowWidget(w, _station_show_coverage + 7);
 
			_station_show_coverage = e->we.click.widget - 7;
 
			LowerWindowWidget(w, _station_show_coverage + 7);
 
			SndPlayFx(SND_15_BEEP);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_MOUSELOOP: {
 
		if (WP(w,def_d).close) {
 
			DeleteWindow(w);
 
			return;
 
		}
 

	
 
		CheckRedrawStationCoverage(w);
 
	} break;
 

	
 
	case WE_DESTROY:
 
		if (!WP(w,def_d).close) ResetObjectToPlace();
 
		break;
 
	}
 
}
 

	
 
static const Widget _bus_station_picker_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   139,     0,    13, STR_3042_BUS_STATION_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   139,    14,   176, 0x0,                              STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    17,    66, 0x0,                              STR_3051_SELECT_BUS_STATION_ORIENTATION},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    69,   118, 0x0,                              STR_3051_SELECT_BUS_STATION_ORIENTATION},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    69,   118, 0x0,                              STR_3051_SELECT_BUS_STATION_ORIENTATION},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    17,    66, 0x0,                              STR_3051_SELECT_BUS_STATION_ORIENTATION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    10,    69,   133,   144, STR_02DB_OFF,                     STR_3065_DON_T_HIGHLIGHT_COVERAGE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    70,   129,   133,   144, STR_02DA_ON,                      STR_3064_HIGHLIGHT_COVERAGE_AREA},
 
{      WWT_LABEL,   RESIZE_NONE,     7,     0,   139,   120,   133, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _bus_station_picker_desc = {
 
	WDP_AUTO, WDP_AUTO, 140, 177,
 
	WC_BUS_STATION, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_bus_station_picker_widgets,
 
	RoadStationPickerWndProc
 
};
 

	
 
static void ShowBusStationPicker(void)
 
{
 
	AllocateWindowDesc(&_bus_station_picker_desc);
 
}
 

	
 
static const Widget _truck_station_picker_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   139,     0,    13, STR_3043_TRUCK_STATION_ORIENT,    STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,     7,     0,   139,    14,   176, 0x0,                              STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    17,    66, 0x0,                              STR_3052_SELECT_TRUCK_LOADING_BAY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    71,   136,    69,   118, 0x0,                              STR_3052_SELECT_TRUCK_LOADING_BAY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    69,   118, 0x0,                              STR_3052_SELECT_TRUCK_LOADING_BAY},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     3,    68,    17,    66, 0x0,                              STR_3052_SELECT_TRUCK_LOADING_BAY},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    10,    69,   133,   144, STR_02DB_OFF,                     STR_3065_DON_T_HIGHLIGHT_COVERAGE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    70,   129,   133,   144, STR_02DA_ON,                      STR_3064_HIGHLIGHT_COVERAGE_AREA},
 
{      WWT_LABEL,   RESIZE_NONE,     7,     0,   139,   120,   133, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _truck_station_picker_desc = {
 
	WDP_AUTO, WDP_AUTO, 140, 177,
 
	WC_TRUCK_STATION, WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_truck_station_picker_widgets,
 
	RoadStationPickerWndProc
 
};
 

	
 
static void ShowTruckStationPicker(void)
 
{
 
	AllocateWindowDesc(&_truck_station_picker_desc);
 
}
 

	
 
void InitializeRoadGui(void)
 
{
 
	_road_depot_orientation = 3;
 
	_road_station_picker_orientation = 3;
 
}
src/road_map.c
Show inline comments
 
deleted file
src/road_map.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "functions.h"
 
#include "road_map.h"
 
#include "station.h"
 
#include "tunnel_map.h"
 
#include "station_map.h"
 
#include "depot.h"
 

	
 

	
 
RoadBits GetAnyRoadBits(TileIndex tile)
 
{
 
	switch (GetTileType(tile)) {
 
		case MP_STREET:
 
			switch (GetRoadTileType(tile)) {
 
				default:
 
				case ROAD_TILE_NORMAL:   return GetRoadBits(tile);
 
				case ROAD_TILE_CROSSING: return GetCrossingRoadBits(tile);
 
				case ROAD_TILE_DEPOT:    return DiagDirToRoadBits(GetRoadDepotDirection(tile));
 
			}
 

	
 
		case MP_STATION:
 
			if (!IsRoadStopTile(tile)) return 0;
 
			return DiagDirToRoadBits(GetRoadStopDir(tile));
 

	
 
		case MP_TUNNELBRIDGE:
 
			if (IsTunnel(tile)) {
 
				if (GetTunnelTransportType(tile) != TRANSPORT_ROAD) return 0;
 
				return DiagDirToRoadBits(ReverseDiagDir(GetTunnelDirection(tile)));
 
			} else {
 
				if (GetBridgeTransportType(tile) != TRANSPORT_ROAD) return 0;
 
				return DiagDirToRoadBits(ReverseDiagDir(GetBridgeRampDirection(tile)));
 
			}
 

	
 
		default: return 0;
 
	}
 
}
 

	
 

	
 
TrackBits GetAnyRoadTrackBits(TileIndex tile)
 
{
 
	uint32 r;
 

	
 
	// Don't allow local authorities to build roads through road depots or road stops.
 
	if ((IsTileType(tile, MP_STREET) && IsTileDepotType(tile, TRANSPORT_ROAD)) || IsTileType(tile, MP_STATION)) {
 
		return 0;
 
	}
 

	
 
	r = GetTileTrackStatus(tile, TRANSPORT_ROAD);
 
	return (byte)(r | (r >> 8));
 
}
src/roadveh_cmd.c
Show inline comments
 
deleted file
src/roadveh_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include <limits.h>
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "road_map.h"
 
#include "roadveh.h"
 
#include "station_map.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "vehicle.h"
 
#include "engine.h"
 
#include "command.h"
 
#include "station.h"
 
#include "news.h"
 
#include "pathfind.h"
 
#include "npf.h"
 
#include "player.h"
 
#include "sound.h"
 
#include "depot.h"
 
#include "bridge.h"
 
#include "tunnel_map.h"
 
#include "bridge_map.h"
 
#include "vehicle_gui.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_text.h"
 
#include "newgrf_sound.h"
 
#include "yapf/yapf.h"
 
#include "date.h"
 

	
 
static const uint16 _roadveh_images[63] = {
 
	0xCD4, 0xCDC, 0xCE4, 0xCEC, 0xCF4, 0xCFC, 0xD0C, 0xD14,
 
	0xD24, 0xD1C, 0xD2C, 0xD04, 0xD1C, 0xD24, 0xD6C, 0xD74,
 
	0xD7C, 0xC14, 0xC1C, 0xC24, 0xC2C, 0xC34, 0xC3C, 0xC4C,
 
	0xC54, 0xC64, 0xC5C, 0xC6C, 0xC44, 0xC5C, 0xC64, 0xCAC,
 
	0xCB4, 0xCBC, 0xD94, 0xD9C, 0xDA4, 0xDAC, 0xDB4, 0xDBC,
 
	0xDCC, 0xDD4, 0xDE4, 0xDDC, 0xDEC, 0xDC4, 0xDDC, 0xDE4,
 
	0xE2C, 0xE34, 0xE3C, 0xC14, 0xC1C, 0xC2C, 0xC3C, 0xC4C,
 
	0xC5C, 0xC64, 0xC6C, 0xC74, 0xC84, 0xC94, 0xCA4
 
};
 

	
 
static const uint16 _roadveh_full_adder[63] = {
 
	 0,  88,   0,   0,   0,   0,  48,  48,
 
	48,  48,   0,   0,  64,  64,   0,  16,
 
	16,   0,  88,   0,   0,   0,   0,  48,
 
	48,  48,  48,   0,   0,  64,  64,   0,
 
	16,  16,   0,  88,   0,   0,   0,   0,
 
	48,  48,  48,  48,   0,   0,  64,  64,
 
	 0,  16,  16,   0,   8,   8,   8,   8,
 
	 0,   0,   0,   8,   8,   8,   8
 
};
 

	
 

	
 
static const uint16 _road_veh_fp_ax_and[4] = {
 
	0x1009, 0x16, 0x520, 0x2A00
 
};
 

	
 
static const byte _road_reverse_table[4] = {
 
	6, 7, 14, 15
 
};
 

	
 
static const uint16 _road_pf_table_3[4] = {
 
	0x910, 0x1600, 0x2005, 0x2A
 
};
 

	
 
int GetRoadVehImage(const Vehicle* v, Direction direction)
 
{
 
	int img = v->spritenum;
 
	int image;
 

	
 
	if (is_custom_sprite(img)) {
 
		image = GetCustomVehicleSprite(v, direction);
 
		if (image != 0) return image;
 
		img = orig_road_vehicle_info[v->engine_type - ROAD_ENGINES_INDEX].image_index;
 
	}
 

	
 
	image = direction + _roadveh_images[img];
 
	if (v->cargo_count >= v->cargo_cap / 2) image += _roadveh_full_adder[img];
 
	return image;
 
}
 

	
 
void DrawRoadVehEngine(int x, int y, EngineID engine, uint32 image_ormod)
 
{
 
	int spritenum = RoadVehInfo(engine)->image_index;
 

	
 
	if (is_custom_sprite(spritenum)) {
 
		int sprite = GetCustomVehicleIcon(engine, DIR_W);
 

	
 
		if (sprite != 0) {
 
			DrawSprite(sprite | image_ormod, x, y);
 
			return;
 
		}
 
		spritenum = orig_road_vehicle_info[engine - ROAD_ENGINES_INDEX].image_index;
 
	}
 
	DrawSprite((6 + _roadveh_images[spritenum]) | image_ormod, x, y);
 
}
 

	
 
static int32 EstimateRoadVehCost(EngineID engine_type)
 
{
 
	return ((_price.roadveh_base >> 3) * RoadVehInfo(engine_type)->base_cost) >> 5;
 
}
 

	
 
/** Build a road vehicle.
 
 * @param tile tile of depot where road vehicle is built
 
 * @param p1 bus/truck type being built (engine)
 
 * @param p2 bit 0 when set, the unitnumber will be 0, otherwise it will be a free number
 
 */
 
int32 CmdBuildRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 cost;
 
	Vehicle *v;
 
	UnitID unit_num;
 
	Engine *e;
 

	
 
	if (!IsEngineBuildable(p1, VEH_Road, _current_player)) return_cmd_error(STR_ENGINE_NOT_BUILDABLE);
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	cost = EstimateRoadVehCost(p1);
 
	if (flags & DC_QUERY_COST) return cost;
 

	
 
	/* The ai_new queries the vehicle cost before building the route,
 
	 * so we must check against cheaters no sooner than now. --pasky */
 
	if (!IsTileDepotType(tile, TRANSPORT_ROAD)) return CMD_ERROR;
 
	if (!IsTileOwner(tile, _current_player)) return CMD_ERROR;
 

	
 
	v = AllocateVehicle();
 
	if (v == NULL || IsOrderPoolFull())
 
		return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
	/* find the first free roadveh id */
 
	unit_num = HASBIT(p2, 0) ? 0 : GetFreeUnitNumber(VEH_Road);
 
	if (unit_num > _patches.max_roadveh)
 
		return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
	if (flags & DC_EXEC) {
 
		int x;
 
		int y;
 

	
 
		const RoadVehicleInfo *rvi = RoadVehInfo(p1);
 

	
 
		v->unitnumber = unit_num;
 
		v->direction = 0;
 
		v->owner = _current_player;
 

	
 
		v->tile = tile;
 
		x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
 
		y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
 
		v->x_pos = x;
 
		v->y_pos = y;
 
		v->z_pos = GetSlopeZ(x,y);
 
		v->z_height = 6;
 

	
 
		v->u.road.state = 254;
 
		v->vehstatus = VS_HIDDEN|VS_STOPPED|VS_DEFPAL;
 

	
 
		v->spritenum = rvi->image_index;
 
		v->cargo_type = rvi->cargo_type;
 
		v->cargo_subtype = 0;
 
		v->cargo_cap = rvi->capacity;
 
//		v->cargo_count = 0;
 
		v->value = cost;
 
//		v->day_counter = 0;
 
//		v->next_order_param = v->next_order = 0;
 
//		v->load_unload_time_rem = 0;
 
//		v->progress = 0;
 

	
 
//	v->u.road.unk2 = 0;
 
//	v->u.road.overtaking = 0;
 

	
 
		v->last_station_visited = INVALID_STATION;
 
		v->max_speed = rvi->max_speed;
 
		v->engine_type = (byte)p1;
 

	
 
		e = GetEngine(p1);
 
		v->reliability = e->reliability;
 
		v->reliability_spd_dec = e->reliability_spd_dec;
 
		v->max_age = e->lifelength * 366;
 
		_new_vehicle_id = v->index;
 

	
 
		v->string_id = STR_SV_ROADVEH_NAME;
 

	
 
		v->service_interval = _patches.servint_roadveh;
 

	
 
		v->date_of_last_service = _date;
 
		v->build_year = _cur_year;
 

	
 
		v->type = VEH_Road;
 
		v->cur_image = 0xC15;
 
		v->random_bits = VehicleRandomBits();
 

	
 
		VehiclePositionChanged(v);
 
		GetPlayer(_current_player)->num_engines[p1]++;
 

	
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
		RebuildVehicleLists();
 
		InvalidateWindow(WC_COMPANY, v->owner);
 
		if (IsLocalPlayer())
 
			InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Road); // updates the replace Road window
 
	}
 

	
 
	return cost;
 
}
 

	
 
/** Start/Stop a road vehicle.
 
 * @param tile unused
 
 * @param p1 road vehicle ID to start/stop
 
 * @param p2 unused
 
 */
 
int32 CmdStartStopRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	uint16 callback;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Road || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	/* Check if this road veh can be started/stopped. The callback will fail or
 
	 * return 0xFF if it can. */
 
	callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
 
	if (callback != CALLBACK_FAILED && callback != 0xFF) {
 
		StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
 
		return_cmd_error(error);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		if (IsRoadVehInDepotStopped(v)) {
 
			DeleteVehicleNews(p1, STR_9016_ROAD_VEHICLE_IS_WAITING);
 
		}
 

	
 
		v->vehstatus ^= VS_STOPPED;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
	}
 

	
 
	return 0;
 
}
 

	
 
void ClearSlot(Vehicle *v)
 
{
 
	RoadStop *rs = v->u.road.slot;
 
	if (v->u.road.slot == NULL) return;
 

	
 
	v->u.road.slot = NULL;
 
	v->u.road.slot_age = 0;
 

	
 
	assert(rs->num_vehicles != 0);
 
	rs->num_vehicles--;
 

	
 
	DEBUG(ms, 3, "Clearing slot at 0x%X", rs->xy);
 
}
 

	
 
/** Sell a road vehicle.
 
 * @param tile unused
 
 * @param p1 vehicle ID to be sold
 
 * @param p2 unused
 
 */
 
int32 CmdSellRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Road || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	if (!IsRoadVehInDepotStopped(v)) {
 
		return_cmd_error(STR_9013_MUST_BE_STOPPED_INSIDE);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		// Invalidate depot
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		RebuildVehicleLists();
 
		InvalidateWindow(WC_COMPANY, v->owner);
 
		DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 
		ClearSlot(v);
 
		DeleteDepotHighlightOfVehicle(v);
 
		DeleteVehicle(v);
 
		if (IsLocalPlayer()) InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Road);
 
	}
 

	
 
	return -(int32)v->value;
 
}
 

	
 
typedef struct RoadFindDepotData {
 
	uint best_length;
 
	TileIndex tile;
 
	byte owner;
 
} RoadFindDepotData;
 

	
 
static const DiagDirection _road_pf_directions[] = {
 
	DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE, 255, 255,
 
	DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE, 255, 255
 
};
 

	
 
static bool EnumRoadSignalFindDepot(TileIndex tile, void* data, int track, uint length, byte* state)
 
{
 
	RoadFindDepotData* rfdd = data;
 

	
 
	tile += TileOffsByDiagDir(_road_pf_directions[track]);
 

	
 
	if (IsTileType(tile, MP_STREET) &&
 
			GetRoadTileType(tile) == ROAD_TILE_DEPOT &&
 
			IsTileOwner(tile, rfdd->owner) &&
 
			length < rfdd->best_length) {
 
		rfdd->best_length = length;
 
		rfdd->tile = tile;
 
	}
 
	return false;
 
}
 

	
 
static const Depot* FindClosestRoadDepot(const Vehicle* v)
 
{
 
	TileIndex tile = v->tile;
 

	
 
	if (_patches.yapf.road_use_yapf) {
 
		Depot* ret = YapfFindNearestRoadDepot(v);
 
		return ret;
 
	} else if (_patches.new_pathfinding_all) {
 
		NPFFoundTargetData ftd;
 
		/* See where we are now */
 
		Trackdir trackdir = GetVehicleTrackdir(v);
 

	
 
		ftd = NPFRouteToDepotBreadthFirst(v->tile, trackdir, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE);
 
		if (ftd.best_bird_dist == 0) {
 
			return GetDepotByTile(ftd.node.tile); /* Target found */
 
		} else {
 
			return NULL; /* Target not found */
 
		}
 
		/* We do not search in two directions here, why should we? We can't reverse right now can we? */
 
	} else {
 
		RoadFindDepotData rfdd;
 
		DiagDirection i;
 

	
 
		rfdd.owner = v->owner;
 
		rfdd.best_length = (uint)-1;
 

	
 
		/* search in all directions */
 
		for (i = 0; i != 4; i++) {
 
			FollowTrack(tile, 0x2000 | TRANSPORT_ROAD, i, EnumRoadSignalFindDepot, NULL, &rfdd);
 
		}
 

	
 
		if (rfdd.best_length == (uint)-1) return NULL;
 

	
 
		return GetDepotByTile(rfdd.tile);
 
	}
 
}
 

	
 
/** Send a road vehicle to the depot.
 
 * @param tile unused
 
 * @param p1 vehicle ID to send to the depot
 
 * @param p2 various bitmasked elements
 
 * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h)
 
 * - p2 bit 8-10 - VLW flag (for mass goto depot)
 
 */
 
int32 CmdSendRoadVehToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	const Depot *dep;
 

	
 
	if (p2 & DEPOT_MASS_SEND) {
 
		/* Mass goto depot requested */
 
		if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
 
		return SendAllVehiclesToDepot(VEH_Road, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1);
 
	}
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Road || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
 

	
 
	if (IsRoadVehInDepot(v)) return CMD_ERROR;
 

	
 
	/* If the current orders are already goto-depot */
 
	if (v->current_order.type == OT_GOTO_DEPOT) {
 
		if (!!(p2 & DEPOT_SERVICE) == HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT)) {
 
			/* We called with a different DEPOT_SERVICE setting.
 
			 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
 
			 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
 
			if (flags & DC_EXEC) {
 
				TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
			}
 
			return 0;
 
		}
 

	
 
		if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
 
		if (flags & DC_EXEC) {
 
			/* If the orders to 'goto depot' are in the orders list (forced servicing),
 
			 * then skip to the next order; effectively cancelling this forced service */
 
			if (HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS))
 
				v->cur_order_index++;
 

	
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
		return 0;
 
	}
 

	
 
	dep = FindClosestRoadDepot(v);
 
	if (dep == NULL) return_cmd_error(STR_9019_UNABLE_TO_FIND_LOCAL_DEPOT);
 

	
 
	if (flags & DC_EXEC) {
 
		ClearSlot(v);
 
		v->current_order.type = OT_GOTO_DEPOT;
 
		v->current_order.flags = OF_NON_STOP;
 
		if (!(p2 & DEPOT_SERVICE)) SETBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
		v->current_order.refit_cargo = CT_INVALID;
 
		v->current_order.dest = dep->index;
 
		v->dest_tile = dep->xy;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Turn a roadvehicle around.
 
 * @param tile unused
 
 * @param p1 vehicle ID to turn
 
 * @param p2 unused
 
 */
 
int32 CmdTurnRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Road || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (v->vehstatus & VS_STOPPED ||
 
			v->u.road.crashed_ctr != 0 ||
 
			v->breakdown_ctr != 0 ||
 
			v->u.road.overtaking != 0 ||
 
			v->u.road.state == 255 ||
 
			IsRoadVehInDepot(v) ||
 
			v->cur_speed < 5) {
 
		return CMD_ERROR;
 
	}
 

	
 
	if (IsTunnelTile(v->tile) && DirToDiagDir(v->direction) == GetTunnelDirection(v->tile)) return CMD_ERROR;
 
	if (IsBridgeTile(v->tile) && DirToDiagDir(v->direction) == GetBridgeRampDirection(v->tile)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) v->u.road.reverse_ctr = 180;
 

	
 
	return 0;
 
}
 

	
 

	
 
static void MarkRoadVehDirty(Vehicle *v)
 
{
 
	v->cur_image = GetRoadVehImage(v, v->direction);
 
	MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
 
}
 

	
 
static void UpdateRoadVehDeltaXY(Vehicle *v)
 
{
 
#define MKIT(a,b,c,d) ((a&0xFF)<<24) | ((b&0xFF)<<16) | ((c&0xFF)<<8) | ((d&0xFF)<<0)
 
	static const uint32 _delta_xy_table[8] = {
 
		MKIT(3, 3, -1, -1),
 
		MKIT(3, 7, -1, -3),
 
		MKIT(3, 3, -1, -1),
 
		MKIT(7, 3, -3, -1),
 
		MKIT(3, 3, -1, -1),
 
		MKIT(3, 7, -1, -3),
 
		MKIT(3, 3, -1, -1),
 
		MKIT(7, 3, -3, -1),
 
	};
 
#undef MKIT
 
	uint32 x = _delta_xy_table[v->direction];
 
	v->x_offs        = GB(x,  0, 8);
 
	v->y_offs        = GB(x,  8, 8);
 
	v->sprite_width  = GB(x, 16, 8);
 
	v->sprite_height = GB(x, 24, 8);
 
}
 

	
 
static void ClearCrashedStation(Vehicle *v)
 
{
 
	RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
 

	
 
	// mark station as not busy
 
	CLRBIT(rs->status, 7);
 

	
 
	// free parking bay
 
	SETBIT(rs->status, HASBIT(v->u.road.state, 1) ? 1 : 0);
 
}
 

	
 
static void RoadVehDelete(Vehicle *v)
 
{
 
	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
	RebuildVehicleLists();
 
	InvalidateWindow(WC_COMPANY, v->owner);
 

	
 
	if (IsTileType(v->tile, MP_STATION)) ClearCrashedStation(v);
 

	
 
	BeginVehicleMove(v);
 
	EndVehicleMove(v);
 

	
 
	ClearSlot(v);
 
	DeleteVehicle(v);
 
}
 

	
 
static byte SetRoadVehPosition(Vehicle *v, int x, int y)
 
{
 
	byte new_z, old_z;
 

	
 
	// need this hint so it returns the right z coordinate on bridges.
 
	v->x_pos = x;
 
	v->y_pos = y;
 
	new_z = GetSlopeZ(x, y);
 

	
 
	old_z = v->z_pos;
 
	v->z_pos = new_z;
 

	
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
	return old_z;
 
}
 

	
 
static void RoadVehSetRandomDirection(Vehicle *v)
 
{
 
	static const DirDiff delta[] = {
 
		DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
 
	};
 

	
 
	uint32 r = Random();
 

	
 
	v->direction = ChangeDir(v->direction, delta[r & 3]);
 
	BeginVehicleMove(v);
 
	UpdateRoadVehDeltaXY(v);
 
	v->cur_image = GetRoadVehImage(v, v->direction);
 
	SetRoadVehPosition(v, v->x_pos, v->y_pos);
 
}
 

	
 
static void RoadVehIsCrashed(Vehicle *v)
 
{
 
	v->u.road.crashed_ctr++;
 
	if (v->u.road.crashed_ctr == 2) {
 
		CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
 
	} else if (v->u.road.crashed_ctr <= 45) {
 
		if ((v->tick_counter & 7) == 0) RoadVehSetRandomDirection(v);
 
	} else if (v->u.road.crashed_ctr >= 2220) {
 
		RoadVehDelete(v);
 
	}
 
}
 

	
 
static void* EnumCheckRoadVehCrashTrain(Vehicle* v, void* data)
 
{
 
	const Vehicle* u = data;
 

	
 
	return
 
		v->type == VEH_Train &&
 
		myabs(v->z_pos - u->z_pos) <= 6 &&
 
		myabs(v->x_pos - u->x_pos) <= 4 &&
 
		myabs(v->y_pos - u->y_pos) <= 4 ?
 
			v : NULL;
 
}
 

	
 
static void RoadVehCrash(Vehicle *v)
 
{
 
	uint16 pass;
 

	
 
	v->u.road.crashed_ctr++;
 
	v->vehstatus |= VS_CRASHED;
 
	ClearSlot(v);
 

	
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 

	
 
	pass = 1;
 
	if (v->cargo_type == CT_PASSENGERS) pass += v->cargo_count;
 
	v->cargo_count = 0;
 

	
 
	SetDParam(0, pass);
 
	AddNewsItem(
 
		(pass == 1) ?
 
			STR_9031_ROAD_VEHICLE_CRASH_DRIVER : STR_9032_ROAD_VEHICLE_CRASH_DIE,
 
		NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
 
		v->index,
 
		0
 
	);
 

	
 
	ModifyStationRatingAround(v->tile, v->owner, -160, 22);
 
	SndPlayVehicleFx(SND_12_EXPLOSION, v);
 
}
 

	
 
static void RoadVehCheckTrainCrash(Vehicle *v)
 
{
 
	TileIndex tile;
 

	
 
	if (v->u.road.state == 255) return;
 

	
 
	tile = v->tile;
 

	
 
	if (!IsLevelCrossingTile(tile)) return;
 

	
 
	if (VehicleFromPos(tile, v, EnumCheckRoadVehCrashTrain) != NULL)
 
		RoadVehCrash(v);
 
}
 

	
 
static void HandleBrokenRoadVeh(Vehicle *v)
 
{
 
	if (v->breakdown_ctr != 1) {
 
		v->breakdown_ctr = 1;
 
		v->cur_speed = 0;
 

	
 
		if (v->breakdowns_since_last_service != 255)
 
			v->breakdowns_since_last_service++;
 

	
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
 
			SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
 
				SND_0F_VEHICLE_BREAKDOWN : SND_35_COMEDY_BREAKDOWN, v);
 
		}
 

	
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
 
			if (u != NULL) u->u.special.unk0 = v->breakdown_delay * 2;
 
		}
 
	}
 

	
 
	if ((v->tick_counter & 1) == 0) {
 
		if (--v->breakdown_delay == 0) {
 
			v->breakdown_ctr = 0;
 
			InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		}
 
	}
 
}
 

	
 
static void ProcessRoadVehOrder(Vehicle *v)
 
{
 
	const Order *order;
 

	
 
	switch (v->current_order.type) {
 
		case OT_GOTO_DEPOT:
 
			// Let a depot order in the orderlist interrupt.
 
			if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return;
 
			if (v->current_order.flags & OF_SERVICE_IF_NEEDED &&
 
					!VehicleNeedsService(v)) {
 
				v->cur_order_index++;
 
			}
 
			break;
 

	
 
		case OT_LOADING:
 
		case OT_LEAVESTATION:
 
			return;
 

	
 
		default: break;
 
	}
 

	
 
	if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
 

	
 
	order = GetVehicleOrder(v, v->cur_order_index);
 

	
 
	if (order == NULL) {
 
		v->current_order.type = OT_NOTHING;
 
		v->current_order.flags = 0;
 
		v->dest_tile = 0;
 
		ClearSlot(v);
 
		return;
 
	}
 

	
 
	if (order->type  == v->current_order.type &&
 
			order->flags == v->current_order.flags &&
 
			order->dest  == v->current_order.dest) {
 
		return;
 
	}
 

	
 
	v->current_order = *order;
 

	
 
	switch (order->type) {
 
		case OT_GOTO_STATION: {
 
			const RoadStop* rs;
 

	
 
			if (order->dest == v->last_station_visited) {
 
				v->last_station_visited = INVALID_STATION;
 
			}
 

	
 
			rs = GetPrimaryRoadStop(
 
				GetStation(order->dest),
 
				v->cargo_type == CT_PASSENGERS ? RS_BUS : RS_TRUCK
 
			);
 

	
 
			if (rs != NULL) {
 
				TileIndex dest = rs->xy;
 
				uint mindist = DistanceManhattan(v->tile, rs->xy);
 

	
 
				for (rs = rs->next; rs != NULL; rs = rs->next) {
 
					uint dist = DistanceManhattan(v->tile, rs->xy);
 

	
 
					if (dist < mindist) {
 
						mindist = dist;
 
						dest = rs->xy;
 
					}
 
				}
 
				v->dest_tile = dest;
 
			} else {
 
				// There is no stop left at the station, so don't even TRY to go there
 
				v->cur_order_index++;
 
				v->dest_tile = 0;
 
			}
 
			break;
 
		}
 

	
 
		case OT_GOTO_DEPOT:
 
			v->dest_tile = GetDepot(order->dest)->xy;
 
			break;
 

	
 
		default:
 
			v->dest_tile = 0;
 
			break;
 
	}
 

	
 
	InvalidateVehicleOrder(v);
 
}
 

	
 
static void HandleRoadVehLoading(Vehicle *v)
 
{
 
	switch (v->current_order.type) {
 
		case OT_LOADING: {
 
			Order b;
 

	
 
			if (--v->load_unload_time_rem != 0) return;
 

	
 
			if (CanFillVehicle(v) && (v->current_order.flags & OF_FULL_LOAD ||
 
					(_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED)))) {
 
				SET_EXPENSES_TYPE(EXPENSES_ROADVEH_INC);
 
				if (LoadUnloadVehicle(v, false)) {
 
					InvalidateWindow(WC_ROADVEH_LIST, v->owner);
 
					MarkRoadVehDirty(v);
 
				}
 
				return;
 
			}
 

	
 
			b = v->current_order;
 
			v->current_order.type = OT_LEAVESTATION;
 
			v->current_order.flags = 0;
 
			if (!(b.flags & OF_NON_STOP)) return;
 
			break;
 
		}
 

	
 
		case OT_DUMMY: break;
 

	
 
		default: return;
 
	}
 

	
 
	v->cur_order_index++;
 
	InvalidateVehicleOrder(v);
 
}
 

	
 
static void StartRoadVehSound(const Vehicle* v)
 
{
 
	if (!PlayVehicleSound(v, VSE_START)) {
 
		SoundFx s = RoadVehInfo(v->engine_type)->sfx;
 
		if (s == SND_19_BUS_START_PULL_AWAY && (v->tick_counter & 3) == 0)
 
			s = SND_1A_BUS_START_PULL_AWAY_WITH_HORN;
 
		SndPlayVehicleFx(s, v);
 
	}
 
}
 

	
 
typedef struct RoadVehFindData {
 
	int x;
 
	int y;
 
	const Vehicle* veh;
 
	Direction dir;
 
} RoadVehFindData;
 

	
 
static void* EnumCheckRoadVehClose(Vehicle *v, void* data)
 
{
 
	static const int8 dist_x[] = { -4, -8, -4, -1, 4, 8, 4, 1 };
 
	static const int8 dist_y[] = { -4, -1, 4, 8, 4, 1, -4, -8 };
 

	
 
	const RoadVehFindData* rvf = data;
 

	
 
	short x_diff = v->x_pos - rvf->x;
 
	short y_diff = v->y_pos - rvf->y;
 

	
 
	return
 
		rvf->veh != v &&
 
		v->type == VEH_Road &&
 
		!IsRoadVehInDepot(v) &&
 
		myabs(v->z_pos - rvf->veh->z_pos) < 6 &&
 
		v->direction == rvf->dir &&
 
		(dist_x[v->direction] >= 0 || (x_diff > dist_x[v->direction] && x_diff <= 0)) &&
 
		(dist_x[v->direction] <= 0 || (x_diff < dist_x[v->direction] && x_diff >= 0)) &&
 
		(dist_y[v->direction] >= 0 || (y_diff > dist_y[v->direction] && y_diff <= 0)) &&
 
		(dist_y[v->direction] <= 0 || (y_diff < dist_y[v->direction] && y_diff >= 0)) ?
 
			v : NULL;
 
}
 

	
 
static Vehicle* RoadVehFindCloseTo(Vehicle* v, int x, int y, Direction dir)
 
{
 
	RoadVehFindData rvf;
 
	Vehicle *u;
 

	
 
	if (v->u.road.reverse_ctr != 0) return NULL;
 

	
 
	rvf.x = x;
 
	rvf.y = y;
 
	rvf.dir = dir;
 
	rvf.veh = v;
 
	u = VehicleFromPos(TileVirtXY(x, y), &rvf, EnumCheckRoadVehClose);
 

	
 
	// This code protects a roadvehicle from being blocked for ever
 
	//  If more than 1480 / 74 days a road vehicle is blocked, it will
 
	//  drive just through it. The ultimate backup-code of TTD.
 
	// It can be disabled.
 
	if (u == NULL) {
 
		v->u.road.blocked_ctr = 0;
 
		return NULL;
 
	}
 

	
 
	if (++v->u.road.blocked_ctr > 1480) return NULL;
 

	
 
	return u;
 
}
 

	
 
static void RoadVehArrivesAt(const Vehicle* v, Station* st)
 
{
 
	if (v->cargo_type == CT_PASSENGERS) {
 
		/* Check if station was ever visited before */
 
		if (!(st->had_vehicle_of_type & HVOT_BUS)) {
 
			uint32 flags;
 

	
 
			st->had_vehicle_of_type |= HVOT_BUS;
 
			SetDParam(0, st->index);
 
			flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0);
 
			AddNewsItem(
 
				STR_902F_CITIZENS_CELEBRATE_FIRST,
 
				flags,
 
				v->index,
 
				0);
 
		}
 
	} else {
 
		/* Check if station was ever visited before */
 
		if (!(st->had_vehicle_of_type & HVOT_TRUCK)) {
 
			uint32 flags;
 

	
 
			st->had_vehicle_of_type |= HVOT_TRUCK;
 
			SetDParam(0, st->index);
 
			flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0);
 
			AddNewsItem(
 
				STR_9030_CITIZENS_CELEBRATE_FIRST,
 
				flags,
 
				v->index,
 
				0
 
			);
 
		}
 
	}
 
}
 

	
 
static bool RoadVehAccelerate(Vehicle *v)
 
{
 
	uint spd = v->cur_speed + 1 + (v->u.road.overtaking != 0 ? 1 : 0);
 
	byte t;
 

	
 
	// Clamp
 
	spd = min(spd, v->max_speed);
 
	if (v->u.road.state == 255) spd = min(spd, SetSpeedLimitOnBridge(v));
 

	
 
	//updates statusbar only if speed have changed to save CPU time
 
	if (spd != v->cur_speed) {
 
		v->cur_speed = spd;
 
		if (_patches.vehicle_speed) {
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
	}
 

	
 
	// Decrease somewhat when turning
 
	if (!(v->direction & 1)) spd = spd * 3 >> 2;
 

	
 
	if (spd == 0) return false;
 

	
 
	if ((byte)++spd == 0) return true;
 

	
 
	v->progress = (t = v->progress) - (byte)spd;
 

	
 
	return (t < v->progress);
 
}
 

	
 
static Direction RoadVehGetNewDirection(const Vehicle* v, int x, int y)
 
{
 
	static const Direction _roadveh_new_dir[] = {
 
		DIR_N , DIR_NW, DIR_W , 0,
 
		DIR_NE, DIR_N , DIR_SW, 0,
 
		DIR_E , DIR_SE, DIR_S
 
	};
 

	
 
	x = x - v->x_pos + 1;
 
	y = y - v->y_pos + 1;
 

	
 
	if ((uint)x > 2 || (uint)y > 2) return v->direction;
 
	return _roadveh_new_dir[y * 4 + x];
 
}
 

	
 
static Direction RoadVehGetSlidingDirection(const Vehicle* v, int x, int y)
 
{
 
	Direction new = RoadVehGetNewDirection(v, x, y);
 
	Direction old = v->direction;
 
	DirDiff delta;
 

	
 
	if (new == old) return old;
 
	delta = (DirDifference(new, old) > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
 
	return ChangeDir(old, delta);
 
}
 

	
 
typedef struct OvertakeData {
 
	const Vehicle* u;
 
	const Vehicle* v;
 
	TileIndex tile;
 
	byte tilebits;
 
} OvertakeData;
 

	
 
static void* EnumFindVehToOvertake(Vehicle* v, void* data)
 
{
 
	const OvertakeData* od = data;
 

	
 
	return
 
		v->tile == od->tile && v->type == VEH_Road && v != od->u && v != od->v ?
 
			v : NULL;
 
}
 

	
 
static bool FindRoadVehToOvertake(OvertakeData *od)
 
{
 
	uint32 bits;
 

	
 
	bits = GetTileTrackStatus(od->tile, TRANSPORT_ROAD) & 0x3F;
 

	
 
	if (!(od->tilebits & bits) || (bits & 0x3C) || (bits & 0x3F3F0000))
 
		return true;
 
	return VehicleFromPos(od->tile, od, EnumFindVehToOvertake) != NULL;
 
}
 

	
 
static void RoadVehCheckOvertake(Vehicle *v, Vehicle *u)
 
{
 
	OvertakeData od;
 
	byte tt;
 

	
 
	od.v = v;
 
	od.u = u;
 

	
 
	if (u->max_speed >= v->max_speed &&
 
			!(u->vehstatus & VS_STOPPED) &&
 
			u->cur_speed != 0) {
 
		return;
 
	}
 

	
 
	if (v->direction != u->direction || !(v->direction & 1)) return;
 

	
 
	if (v->u.road.state >= 32 || (v->u.road.state & 7) > 1) return;
 

	
 
	tt = GetTileTrackStatus(v->tile, TRANSPORT_ROAD) & 0x3F;
 
	if ((tt & 3) == 0) return;
 
	if ((tt & 0x3C) != 0) return;
 

	
 
	if (tt == 3) tt = (v->direction & 2) ? 2 : 1;
 
	od.tilebits = tt;
 

	
 
	od.tile = v->tile;
 
	if (FindRoadVehToOvertake(&od)) return;
 

	
 
	od.tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
 
	if (FindRoadVehToOvertake(&od)) return;
 

	
 
	if (od.u->cur_speed == 0 || od.u->vehstatus& VS_STOPPED) {
 
		v->u.road.overtaking_ctr = 0x11;
 
		v->u.road.overtaking = 0x10;
 
	} else {
 
//		if (FindRoadVehToOvertake(&od)) return;
 
		v->u.road.overtaking_ctr = 0;
 
		v->u.road.overtaking = 0x10;
 
	}
 
}
 

	
 
static void RoadZPosAffectSpeed(Vehicle *v, byte old_z)
 
{
 
	if (old_z == v->z_pos) return;
 

	
 
	if (old_z < v->z_pos) {
 
		v->cur_speed = v->cur_speed * 232 / 256; // slow down by ~10%
 
	} else {
 
		uint16 spd = v->cur_speed + 2;
 
		if (spd <= v->max_speed) v->cur_speed = spd;
 
	}
 
}
 

	
 
static int PickRandomBit(uint bits)
 
{
 
	uint num = 0;
 
	uint b = bits;
 
	uint i;
 

	
 
	do {
 
		if (b & 1) num++;
 
	} while (b >>= 1);
 

	
 
	num = RandomRange(num);
 

	
 
	for (i = 0; !(bits & 1) || (int)--num >= 0; bits >>= 1, i++) {}
 
	return i;
 
}
 

	
 
typedef struct {
 
	TileIndex dest;
 
	uint maxtracklen;
 
	uint mindist;
 
} FindRoadToChooseData;
 

	
 
static bool EnumRoadTrackFindDist(TileIndex tile, void* data, int track, uint length, byte* state)
 
{
 
	FindRoadToChooseData* frd = data;
 
	uint dist = DistanceManhattan(tile, frd->dest);
 

	
 
	if (dist <= frd->mindist) {
 
		if (dist != frd->mindist || length < frd->maxtracklen) {
 
			frd->maxtracklen = length;
 
		}
 
		frd->mindist = dist;
 
	}
 
	return false;
 
}
 

	
 
static inline NPFFoundTargetData PerfNPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypeMask railtypes)
 
{
 

	
 
	void* perf = NpfBeginInterval();
 
	NPFFoundTargetData ret = NPFRouteToStationOrTile(tile, trackdir, target, type, owner, railtypes);
 
	int t = NpfEndInterval(perf);
 
	DEBUG(yapf, 4, "[NPFR] %d us - %d rounds - %d open - %d closed -- ", t, 0, _aystar_stats_open_size, _aystar_stats_closed_size);
 
	return ret;
 
}
 

	
 
// Returns direction to choose
 
// or -1 if the direction is currently blocked
 
static int RoadFindPathToDest(Vehicle* v, TileIndex tile, DiagDirection enterdir)
 
{
 
#define return_track(x) {best_track = x; goto found_best_track; }
 

	
 
	uint16 signal;
 
	uint bitmask;
 
	TileIndex desttile;
 
	FindRoadToChooseData frd;
 
	int best_track;
 
	uint best_dist, best_maxlen;
 
	uint i;
 

	
 
	{
 
		uint32 r = GetTileTrackStatus(tile, TRANSPORT_ROAD);
 
		signal  = GB(r, 16, 16);
 
		bitmask = GB(r,  0, 16);
 
	}
 

	
 
	if (IsTileType(tile, MP_STREET)) {
 
		if (GetRoadTileType(tile) == ROAD_TILE_DEPOT && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir)) {
 
			/* Road depot owned by another player or with the wrong orientation */
 
			bitmask = 0;
 
		}
 
	} else if (IsTileType(tile, MP_STATION) && IsRoadStopTile(tile)) {
 
		if (!IsTileOwner(tile, v->owner) || GetRoadStopDir(tile) == enterdir) {
 
			/* different station owner or wrong orientation */
 
			bitmask = 0;
 
		} else {
 
			/* Our station */
 
			RoadStopType rstype = (v->cargo_type == CT_PASSENGERS) ? RS_BUS : RS_TRUCK;
 

	
 
			if (GetRoadStopType(tile) != rstype) {
 
				// wrong station type
 
				bitmask = 0;
 
			} else {
 
				// proper station type, check if there is free loading bay
 
				const RoadStop *rs = GetRoadStopByTile(tile, rstype);
 
				if (rs == NULL || (!_patches.roadveh_queue && GB(rs->status, 0, 2) == 0)) {
 
					// station is full and RV queuing is off
 
					bitmask = 0;
 
				}
 
			}
 
		}
 
	}
 
	/* The above lookups should be moved to GetTileTrackStatus in the
 
	 * future, but that requires more changes to the pathfinder and other
 
	 * stuff, probably even more arguments to GTTS.
 
	 */
 

	
 
	/* remove unreachable tracks */
 
	bitmask &= _road_veh_fp_ax_and[enterdir];
 
	if (bitmask == 0) {
 
		/* No reachable tracks, so we'll reverse */
 
		return_track(_road_reverse_table[enterdir]);
 
	}
 

	
 
	if (v->u.road.reverse_ctr != 0) {
 
		/* What happens here?? */
 
		v->u.road.reverse_ctr = 0;
 
		if (v->tile != tile) {
 
			return_track(_road_reverse_table[enterdir]);
 
		}
 
	}
 

	
 
	desttile = v->dest_tile;
 
	if (desttile == 0) {
 
		// Pick a random track
 
		return_track(PickRandomBit(bitmask));
 
	}
 

	
 
	// Only one track to choose between?
 
	if (!(KillFirstBit2x64(bitmask))) {
 
		return_track(FindFirstBit2x64(bitmask));
 
	}
 

	
 
	if (_patches.yapf.road_use_yapf) {
 
		Trackdir trackdir = YapfChooseRoadTrack(v, tile, enterdir);
 
		if (trackdir != INVALID_TRACKDIR) return_track(trackdir);
 
		return_track(PickRandomBit(bitmask));
 
	} else if (_patches.new_pathfinding_all) {
 
		NPFFindStationOrTileData fstd;
 
		NPFFoundTargetData ftd;
 
		byte trackdir;
 

	
 
		NPFFillWithOrderData(&fstd, v);
 
		trackdir = DiagdirToDiagTrackdir(enterdir);
 
		//debug("Finding path. Enterdir: %d, Trackdir: %d", enterdir, trackdir);
 

	
 
		ftd = PerfNPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, &fstd, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE);
 
		if (ftd.best_trackdir == 0xff) {
 
			/* We are already at our target. Just do something */
 
			//TODO: maybe display error?
 
			//TODO: go straight ahead if possible?
 
			return_track(FindFirstBit2x64(bitmask));
 
		} else {
 
			/* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
 
			the direction we need to take to get there, if ftd.best_bird_dist is not 0,
 
			we did not find our target, but ftd.best_trackdir contains the direction leading
 
			to the tile closest to our target. */
 
			return_track(ftd.best_trackdir);
 
		}
 
	} else {
 
		DiagDirection dir;
 

	
 
		if (IsTileType(desttile, MP_STREET)) {
 
			if (GetRoadTileType(desttile) == ROAD_TILE_DEPOT) {
 
				dir = GetRoadDepotDirection(desttile);
 
				goto do_it;
 
			}
 
		} else if (IsTileType(desttile, MP_STATION)) {
 
			if (IsRoadStop(desttile)) {
 
				dir = GetRoadStopDir(desttile);
 
do_it:;
 
				/* When we are heading for a depot or station, we just
 
				 * pretend we are heading for the tile in front, we'll
 
				 * see from there */
 
				desttile += TileOffsByDiagDir(dir);
 
				if (desttile == tile && bitmask & _road_pf_table_3[dir]) {
 
					/* If we are already in front of the
 
					 * station/depot and we can get in from here,
 
					 * we enter */
 
					return_track(FindFirstBit2x64(bitmask & _road_pf_table_3[dir]));
 
				}
 
			}
 
		}
 
		// do pathfind
 
		frd.dest = desttile;
 

	
 
		best_track = -1;
 
		best_dist = (uint)-1;
 
		best_maxlen = (uint)-1;
 
		i = 0;
 
		do {
 
			if (bitmask & 1) {
 
				if (best_track == -1) best_track = i; // in case we don't find the path, just pick a track
 
				frd.maxtracklen = (uint)-1;
 
				frd.mindist = (uint)-1;
 
				FollowTrack(tile, 0x2000 | TRANSPORT_ROAD, _road_pf_directions[i], EnumRoadTrackFindDist, NULL, &frd);
 

	
 
				if (frd.mindist < best_dist || (frd.mindist==best_dist && frd.maxtracklen < best_maxlen)) {
 
					best_dist = frd.mindist;
 
					best_maxlen = frd.maxtracklen;
 
					best_track = i;
 
				}
 
			}
 
		} while (++i,(bitmask>>=1) != 0);
 
	}
 

	
 
found_best_track:;
 

	
 
	if (HASBIT(signal, best_track)) return -1;
 

	
 
	return best_track;
 
}
 

	
 
static uint RoadFindPathToStop(const Vehicle *v, TileIndex tile)
 
{
 
	uint dist;
 
	if (_patches.yapf.road_use_yapf) {
 
		// use YAPF
 
		dist = YapfRoadVehDistanceToTile(v, tile);
 
	} else {
 
		// use NPF
 
		NPFFindStationOrTileData fstd;
 
		byte trackdir = GetVehicleTrackdir(v);
 
		assert(trackdir != 0xFF);
 

	
 
		fstd.dest_coords = tile;
 
		fstd.station_index = INVALID_STATION; // indicates that the destination is a tile, not a station
 

	
 
		dist = NPFRouteToStationOrTile(v->tile, trackdir, &fstd, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE).best_path_dist;
 
		// change units from NPF_TILE_LENGTH to # of tiles
 
		if (dist != UINT_MAX)
 
			dist = (dist + NPF_TILE_LENGTH - 1) / NPF_TILE_LENGTH;
 
	}
 
	return dist;
 
}
 

	
 
typedef struct RoadDriveEntry {
 
	byte x,y;
 
} RoadDriveEntry;
 

	
 
#include "table/roadveh.h"
 

	
 
static const byte _road_veh_data_1[] = {
 
	20, 20, 16, 16, 0, 0, 0, 0,
 
	19, 19, 15, 15, 0, 0, 0, 0,
 
	16, 16, 12, 12, 0, 0, 0, 0,
 
	15, 15, 11, 11
 
};
 

	
 
static const byte _roadveh_data_2[4] = { 0, 1, 8, 9 };
 

	
 
static void RoadVehController(Vehicle *v)
 
{
 
	Direction new_dir;
 
	Direction old_dir;
 
	RoadDriveEntry rd;
 
	int x,y;
 
	uint32 r;
 

	
 
	// decrease counters
 
	v->tick_counter++;
 
	if (v->u.road.reverse_ctr != 0) v->u.road.reverse_ctr--;
 

	
 
	// handle crashed
 
	if (v->u.road.crashed_ctr != 0) {
 
		RoadVehIsCrashed(v);
 
		return;
 
	}
 

	
 
	RoadVehCheckTrainCrash(v);
 

	
 
	// road vehicle has broken down?
 
	if (v->breakdown_ctr != 0) {
 
		if (v->breakdown_ctr <= 2) {
 
			HandleBrokenRoadVeh(v);
 
			return;
 
		}
 
		v->breakdown_ctr--;
 
	}
 

	
 
	if (v->vehstatus & VS_STOPPED) return;
 

	
 
	ProcessRoadVehOrder(v);
 
	HandleRoadVehLoading(v);
 

	
 
	if (v->current_order.type == OT_LOADING) return;
 

	
 
	if (IsRoadVehInDepot(v)) {
 
		DiagDirection dir;
 
		const RoadDriveEntry* rdp;
 
		byte rd2;
 

	
 
		v->cur_speed = 0;
 

	
 
		dir = GetRoadDepotDirection(v->tile);
 
		v->direction = DiagDirToDir(dir);
 

	
 
		rd2 = _roadveh_data_2[dir];
 
		rdp = _road_drive_data[(_opt.road_side << 4) + rd2];
 

	
 
		x = TileX(v->tile) * TILE_SIZE + (rdp[6].x & 0xF);
 
		y = TileY(v->tile) * TILE_SIZE + (rdp[6].y & 0xF);
 

	
 
		if (RoadVehFindCloseTo(v, x, y, v->direction) != NULL) return;
 

	
 
		VehicleServiceInDepot(v);
 

	
 
		StartRoadVehSound(v);
 

	
 
		BeginVehicleMove(v);
 

	
 
		v->vehstatus &= ~VS_HIDDEN;
 
		v->u.road.state = rd2;
 
		v->u.road.frame = 6;
 

	
 
		v->cur_image = GetRoadVehImage(v, v->direction);
 
		UpdateRoadVehDeltaXY(v);
 
		SetRoadVehPosition(v,x,y);
 

	
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
		return;
 
	}
 

	
 
	if (!RoadVehAccelerate(v)) return;
 

	
 
	if (v->u.road.overtaking != 0)  {
 
		if (++v->u.road.overtaking_ctr >= 35)
 
			/* If overtaking just aborts at a random moment, we can have a out-of-bound problem,
 
			 *  if the vehicle started a corner. To protect that, only allow an abort of
 
			 *  overtake if we are on straight road, which are the 8 states below */
 
			if (v->u.road.state == 0  || v->u.road.state == 1  ||
 
					v->u.road.state == 8  || v->u.road.state == 9  ||
 
					v->u.road.state == 16 || v->u.road.state == 17 ||
 
					v->u.road.state == 24 || v->u.road.state == 25) {
 
				v->u.road.overtaking = 0;
 
			}
 
	}
 

	
 
	BeginVehicleMove(v);
 

	
 
	if (v->u.road.state == 255) {
 
		GetNewVehiclePosResult gp;
 

	
 
		GetNewVehiclePos(v, &gp);
 

	
 
		if (RoadVehFindCloseTo(v, gp.x, gp.y, v->direction) != NULL) {
 
			v->cur_speed = 0;
 
			return;
 
		}
 

	
 
		if ((IsTunnelTile(gp.new_tile) || IsBridgeTile(gp.new_tile)) && VehicleEnterTile(v, gp.new_tile, gp.x, gp.y) & 4) {
 
			//new_dir = RoadGetNewDirection(v, gp.x, gp.y)
 
			v->cur_image = GetRoadVehImage(v, v->direction);
 
			UpdateRoadVehDeltaXY(v);
 
			SetRoadVehPosition(v,gp.x,gp.y);
 
			return;
 
		}
 

	
 
		v->x_pos = gp.x;
 
		v->y_pos = gp.y;
 
		VehiclePositionChanged(v);
 
		if (!(v->vehstatus & VS_HIDDEN)) EndVehicleMove(v);
 
		return;
 
	}
 

	
 
	rd = _road_drive_data[(v->u.road.state + (_opt.road_side << 4)) ^ v->u.road.overtaking][v->u.road.frame + 1];
 

	
 
// switch to another tile
 
	if (rd.x & 0x80) {
 
		TileIndex tile = v->tile + TileOffsByDiagDir(rd.x & 3);
 
		int dir = RoadFindPathToDest(v, tile, rd.x & 3);
 
		uint32 r;
 
		Direction newdir;
 
		const RoadDriveEntry *rdp;
 

	
 
		if (dir == -1) {
 
			v->cur_speed = 0;
 
			return;
 
		}
 

	
 
again:
 
		if ((dir & 7) >= 6) {
 
			/* Turning around */
 
			tile = v->tile;
 
		}
 

	
 
		rdp = _road_drive_data[(dir + (_opt.road_side << 4)) ^ v->u.road.overtaking];
 

	
 
		x = TileX(tile) * TILE_SIZE + rdp[0].x;
 
		y = TileY(tile) * TILE_SIZE + rdp[0].y;
 

	
 
		newdir = RoadVehGetSlidingDirection(v, x, y);
 
		if (RoadVehFindCloseTo(v, x, y, newdir) != NULL) return;
 

	
 
		r = VehicleEnterTile(v, tile, x, y);
 
		if (r & 8) {
 
			if (!IsTileType(tile, MP_TUNNELBRIDGE)) {
 
				v->cur_speed = 0;
 
				return;
 
			}
 
			dir = _road_reverse_table[rd.x&3];
 
			goto again;
 
		}
 

	
 
		if (IS_BYTE_INSIDE(v->u.road.state, 0x20, 0x30) && IsTileType(v->tile, MP_STATION)) {
 
			if ((dir & 7) >= 6) {
 
				v->cur_speed = 0;
 
				return;
 
			}
 
			if (IsRoadStop(v->tile)) {
 
				RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
 

	
 
				// reached a loading bay, mark it as used and clear the usage bit
 
				SETBIT(rs->status, v->u.road.state & 2 ? 1 : 0); // occupied bay
 
				CLRBIT(rs->status, 7); // usage bit
 
			}
 
		}
 

	
 
		if (!(r & 4)) {
 
			v->tile = tile;
 
			v->u.road.state = (byte)dir;
 
			v->u.road.frame = 0;
 
		}
 
		if (newdir != v->direction) {
 
			v->direction = newdir;
 
			v->cur_speed -= v->cur_speed >> 2;
 
		}
 

	
 
		v->cur_image = GetRoadVehImage(v, newdir);
 
		UpdateRoadVehDeltaXY(v);
 
		RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
 
		return;
 
	}
 

	
 
	if (rd.x & 0x40) {
 
		int dir = RoadFindPathToDest(v, v->tile, rd.x & 3);
 
		uint32 r;
 
		int tmp;
 
		Direction newdir;
 
		const RoadDriveEntry *rdp;
 

	
 
		if (dir == -1) {
 
			v->cur_speed = 0;
 
			return;
 
		}
 

	
 
		tmp = (_opt.road_side << 4) + dir;
 
		rdp = _road_drive_data[tmp];
 

	
 
		x = TileX(v->tile) * TILE_SIZE + rdp[1].x;
 
		y = TileY(v->tile) * TILE_SIZE + rdp[1].y;
 

	
 
		newdir = RoadVehGetSlidingDirection(v, x, y);
 
		if (RoadVehFindCloseTo(v, x, y, newdir) != NULL) return;
 

	
 
		r = VehicleEnterTile(v, v->tile, x, y);
 
		if (r & 8) {
 
			v->cur_speed = 0;
 
			return;
 
		}
 

	
 
		v->u.road.state = tmp & ~16;
 
		v->u.road.frame = 1;
 

	
 
		if (newdir != v->direction) {
 
			v->direction = newdir;
 
			v->cur_speed -= v->cur_speed >> 2;
 
		}
 

	
 
		v->cur_image = GetRoadVehImage(v, newdir);
 
		UpdateRoadVehDeltaXY(v);
 
		RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
 
		return;
 
	}
 

	
 
	x = (v->x_pos & ~15) + (rd.x & 15);
 
	y = (v->y_pos & ~15) + (rd.y & 15);
 

	
 
	new_dir = RoadVehGetSlidingDirection(v, x, y);
 

	
 
	if (!IS_BYTE_INSIDE(v->u.road.state, 0x20, 0x30)) {
 
		Vehicle* u = RoadVehFindCloseTo(v, x, y, new_dir);
 

	
 
		if (u != NULL) {
 
			if (v->u.road.overtaking == 0) RoadVehCheckOvertake(v, u);
 
			return;
 
		}
 
	}
 

	
 
	old_dir = v->direction;
 
	if (new_dir != old_dir) {
 
		v->direction = new_dir;
 
		v->cur_speed -= (v->cur_speed >> 2);
 
		if (old_dir != v->u.road.state) {
 
			v->cur_image = GetRoadVehImage(v, new_dir);
 
			UpdateRoadVehDeltaXY(v);
 
			SetRoadVehPosition(v, v->x_pos, v->y_pos);
 
			return;
 
		}
 
	}
 

	
 
	if (v->u.road.state >= 0x20 &&
 
			_road_veh_data_1[v->u.road.state - 0x20 + (_opt.road_side<<4)] == v->u.road.frame) {
 
		RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
 
		Station* st = GetStationByTile(v->tile);
 

	
 
		if (v->current_order.type != OT_LEAVESTATION &&
 
				v->current_order.type != OT_GOTO_DEPOT) {
 
			Order old_order;
 

	
 
			CLRBIT(rs->status, 7);
 

	
 
			v->last_station_visited = GetStationIndex(v->tile);
 

	
 
			RoadVehArrivesAt(v, st);
 

	
 
			old_order = v->current_order;
 
			v->current_order.type = OT_LOADING;
 
			v->current_order.flags = 0;
 

	
 
			if (old_order.type == OT_GOTO_STATION &&
 
					v->current_order.dest == v->last_station_visited) {
 
				v->current_order.flags =
 
					(old_order.flags & (OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER)) | OF_NON_STOP;
 
			}
 

	
 
			SET_EXPENSES_TYPE(EXPENSES_ROADVEH_INC);
 
			if (LoadUnloadVehicle(v, true)) {
 
				InvalidateWindow(WC_ROADVEH_LIST, v->owner);
 
				MarkRoadVehDirty(v);
 
			}
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
			return;
 
		}
 

	
 
		if (v->current_order.type != OT_GOTO_DEPOT) {
 
			if (HASBIT(rs->status, 7)) {
 
				v->cur_speed = 0;
 
				return;
 
			}
 
			v->current_order.type = OT_NOTHING;
 
			v->current_order.flags = 0;
 
			ClearSlot(v);
 
		}
 
		SETBIT(rs->status, 7);
 

	
 
		if (rs == v->u.road.slot) {
 
			//we have arrived at the correct station
 
			ClearSlot(v);
 
		} else if (v->u.road.slot != NULL) {
 
			//we have arrived at the wrong station
 
			//XXX The question is .. what to do? Actually we shouldn't be here
 
			//but I guess we need to clear the slot
 
			DEBUG(ms, 0, "Vehicle %d (index %d) arrived at wrong stop", v->unitnumber, v->index);
 
			if (v->tile != v->dest_tile) {
 
				DEBUG(ms, 2, " current tile 0x%X is not destination tile 0x%X. Route problem", v->tile, v->dest_tile);
 
			}
 
			if (v->dest_tile != v->u.road.slot->xy) {
 
				DEBUG(ms, 2, " stop tile 0x%X is not destination tile 0x%X. Multistop desync", v->u.road.slot->xy, v->dest_tile);
 
			}
 
			if (v->current_order.type != OT_GOTO_STATION) {
 
				DEBUG(ms, 2, " current order type (%d) is not OT_GOTO_STATION", v->current_order.type);
 
			} else {
 
				if (v->current_order.dest != st->index)
 
					DEBUG(ms, 2, " current station %d is not target station in current_order.station (%d)",
 
							st->index, v->current_order.dest);
 
			}
 

	
 
			DEBUG(ms, 2, " force a slot clearing");
 
			ClearSlot(v);
 
		}
 

	
 
		StartRoadVehSound(v);
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 

	
 
	r = VehicleEnterTile(v, v->tile, x, y);
 
	if (r & 8) {
 
		v->cur_speed = 0;
 
		return;
 
	}
 

	
 
	if ((r & 4) == 0) v->u.road.frame++;
 

	
 
	v->cur_image = GetRoadVehImage(v, v->direction);
 
	UpdateRoadVehDeltaXY(v);
 
	RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y));
 
}
 

	
 
static void AgeRoadVehCargo(Vehicle *v)
 
{
 
	if (_age_cargo_skip_counter != 0) return;
 
	if (v->cargo_days != 255) v->cargo_days++;
 
}
 

	
 
void RoadVeh_Tick(Vehicle *v)
 
{
 
	AgeRoadVehCargo(v);
 
	RoadVehController(v);
 
}
 

	
 
static void CheckIfRoadVehNeedsService(Vehicle *v)
 
{
 
	const Depot* depot;
 

	
 
	if (_patches.servint_roadveh == 0) return;
 
	if (!VehicleNeedsService(v)) return;
 
	if (v->vehstatus & VS_STOPPED) return;
 
	if (_patches.gotodepot && VehicleHasDepotOrders(v)) return;
 

	
 
	// Don't interfere with a depot visit scheduled by the user, or a
 
	// depot visit by the order list.
 
	if (v->current_order.type == OT_GOTO_DEPOT &&
 
			(v->current_order.flags & (OF_HALT_IN_DEPOT | OF_PART_OF_ORDERS)) != 0)
 
		return;
 

	
 
	// If we already got a slot at a stop, use that FIRST, and go to a depot later
 
	if (v->u.road.slot != NULL) return;
 

	
 
	if (IsRoadVehInDepot(v)) {
 
		VehicleServiceInDepot(v);
 
		return;
 
	}
 

	
 
	// XXX If we already have a depot order, WHY do we search over and over?
 
	depot = FindClosestRoadDepot(v);
 

	
 
	if (depot == NULL || DistanceManhattan(v->tile, depot->xy) > 12) {
 
		if (v->current_order.type == OT_GOTO_DEPOT) {
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
		return;
 
	}
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT &&
 
			v->current_order.flags & OF_NON_STOP &&
 
			!CHANCE16(1, 20)) {
 
		return;
 
	}
 

	
 
	v->current_order.type = OT_GOTO_DEPOT;
 
	v->current_order.flags = OF_NON_STOP;
 
	v->current_order.dest = depot->index;
 
	v->dest_tile = depot->xy;
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
}
 

	
 
void OnNewDay_RoadVeh(Vehicle *v)
 
{
 
	int32 cost;
 

	
 
	if ((++v->day_counter & 7) == 0) DecreaseVehicleValue(v);
 
	if (v->u.road.blocked_ctr == 0) CheckVehicleBreakdown(v);
 

	
 
	AgeVehicle(v);
 
	CheckIfRoadVehNeedsService(v);
 

	
 
	CheckOrders(v);
 

	
 
	//Current slot has expired
 
	if (v->current_order.type == OT_GOTO_STATION && v->u.road.slot != NULL && v->u.road.slot_age-- == 0) {
 
		DEBUG(ms, 3, "Slot expired for vehicle %d (index %d) at stop 0x%X",
 
			v->unitnumber, v->index, v->u.road.slot->xy);
 
		ClearSlot(v);
 
	}
 

	
 
	if (v->vehstatus & VS_STOPPED) return;
 

	
 
	/* update destination */
 
	if (v->current_order.type == OT_GOTO_STATION && v->u.road.slot == NULL && !(v->vehstatus & VS_CRASHED)) {
 
		Station* st = GetStation(v->current_order.dest);
 
		RoadStop* rs = GetPrimaryRoadStop(st, v->cargo_type == CT_PASSENGERS ? RS_BUS : RS_TRUCK);
 
		RoadStop* best = NULL;
 

	
 
		if (rs != NULL) {
 
			if (DistanceManhattan(v->tile, st->xy) < 16) {
 
				uint dist, badness;
 
				uint minbadness = UINT_MAX;
 

	
 
				DEBUG(ms, 2, "Attempting to obtain a slot for vehicle %d (index %d) at station %d (0x%X)",
 
					v->unitnumber, v->index, st->index, st->xy
 
				);
 
				/* Now we find the nearest road stop that has a free slot */
 
				for (; rs != NULL; rs = rs->next) {
 
					dist = RoadFindPathToStop(v, rs->xy);
 
					if (dist == UINT_MAX) {
 
						DEBUG(ms, 4, " stop 0x%X is unreachable, not treating further", rs->xy);
 
						continue;
 
					}
 
					badness = (rs->num_vehicles + 1) * (rs->num_vehicles + 1) + dist;
 

	
 
					DEBUG(ms, 4, " stop 0x%X has %d vehicle%s waiting", rs->xy, rs->num_vehicles, rs->num_vehicles == 1 ? "":"s");
 
					DEBUG(ms, 4, " distance is %u", dist);
 
					DEBUG(ms, 4, " badness %u", badness);
 

	
 
					if (badness < minbadness) {
 
						best = rs;
 
						minbadness = badness;
 
					}
 
				}
 

	
 
				if (best != NULL) {
 
					best->num_vehicles++;
 
					DEBUG(ms, 3, "Assigned to stop 0x%X", best->xy);
 

	
 
					v->u.road.slot = best;
 
					v->dest_tile = best->xy;
 
					v->u.road.slot_age = 14;
 
				} else {
 
					DEBUG(ms, 3, "Could not find a suitable stop");
 
				}
 
			} else {
 
				DEBUG(ms, 5, "Distance from station too far. Postponing slotting for vehicle %d (index %d) at station %d, (0x%X)",
 
						v->unitnumber, v->index, st->index, st->xy);
 
			}
 
		} else {
 
			DEBUG(ms, 4, "No road stop for vehicle %d (index %d) at station %d (0x%X)",
 
					v->unitnumber, v->index, st->index, st->xy);
 
		}
 
	}
 

	
 
	cost = RoadVehInfo(v->engine_type)->running_cost * _price.roadveh_running / 364;
 

	
 
	v->profit_this_year -= cost >> 8;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_ROADVEH_RUN);
 
	SubtractMoneyFromPlayerFract(v->owner, cost);
 

	
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	InvalidateWindowClasses(WC_ROADVEH_LIST);
 
}
 

	
 

	
 
void RoadVehiclesYearlyLoop(void)
 
{
 
	Vehicle *v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Road) {
 
			v->profit_last_year = v->profit_this_year;
 
			v->profit_this_year = 0;
 
			InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		}
 
	}
 
}
 

	
 
/** Refit a road vehicle to the specified cargo type
 
 * @param tile unused
 
 * @param p1 Vehicle ID of the vehicle to refit
 
 * @param p2 Bitstuffed elements
 
 * - p2 = (bit 0-7) - the new cargo type to refit to
 
 * - p2 = (bit 8-15) - the new cargo subtype to refit to
 
 */
 
int32 CmdRefitRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	int32 cost;
 
	CargoID new_cid = GB(p2, 0, 8);
 
	byte new_subtype = GB(p2, 8, 8);
 
	uint16 capacity = CALLBACK_FAILED;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Road || !CheckOwnership(v->owner)) return CMD_ERROR;
 
	if (!IsRoadVehInDepotStopped(v)) return_cmd_error(STR_9013_MUST_BE_STOPPED_INSIDE);
 

	
 
	if (new_cid > NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_ROADVEH_RUN);
 

	
 
	if (HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_REFIT_CAPACITY)) {
 
		/* Back up the cargo type */
 
		CargoID temp_cid = v->cargo_type;
 
		byte temp_subtype = v->cargo_subtype;
 
		v->cargo_type = new_cid;
 
		v->cargo_subtype = new_subtype;
 

	
 
		/* Check the refit capacity callback */
 
		capacity = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
 

	
 
		/* Restore the original cargo type */
 
		v->cargo_type = temp_cid;
 
		v->cargo_subtype = temp_subtype;
 
	}
 

	
 
	if (capacity == CALLBACK_FAILED) {
 
		/* callback failed or not used, use default capacity */
 
		const RoadVehicleInfo *rvi = RoadVehInfo(v->engine_type);
 

	
 
		CargoID old_cid = rvi->cargo_type;
 
		/* normally, the capacity depends on the cargo type, a vehicle can
 
		 * carry twice as much mail/goods as normal cargo, and four times as
 
		 * many passengers
 
		 */
 
		capacity = rvi->capacity;
 
		switch (old_cid) {
 
			case CT_PASSENGERS: break;
 
			case CT_MAIL:
 
			case CT_GOODS: capacity *= 2; break;
 
			default:       capacity *= 4; break;
 
		}
 
		switch (new_cid) {
 
			case CT_PASSENGERS: break;
 
			case CT_MAIL:
 
			case CT_GOODS: capacity /= 2; break;
 
			default:       capacity /= 4; break;
 
		}
 
	}
 
	_returned_refit_capacity = capacity;
 

	
 
	cost = 0;
 
	if (IsHumanPlayer(v->owner) && new_cid != v->cargo_type) {
 
		cost = GetRefitCost(v->engine_type);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		v->cargo_cap = capacity;
 
		v->cargo_count = (v->cargo_type == new_cid) ? min(capacity, v->cargo_count) : 0;
 
		v->cargo_type = new_cid;
 
		v->cargo_subtype = new_subtype;
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		RebuildVehicleLists();
 
	}
 

	
 
	return cost;
 
}
src/roadveh_gui.c
Show inline comments
 
deleted file
src/roadveh_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "roadveh.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "vehicle.h"
 
#include "viewport.h"
 
#include "station.h"
 
#include "command.h"
 
#include "player.h"
 
#include "engine.h"
 
#include "depot.h"
 
#include "vehicle_gui.h"
 
#include "newgrf_engine.h"
 
#include "date.h"
 

	
 
/**
 
 * Draw the purchase info details of road vehicle at a given location.
 
 * @param x,y location where to draw the info
 
 * @param engine_number the engine of which to draw the info of
 
 */
 
void DrawRoadVehPurchaseInfo(int x, int y, uint w, EngineID engine_number)
 
{
 
	const RoadVehicleInfo *rvi = RoadVehInfo(engine_number);
 
	const Engine *e = GetEngine(engine_number);
 
	bool refittable = (_engine_info[engine_number].refit_mask != 0);
 
	YearMonthDay ymd;
 
	ConvertDateToYMD(e->intro_date, &ymd);
 

	
 
	/* Purchase cost - Max speed */
 
	SetDParam(0, rvi->base_cost * (_price.roadveh_base>>3)>>5);
 
	SetDParam(1, rvi->max_speed / 2);
 
	DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, 0);
 
	y += 10;
 

	
 
	/* Running cost */
 
	SetDParam(0, rvi->running_cost * _price.roadveh_running >> 8);
 
	DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, 0);
 
	y += 10;
 

	
 
	/* Cargo type + capacity */
 
	SetDParam(0, rvi->cargo_type);
 
	SetDParam(1, rvi->capacity);
 
	SetDParam(2, refittable ? STR_9842_REFITTABLE : STR_EMPTY);
 
	DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, 0);
 
	y += 10;
 

	
 
	/* Design date - Life length */
 
	SetDParam(0, ymd.year);
 
	SetDParam(1, e->lifelength);
 
	DrawString(x, y, STR_PURCHASE_INFO_DESIGNED_LIFE, 0);
 
	y += 10;
 

	
 
	/* Reliability */
 
	SetDParam(0, e->reliability * 100 >> 16);
 
	DrawString(x, y, STR_PURCHASE_INFO_RELIABILITY, 0);
 
	y += 10;
 

	
 
	/* Additional text from NewGRF */
 
	y += ShowAdditionalText(x, y, w, engine_number);
 
	y += ShowRefitOptionsList(x, y, w, engine_number);
 
}
 

	
 
void DrawRoadVehImage(const Vehicle *v, int x, int y, VehicleID selection)
 
{
 
	PalSpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
 
	DrawSprite(GetRoadVehImage(v, DIR_W) | pal, x + 14, y + 6);
 

	
 
	if (v->index == selection) {
 
		DrawFrameRect(x - 1, y - 1, x + 28, y + 12, 15, FR_BORDERONLY);
 
	}
 
}
 

	
 
static void RoadVehDetailsWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const Vehicle *v = GetVehicle(w->window_number);
 
		StringID str;
 

	
 
		SetWindowWidgetDisabledState(w, 2, v->owner != _local_player);
 
		// disable service-scroller when interval is set to disabled
 
		SetWindowWidgetDisabledState(w, 5, !_patches.servint_roadveh);
 
		SetWindowWidgetDisabledState(w, 6, !_patches.servint_roadveh);
 

	
 
		SetDParam(0, v->string_id);
 
		SetDParam(1, v->unitnumber);
 
		DrawWindowWidgets(w);
 

	
 
		/* Draw running cost */
 
		{
 
			int year = v->age / 366;
 

	
 
			SetDParam(1, year);
 

	
 
			SetDParam(0, (v->age + 365 < v->max_age) ? STR_AGE : STR_AGE_RED);
 
			SetDParam(2, v->max_age / 366);
 
			SetDParam(3, RoadVehInfo(v->engine_type)->running_cost * _price.roadveh_running >> 8);
 
			DrawString(2, 15, STR_900D_AGE_RUNNING_COST_YR, 0);
 
		}
 

	
 
		/* Draw max speed */
 
		{
 
			SetDParam(0, v->max_speed / 2);
 
			DrawString(2, 25, STR_900E_MAX_SPEED, 0);
 
		}
 

	
 
		/* Draw profit */
 
		{
 
			SetDParam(0, v->profit_this_year);
 
			SetDParam(1, v->profit_last_year);
 
			DrawString(2, 35, STR_900F_PROFIT_THIS_YEAR_LAST_YEAR, 0);
 
		}
 

	
 
		/* Draw breakdown & reliability */
 
		{
 
			SetDParam(0, v->reliability * 100 >> 16);
 
			SetDParam(1, v->breakdowns_since_last_service);
 
			DrawString(2, 45, STR_9010_RELIABILITY_BREAKDOWNS, 0);
 
		}
 

	
 
		/* Draw service interval text */
 
		{
 
			SetDParam(0, v->service_interval);
 
			SetDParam(1, v->date_of_last_service);
 
			DrawString(13, 90, _patches.servint_ispercent?STR_SERVICING_INTERVAL_PERCENT:STR_883C_SERVICING_INTERVAL_DAYS, 0);
 
		}
 

	
 
		DrawRoadVehImage(v, 3, 57, INVALID_VEHICLE);
 

	
 
		SetDParam(0, GetCustomEngineName(v->engine_type));
 
		SetDParam(1, v->build_year);
 
		SetDParam(2, v->value);
 
		DrawString(34, 57, STR_9011_BUILT_VALUE, 0);
 

	
 
		SetDParam(0, v->cargo_type);
 
		SetDParam(1, v->cargo_cap);
 
		DrawString(34, 67, STR_9012_CAPACITY, 0);
 

	
 
		str = STR_8812_EMPTY;
 
		if (v->cargo_count != 0) {
 
			SetDParam(0, v->cargo_type);
 
			SetDParam(1, v->cargo_count);
 
			SetDParam(2, v->cargo_source);
 
			str = STR_8813_FROM;
 
		}
 
		DrawString(34, 78, str, 0);
 
	} break;
 

	
 
	case WE_CLICK: {
 
		int mod;
 
		const Vehicle *v;
 
		switch (e->we.click.widget) {
 
		case 2: /* rename */
 
			v = GetVehicle(w->window_number);
 
			SetDParam(0, v->unitnumber);
 
			ShowQueryString(v->string_id, STR_902C_NAME_ROAD_VEHICLE, 31, 150, w, CS_ALPHANUMERAL);
 
			break;
 

	
 
		case 5: /* increase int */
 
			mod = _ctrl_pressed? 5 : 10;
 
			goto do_change_service_int;
 
		case 6: /* decrease int */
 
			mod = _ctrl_pressed? -5 : -10;
 
do_change_service_int:
 
			v = GetVehicle(w->window_number);
 

	
 
			mod = GetServiceIntervalClamped(mod + v->service_interval);
 
			if (mod == v->service_interval) return;
 

	
 
			DoCommandP(v->tile, v->index, mod, NULL, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING));
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
		if (e->we.edittext.str[0] != '\0') {
 
			_cmd_text = e->we.edittext.str;
 
			DoCommandP(0, w->window_number, 0, NULL,
 
				CMD_NAME_VEHICLE | CMD_MSG(STR_902D_CAN_T_NAME_ROAD_VEHICLE));
 
		}
 
	} break;
 

	
 
	}
 
}
 

	
 
static const Widget _roadveh_details_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   339,     0,    13, STR_900C_DETAILS, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   340,   379,     0,    13, STR_01AA_NAME,    STR_902E_NAME_ROAD_VEHICLE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   379,    14,    55, 0x0,              STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   379,    56,    88, 0x0,              STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    10,    89,    94, STR_0188,         STR_884D_INCREASE_SERVICING_INTERVAL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    10,    95,   100, STR_0189,         STR_884E_DECREASE_SERVICING_INTERVAL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    11,   379,    89,   100, 0x0,              STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _roadveh_details_desc = {
 
	WDP_AUTO, WDP_AUTO, 380, 101,
 
	WC_VEHICLE_DETAILS,WC_VEHICLE_VIEW,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_roadveh_details_widgets,
 
	RoadVehDetailsWndProc
 
};
 

	
 
static void ShowRoadVehDetailsWindow(const Vehicle *v)
 
{
 
	Window *w;
 
	VehicleID veh = v->index;
 

	
 
	DeleteWindowById(WC_VEHICLE_ORDERS, veh);
 
	DeleteWindowById(WC_VEHICLE_DETAILS, veh);
 

	
 
	w = AllocateWindowDescFront(&_roadveh_details_desc, veh);
 
	w->caption_color = v->owner;
 
}
 

	
 
void CcCloneRoadVeh(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) ShowRoadVehViewWindow(GetVehicle(_new_vehicle_id));
 
}
 

	
 
static void RoadVehViewWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		Vehicle *v = GetVehicle(w->window_number);
 
		StringID str;
 
		bool is_localplayer = v->owner == _local_player;
 

	
 
		SetWindowWidgetDisabledState(w,  7, !is_localplayer);
 
		SetWindowWidgetDisabledState(w,  8, !is_localplayer);
 
		SetWindowWidgetDisabledState(w, 11, !is_localplayer);
 
		/* Disable refit button if vehicle not refittable */
 
		SetWindowWidgetDisabledState(w, 12, !is_localplayer ||
 
				_engine_info[v->engine_type].refit_mask == 0);
 

	
 
		/* draw widgets & caption */
 
		SetDParam(0, v->string_id);
 
		SetDParam(1, v->unitnumber);
 
		DrawWindowWidgets(w);
 

	
 
		if (v->u.road.crashed_ctr != 0) {
 
			str = STR_8863_CRASHED;
 
		} else if (v->breakdown_ctr == 1) {
 
			str = STR_885C_BROKEN_DOWN;
 
		} else if (v->vehstatus & VS_STOPPED) {
 
			str = STR_8861_STOPPED;
 
		} else {
 
			switch (v->current_order.type) {
 
			case OT_GOTO_STATION: {
 
				SetDParam(0, v->current_order.dest);
 
				SetDParam(1, v->cur_speed / 2);
 
				str = STR_HEADING_FOR_STATION + _patches.vehicle_speed;
 
			} break;
 

	
 
			case OT_GOTO_DEPOT: {
 
				Depot *depot = GetDepot(v->current_order.dest);
 
				SetDParam(0, depot->town_index);
 
				SetDParam(1, v->cur_speed / 2);
 
				if (HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT) && !HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) {
 
					str = STR_HEADING_FOR_ROAD_DEPOT + _patches.vehicle_speed;
 
				} else {
 
					str = STR_HEADING_FOR_ROAD_DEPOT_SERVICE + _patches.vehicle_speed;
 
				}
 
			} break;
 

	
 
			case OT_LOADING:
 
			case OT_LEAVESTATION:
 
				str = STR_882F_LOADING_UNLOADING;
 
				break;
 

	
 
			default:
 
				if (v->num_orders == 0) {
 
					str = STR_NO_ORDERS + _patches.vehicle_speed;
 
					SetDParam(0, v->cur_speed / 2);
 
				} else {
 
					str = STR_EMPTY;
 
				}
 
				break;
 
			}
 
		}
 

	
 
		/* draw the flag plus orders */
 
		DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, 2, w->widget[5].top + 1);
 
		DrawStringCenteredTruncated(w->widget[5].left + 8, w->widget[5].right, w->widget[5].top + 1, str, 0);
 
		DrawWindowViewport(w);
 
	} break;
 

	
 
	case WE_CLICK: {
 
		const Vehicle *v = GetVehicle(w->window_number);
 

	
 
		switch (e->we.click.widget) {
 
		case 5: /* start stop */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_ROADVEH | CMD_MSG(STR_9015_CAN_T_STOP_START_ROAD_VEHICLE));
 
			break;
 
		case 6: /* center main view */
 
			ScrollMainWindowTo(v->x_pos, v->y_pos);
 
			break;
 
		case 7: /* goto depot */
 
			DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0, NULL, CMD_SEND_ROADVEH_TO_DEPOT | CMD_MSG(STR_9018_CAN_T_SEND_VEHICLE_TO_DEPOT));
 
			break;
 
		case 8: /* turn around */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_TURN_ROADVEH | CMD_MSG(STR_9033_CAN_T_MAKE_VEHICLE_TURN));
 
			break;
 
		case 9: /* show orders */
 
			ShowOrdersWindow(v);
 
			break;
 
		case 10: /* show details */
 
			ShowRoadVehDetailsWindow(v);
 
			break;
 
		case 11: /* clone vehicle */
 
			DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneRoadVeh, CMD_CLONE_VEHICLE | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE));
 
			break;
 
		case 12: /* Refit vehicle */
 
			ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID);
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_RESIZE:
 
		w->viewport->width          += e->we.sizing.diff.x;
 
		w->viewport->height         += e->we.sizing.diff.y;
 
		w->viewport->virtual_width  += e->we.sizing.diff.x;
 
		w->viewport->virtual_height += e->we.sizing.diff.y;
 
		break;
 

	
 
	case WE_DESTROY:
 
		DeleteWindowById(WC_VEHICLE_REFIT, w->window_number);
 
		DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number);
 
		DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number);
 
		break;
 

	
 
	case WE_MOUSELOOP: {
 
			const Vehicle *v = GetVehicle(w->window_number);
 
			bool rv_stopped = IsRoadVehInDepotStopped(v);
 

	
 
			/* Widget 7 (send to depot) must be hidden if the truck/bus is already stopped in depot.
 
			 * Widget 11 (clone) should then be shown, since cloning is allowed only while in depot and stopped.
 
			 * This sytem allows to have two buttons, on top of each other.
 
			 * The same system applies to widget 8 and 12, force turn around and refit. */
 
			if (rv_stopped != IsWindowWidgetHidden(w, 7) || rv_stopped == IsWindowWidgetHidden(w, 11)) {
 
				SetWindowWidgetHiddenState(w,  7, rv_stopped);  // send to depot
 
				SetWindowWidgetHiddenState(w,  8, rv_stopped);  // force turn around
 
				SetWindowWidgetHiddenState(w, 11, !rv_stopped); // clone
 
				SetWindowWidgetHiddenState(w, 12, !rv_stopped); // refit
 
				SetWindowDirty(w);
 
			}
 
		}
 
	}
 
}
 

	
 
static const Widget _roadveh_view_widgets[] = {
 
{   WWT_CLOSEBOX, RESIZE_NONE,  14,   0,  10,   0,  13, STR_00C5,                STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION, RESIZE_RIGHT, 14,  11, 237,   0,  13, STR_9002,                STR_018C_WINDOW_TITLE_DRAG_THIS },
 
{  WWT_STICKYBOX, RESIZE_LR,    14, 238, 249,   0,  13, 0x0,                     STR_STICKY_BUTTON },
 
{      WWT_PANEL, RESIZE_RB,    14,   0, 231,  14, 103, 0x0,                     STR_NULL },
 
{      WWT_INSET, RESIZE_RB,    14,   2, 229,  16, 101, 0x0,                     STR_NULL },
 
{    WWT_PUSHBTN, RESIZE_RTB,   14,   0, 237, 104, 115, 0x0,                     STR_901C_CURRENT_VEHICLE_ACTION },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  14,  31, SPR_CENTRE_VIEW_VEHICLE, STR_901E_CENTER_MAIN_VIEW_ON_VEHICLE },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  32,  49, SPR_SEND_ROADVEH_TODEPOT,STR_901F_SEND_VEHICLE_TO_DEPOT },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  50,  67, SPR_FORCE_VEHICLE_TURN,  STR_9020_FORCE_VEHICLE_TO_TURN_AROUND },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  68,  85, SPR_SHOW_ORDERS,         STR_901D_SHOW_VEHICLE_S_ORDERS },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  86, 103, SPR_SHOW_VEHICLE_DETAILS,STR_9021_SHOW_ROAD_VEHICLE_DETAILS },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  32,  49, SPR_CLONE_ROADVEH,       STR_CLONE_ROAD_VEHICLE_INFO },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  50,  67, SPR_REFIT_VEHICLE,       STR_REFIT_ROAD_VEHICLE_TO_CARRY },
 
{      WWT_PANEL, RESIZE_LRB,   14, 232, 249, 104, 103, 0x0,                     STR_NULL },
 
{  WWT_RESIZEBOX, RESIZE_LRTB,  14, 238, 249, 104, 115, 0x0,                     STR_NULL },
 
{ WIDGETS_END }
 
};
 

	
 
static const WindowDesc _roadveh_view_desc = {
 
	WDP_AUTO, WDP_AUTO, 250, 116,
 
	WC_VEHICLE_VIEW,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_roadveh_view_widgets,
 
	RoadVehViewWndProc,
 
};
 

	
 
void ShowRoadVehViewWindow(const Vehicle *v)
 
{
 
	Window *w = AllocateWindowDescFront(&_roadveh_view_desc, v->index);
 

	
 
	if (w != NULL) {
 
		w->caption_color = v->owner;
 
		AssignWindowViewport(w, 3, 17, 0xE2, 0x54, w->window_number | (1 << 31), 0);
 
	}
 
}
 

	
 

	
 
static void DrawNewRoadVehWindow(Window *w)
 
{
 
	EngineID selected_id;
 
	EngineID e;
 
	uint count;
 
	int pos;
 
	int sel;
 
	int y;
 

	
 
	SetWindowWidgetDisabledState(w, 5, w->window_number == 0);
 

	
 
	count = 0;
 
	for (e = ROAD_ENGINES_INDEX; e < ROAD_ENGINES_INDEX + NUM_ROAD_ENGINES; e++) {
 
		if (HASBIT(GetEngine(e)->player_avail, _local_player)) count++;
 
	}
 
	SetVScrollCount(w, count);
 

	
 
	DrawWindowWidgets(w);
 

	
 
	y = 15;
 
	sel = WP(w,buildvehicle_d).sel_index;
 
	pos = w->vscroll.pos;
 
	selected_id = INVALID_ENGINE;
 
	for (e = ROAD_ENGINES_INDEX; e < ROAD_ENGINES_INDEX + NUM_ROAD_ENGINES; e++) {
 
		if (!HASBIT(GetEngine(e)->player_avail, _local_player)) continue;
 
		if (sel == 0) selected_id = e;
 
		if (IS_INT_INSIDE(--pos, -w->vscroll.cap, 0)) {
 
			DrawString(60, y + 2, GetCustomEngineName(e), sel == 0 ? 0xC : 0x10);
 
			DrawRoadVehEngine(30, y + 6, e, GetEnginePalette(e, _local_player));
 
			y += 14;
 
		}
 
		sel--;
 
	}
 

	
 
	WP(w,buildvehicle_d).sel_engine = selected_id;
 
	if (selected_id != INVALID_ENGINE) {
 
		const Widget *wi = &w->widget[4];
 
		DrawRoadVehPurchaseInfo(2, wi->top + 1, wi->right - wi->left - 2, selected_id);
 
	}
 
}
 

	
 
void CcBuildRoadVeh(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	const Vehicle *v;
 

	
 
	if (!success) return;
 

	
 
	v = GetVehicle(_new_vehicle_id);
 
	if (v->tile == _backup_orders_tile) {
 
		_backup_orders_tile = 0;
 
		RestoreVehicleOrders(v, _backup_orders_data);
 
	}
 
	ShowRoadVehViewWindow(v);
 
}
 

	
 
static void NewRoadVehWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		DrawNewRoadVehWindow(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 2: { /* listbox */
 
			uint i = (e->we.click.pt.y - 14) / 14;
 
			if (i < w->vscroll.cap) {
 
				WP(w,buildvehicle_d).sel_index = i + w->vscroll.pos;
 
				SetWindowDirty(w);
 
			}
 
		} break;
 

	
 
		case 5: { /* build */
 
			EngineID sel_eng = WP(w,buildvehicle_d).sel_engine;
 
			if (sel_eng != INVALID_ENGINE)
 
				DoCommandP(w->window_number, sel_eng, 0, CcBuildRoadVeh, CMD_BUILD_ROAD_VEH | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE));
 
		} break;
 

	
 
		case 6: { /* rename */
 
			EngineID sel_eng = WP(w,buildvehicle_d).sel_engine;
 
			if (sel_eng != INVALID_ENGINE) {
 
				WP(w,buildvehicle_d).rename_engine = sel_eng;
 
				ShowQueryString(GetCustomEngineName(sel_eng), STR_9036_RENAME_ROAD_VEHICLE_TYPE, 31, 160, w, CS_ALPHANUMERAL);
 
			}
 
		}	break;
 
		}
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT:
 
		if (e->we.edittext.str[0] != '\0') {
 
			_cmd_text = e->we.edittext.str;
 
			DoCommandP(0, WP(w, buildvehicle_d).rename_engine, 0, NULL,
 
				CMD_RENAME_ENGINE | CMD_MSG(STR_9037_CAN_T_RENAME_ROAD_VEHICLE));
 
		}
 
		break;
 

	
 
	case WE_RESIZE: {
 
		if (e->we.sizing.diff.y == 0) break;
 

	
 
		w->vscroll.cap += e->we.sizing.diff.y / 14;
 
		w->widget[2].data = (w->vscroll.cap << 8) + 1;
 
	} break;
 

	
 
	}
 
}
 

	
 
static const Widget _new_road_veh_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                   STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   247,     0,    13, STR_9006_NEW_ROAD_VEHICLES, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   235,    14,   125, 0x801,                      STR_9026_ROAD_VEHICLE_SELECTION},
 
{  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   236,   247,    14,   125, 0x0,                        STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,     RESIZE_TB,    14,     0,   247,   126,   217, 0x0,                        STR_NULL},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   117,   218,   229, STR_9007_BUILD_VEHICLE,     STR_9027_BUILD_THE_HIGHLIGHTED_ROAD},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   118,   235,   218,   229, STR_9034_RENAME,            STR_9035_RENAME_ROAD_VEHICLE_TYPE},
 
{  WWT_RESIZEBOX,     RESIZE_TB,    14,   236,   247,   218,   229, 0x0,                        STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _new_road_veh_desc = {
 
	WDP_AUTO, WDP_AUTO, 248, 230,
 
	WC_BUILD_VEHICLE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_new_road_veh_widgets,
 
	NewRoadVehWndProc
 
};
 

	
 
void ShowBuildRoadVehWindow(TileIndex tile)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_BUILD_VEHICLE, tile);
 

	
 
	w = AllocateWindowDescFront(&_new_road_veh_desc, tile);
 
	w->vscroll.cap = 8;
 
	w->widget[2].data = (w->vscroll.cap << 8) + 1;
 

	
 
	w->resize.step_height = 14;
 
	w->resize.height = w->height - 14 * 4; /* Minimum of 4 vehicles in the display */
 

	
 
	if (tile != 0) {
 
		w->caption_color = GetTileOwner(tile);
 
	} else {
 
		w->caption_color = _local_player;
 
	}
 
}
 

	
 

	
src/saveload.c
Show inline comments
 
deleted file
src/saveload.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/** @file
 
 * All actions handling saving and loading goes on in this file. The general actions
 
 * are as follows for saving a game (loading is analogous):
 
 * <ol>
 
 * <li>initialize the writer by creating a temporary memory-buffer for it
 
 * <li>go through all to-be saved elements, each 'chunk' (ChunkHandler) prefixed by a label
 
 * <li>use their description array (SaveLoad) to know what elements to save and in what version
 
 *    of the game it was active (used when loading)
 
 * <li>write all data byte-by-byte to the temporary buffer so it is endian-safe
 
 * <li>when the buffer is full; flush it to the output (eg save to file) (_sl.buf, _sl.bufp, _sl.bufe)
 
 * <li>repeat this until everything is done, and flush any remaining output to file
 
 * </ol>
 
 * @see ChunkHandler
 
 * @see SaveLoad
 
 */
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "hal.h"
 
#include "vehicle.h"
 
#include "station.h"
 
#include "thread.h"
 
#include "town.h"
 
#include "player.h"
 
#include "saveload.h"
 
#include "network/network.h"
 
#include "variables.h"
 
#include <setjmp.h>
 

	
 
const uint16 SAVEGAME_VERSION = 43;
 
uint16 _sl_version;       /// the major savegame version identifier
 
byte   _sl_minor_version; /// the minor savegame version, DO NOT USE!
 

	
 
typedef void WriterProc(uint len);
 
typedef uint ReaderProc(void);
 

	
 
/** The saveload struct, containing reader-writer functions, bufffer, version, etc. */
 
static struct {
 
	bool save;                           /// are we doing a save or a load atm. True when saving
 
	byte need_length;                    /// ???
 
	byte block_mode;                     /// ???
 
	bool error;                          /// did an error occur or not
 

	
 
	int obj_len;                         /// the length of the current object we are busy with
 
	int array_index, last_array_index;   /// in the case of an array, the current and last positions
 

	
 
	uint32 offs_base;                    /// the offset in number of bytes since we started writing data (eg uncompressed savegame size)
 

	
 
	WriterProc *write_bytes;             /// savegame writer function
 
	ReaderProc *read_bytes;              /// savegame loader function
 

	
 
	const ChunkHandler* const *chs;      /// the chunk of data that is being processed atm (vehicles, signs, etc.)
 
	const SaveLoad* const *includes;     /// the internal layouf of the given chunk
 

	
 
	/** When saving/loading savegames, they are always saved to a temporary memory-place
 
	 * to be flushed to file (save) or to final place (load) when full. */
 
	byte *bufp, *bufe;                   /// bufp(ointer) gives the current position in the buffer bufe(nd) gives the end of the buffer
 

	
 
	// these 3 may be used by compressor/decompressors.
 
	byte *buf;                           /// pointer to temporary memory to read/write, initialized by SaveLoadFormat->initread/write
 
	byte *buf_ori;                       /// pointer to the original memory location of buf, used to free it afterwards
 
	uint bufsize;                        /// the size of the temporary memory *buf
 
	FILE *fh;                            /// the file from which is read or written to
 

	
 
	void (*excpt_uninit)(void);          /// the function to execute on any encountered error
 
	const char *excpt_msg;               /// the error message
 
	jmp_buf excpt;                       /// @todo used to jump to "exception handler";  really ugly
 
} _sl;
 

	
 

	
 
enum NeedLengthValues {NL_NONE = 0, NL_WANTLENGTH = 1, NL_CALCLENGTH = 2};
 

	
 
/**
 
 * Fill the input buffer by reading from the file with the given reader
 
 */
 
static void SlReadFill(void)
 
{
 
	uint len = _sl.read_bytes();
 
	assert(len != 0);
 

	
 
	_sl.bufp = _sl.buf;
 
	_sl.bufe = _sl.buf + len;
 
	_sl.offs_base += len;
 
}
 

	
 
static inline uint32 SlGetOffs(void) {return _sl.offs_base - (_sl.bufe - _sl.bufp);}
 

	
 
/** Return the size in bytes of a certain type of normal/atomic variable
 
 * as it appears in memory. @see VarTypes
 
 * @param conv @VarType type of variable that is used for calculating the size
 
 * @return Return the size of this type in bytes */
 
static inline byte SlCalcConvMemLen(VarType conv)
 
{
 
	static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0};
 
	byte length = GB(conv, 4, 4);
 
	assert(length < lengthof(conv_mem_size));
 
	return conv_mem_size[length];
 
}
 

	
 
/** Return the size in bytes of a certain type of normal/atomic variable
 
 * as it appears in a saved game. @see VarTypes
 
 * @param conv @VarType type of variable that is used for calculating the size
 
 * @return Return the size of this type in bytes */
 
static inline byte SlCalcConvFileLen(VarType conv)
 
{
 
	static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2};
 
	byte length = GB(conv, 0, 4);
 
	assert(length < lengthof(conv_file_size));
 
	return conv_file_size[length];
 
}
 

	
 
/* Return the size in bytes of a reference (pointer) */
 
static inline size_t SlCalcRefLen(void) {return 2;}
 

	
 
/** Flush the output buffer by writing to disk with the given reader.
 
 * If the buffer pointer has not yet been set up, set it up now. Usually
 
 * only called when the buffer is full, or there is no more data to be processed
 
 */
 
static void SlWriteFill(void)
 
{
 
	// flush the buffer to disk (the writer)
 
	if (_sl.bufp != NULL) {
 
		uint len = _sl.bufp - _sl.buf;
 
		_sl.offs_base += len;
 
		if (len) _sl.write_bytes(len);
 
	}
 

	
 
	/* All the data from the buffer has been written away, rewind to the beginning
 
	* to start reading in more data */
 
	_sl.bufp = _sl.buf;
 
	_sl.bufe = _sl.buf + _sl.bufsize;
 
}
 

	
 
/** Error handler, calls longjmp to simulate an exception.
 
 * @todo this was used to have a central place to handle errors, but it is
 
 * pretty ugly, and seriously interferes with any multithreaded approaches */
 
static void NORETURN SlError(const char *msg)
 
{
 
	_sl.excpt_msg = msg;
 
	longjmp(_sl.excpt, 0);
 
}
 

	
 
/** Read in a single byte from file. If the temporary buffer is full,
 
 * flush it to its final destination
 
 * @return return the read byte from file
 
 */
 
static inline byte SlReadByteInternal(void)
 
{
 
	if (_sl.bufp == _sl.bufe) SlReadFill();
 
	return *_sl.bufp++;
 
}
 

	
 
/** Wrapper for SlReadByteInternal */
 
byte SlReadByte(void) {return SlReadByteInternal();}
 

	
 
/** Write away a single byte from memory. If the temporary buffer is full,
 
 * flush it to its destination (file)
 
 * @param b the byte that is currently written
 
 */
 
static inline void SlWriteByteInternal(byte b)
 
{
 
	if (_sl.bufp == _sl.bufe) SlWriteFill();
 
	*_sl.bufp++ = b;
 
}
 

	
 
/** Wrapper for SlWriteByteInternal */
 
void SlWriteByte(byte b) {SlWriteByteInternal(b);}
 

	
 
static inline int SlReadUint16(void)
 
{
 
	int x = SlReadByte() << 8;
 
	return x | SlReadByte();
 
}
 

	
 
static inline uint32 SlReadUint32(void)
 
{
 
	uint32 x = SlReadUint16() << 16;
 
	return x | SlReadUint16();
 
}
 

	
 
static inline uint64 SlReadUint64(void)
 
{
 
	uint32 x = SlReadUint32();
 
	uint32 y = SlReadUint32();
 
	return (uint64)x << 32 | y;
 
}
 

	
 
static inline void SlWriteUint16(uint16 v)
 
{
 
	SlWriteByte(GB(v, 8, 8));
 
	SlWriteByte(GB(v, 0, 8));
 
}
 

	
 
static inline void SlWriteUint32(uint32 v)
 
{
 
	SlWriteUint16(GB(v, 16, 16));
 
	SlWriteUint16(GB(v,  0, 16));
 
}
 

	
 
static inline void SlWriteUint64(uint64 x)
 
{
 
	SlWriteUint32((uint32)(x >> 32));
 
	SlWriteUint32((uint32)x);
 
}
 

	
 
/**
 
 * Read in the header descriptor of an object or an array.
 
 * If the highest bit is set (7), then the index is bigger than 127
 
 * elements, so use the next byte to read in the real value.
 
 * The actual value is then both bytes added with the first shifted
 
 * 8 bits to the left, and dropping the highest bit (which only indicated a big index).
 
 * x = ((x & 0x7F) << 8) + SlReadByte();
 
 * @return Return the value of the index
 
 */
 
static uint SlReadSimpleGamma(void)
 
{
 
	uint i = SlReadByte();
 
	if (HASBIT(i, 7)) {
 
		i &= ~0x80;
 
		if (HASBIT(i, 6)) {
 
			i &= ~0x40;
 
			if (HASBIT(i, 5)) {
 
				i &= ~0x20;
 
				if (HASBIT(i, 4))
 
					SlError("Unsupported gamma");
 
				i = (i << 8) | SlReadByte();
 
			}
 
			i = (i << 8) | SlReadByte();
 
		}
 
		i = (i << 8) | SlReadByte();
 
	}
 
	return i;
 
}
 

	
 
/**
 
 * Write the header descriptor of an object or an array.
 
 * If the element is bigger than 127, use 2 bytes for saving
 
 * and use the highest byte of the first written one as a notice
 
 * that the length consists of 2 bytes, etc.. like this:
 
 * 0xxxxxxx
 
 * 10xxxxxx xxxxxxxx
 
 * 110xxxxx xxxxxxxx xxxxxxxx
 
 * 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
 
 * @param i Index being written
 
 */
 

	
 
static void SlWriteSimpleGamma(uint i)
 
{
 
	if (i >= (1 << 7)) {
 
		if (i >= (1 << 14)) {
 
			if (i >= (1 << 21)) {
 
				assert(i < (1 << 28));
 
				SlWriteByte((byte)0xE0 | (i>>24));
 
				SlWriteByte((byte)(i>>16));
 
			} else {
 
				SlWriteByte((byte)0xC0 | (i>>16));
 
			}
 
			SlWriteByte((byte)(i>>8));
 
		} else {
 
			SlWriteByte((byte)(0x80 | (i>>8)));
 
		}
 
	}
 
	SlWriteByte(i);
 
}
 

	
 
/** Return how many bytes used to encode a gamma value */
 
static inline uint SlGetGammaLength(uint i) {
 
	return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21));
 
}
 

	
 
static inline uint SlReadSparseIndex(void) {return SlReadSimpleGamma();}
 
static inline void SlWriteSparseIndex(uint index) {SlWriteSimpleGamma(index);}
 

	
 
static inline uint SlReadArrayLength(void) {return SlReadSimpleGamma();}
 
static inline void SlWriteArrayLength(uint length) {SlWriteSimpleGamma(length);}
 
static inline uint SlGetArrayLength(uint length) {return SlGetGammaLength(length);}
 

	
 
void SlSetArrayIndex(uint index)
 
{
 
	_sl.need_length = NL_WANTLENGTH;
 
	_sl.array_index = index;
 
}
 

	
 
/**
 
 * Iterate through the elements of an array and read the whole thing
 
 * @return The index of the object, or -1 if we have reached the end of current block
 
 */
 
int SlIterateArray(void)
 
{
 
	int index;
 
	static uint32 next_offs;
 

	
 
	/* After reading in the whole array inside the loop
 
	 * we must have read in all the data, so we must be at end of current block. */
 
	assert(next_offs == 0 || SlGetOffs() == next_offs);
 

	
 
	while (true) {
 
		uint length = SlReadArrayLength();
 
		if (length == 0) {
 
			next_offs = 0;
 
			return -1;
 
		}
 

	
 
		_sl.obj_len = --length;
 
		next_offs = SlGetOffs() + length;
 

	
 
		switch (_sl.block_mode) {
 
		case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
 
		case CH_ARRAY:        index = _sl.array_index++; break;
 
		default:
 
			DEBUG(sl, 0, "SlIterateArray error");
 
			return -1; // error
 
		}
 

	
 
		if (length != 0) return index;
 
	}
 
}
 

	
 
/**
 
 * Sets the length of either a RIFF object or the number of items in an array.
 
 * This lets us load an object or an array of arbitrary size
 
 * @param length The length of the sought object/array
 
 */
 
void SlSetLength(size_t length)
 
{
 
	assert(_sl.save);
 

	
 
	switch (_sl.need_length) {
 
	case NL_WANTLENGTH:
 
		_sl.need_length = NL_NONE;
 
		switch (_sl.block_mode) {
 
		case CH_RIFF:
 
			// Ugly encoding of >16M RIFF chunks
 
			// The lower 24 bits are normal
 
			// The uppermost 4 bits are bits 24:27
 
			assert(length < (1<<28));
 
			SlWriteUint32((length & 0xFFFFFF) | ((length >> 24) << 28));
 
			break;
 
		case CH_ARRAY:
 
			assert(_sl.last_array_index <= _sl.array_index);
 
			while (++_sl.last_array_index <= _sl.array_index)
 
				SlWriteArrayLength(1);
 
			SlWriteArrayLength(length + 1);
 
			break;
 
		case CH_SPARSE_ARRAY:
 
			SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index.
 
			SlWriteSparseIndex(_sl.array_index);
 
			break;
 
		default: NOT_REACHED();
 
		} break;
 
	case NL_CALCLENGTH:
 
		_sl.obj_len += length;
 
		break;
 
	}
 
}
 

	
 
/**
 
 * Save/Load bytes. These do not need to be converted to Little/Big Endian
 
 * so directly write them or read them to/from file
 
 * @param ptr The source or destination of the object being manipulated
 
 * @param length number of bytes this fast CopyBytes lasts
 
 */
 
static void SlCopyBytes(void *ptr, size_t length)
 
{
 
	byte *p = (byte*)ptr;
 

	
 
	if (_sl.save) {
 
		for (; length != 0; length--) {SlWriteByteInternal(*p++);}
 
	} else {
 
		for (; length != 0; length--) {*p++ = SlReadByteInternal();}
 
	}
 
}
 

	
 
/** Read in bytes from the file/data structure but don't do
 
 * anything with them, discarding them in effect
 
 * @param length The amount of bytes that is being treated this way
 
 */
 
static inline void SlSkipBytes(size_t length)
 
{
 
	for (; length != 0; length--) SlReadByte();
 
}
 

	
 
/* Get the length of the current object */
 
uint SlGetFieldLength(void) {return _sl.obj_len;}
 

	
 
/** Return a signed-long version of the value of a setting
 
 * @param ptr pointer to the variable
 
 * @param conv type of variable, can be a non-clean
 
 * type, eg one with other flags because it is parsed
 
 * @return returns the value of the pointer-setting */
 
int64 ReadValue(const void *ptr, VarType conv)
 
{
 
	switch (GetVarMemType(conv)) {
 
	case SLE_VAR_BL:  return (*(bool*)ptr != 0);
 
	case SLE_VAR_I8:  return *(int8*  )ptr;
 
	case SLE_VAR_U8:  return *(byte*  )ptr;
 
	case SLE_VAR_I16: return *(int16* )ptr;
 
	case SLE_VAR_U16: return *(uint16*)ptr;
 
	case SLE_VAR_I32: return *(int32* )ptr;
 
	case SLE_VAR_U32: return *(uint32*)ptr;
 
	case SLE_VAR_I64: return *(int64* )ptr;
 
	case SLE_VAR_U64: return *(uint64*)ptr;
 
	case SLE_VAR_NULL:return 0;
 
	default: NOT_REACHED();
 
	}
 

	
 
	/* useless, but avoids compiler warning this way */
 
	return 0;
 
}
 

	
 
/** Write the value of a setting
 
 * @param ptr pointer to the variable
 
 * @param conv type of variable, can be a non-clean type, eg
 
 * with other flags. It is parsed upon read
 
 * @param var the new value being given to the variable */
 
void WriteValue(void *ptr, VarType conv, int64 val)
 
{
 
	switch (GetVarMemType(conv)) {
 
	case SLE_VAR_BL:  *(bool  *)ptr = (val != 0);  break;
 
	case SLE_VAR_I8:  *(int8  *)ptr = val; break;
 
	case SLE_VAR_U8:  *(byte  *)ptr = val; break;
 
	case SLE_VAR_I16: *(int16 *)ptr = val; break;
 
	case SLE_VAR_U16: *(uint16*)ptr = val; break;
 
	case SLE_VAR_I32: *(int32 *)ptr = val; break;
 
	case SLE_VAR_U32: *(uint32*)ptr = val; break;
 
	case SLE_VAR_I64: *(int64 *)ptr = val; break;
 
	case SLE_VAR_U64: *(uint64*)ptr = val; break;
 
	case SLE_VAR_NULL: break;
 
	default: NOT_REACHED();
 
	}
 
}
 

	
 
/**
 
 * Handle all conversion and typechecking of variables here.
 
 * In the case of saving, read in the actual value from the struct
 
 * and then write them to file, endian safely. Loading a value
 
 * goes exactly the opposite way
 
 * @param ptr The object being filled/read
 
 * @param conv @VarType type of the current element of the struct
 
 */
 
static void SlSaveLoadConv(void *ptr, VarType conv)
 
{
 
	int64 x = 0;
 

	
 
	if (_sl.save) { /* SAVE values */
 
		/* Read a value from the struct. These ARE endian safe. */
 
		x = ReadValue(ptr, conv);
 

	
 
		/* Write the value to the file and check if its value is in the desired range */
 
		switch (GetVarFileType(conv)) {
 
		case SLE_FILE_I8: assert(x >= -128 && x <= 127);     SlWriteByte(x);break;
 
		case SLE_FILE_U8: assert(x >= 0 && x <= 255);        SlWriteByte(x);break;
 
		case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
 
		case SLE_FILE_STRINGID:
 
		case SLE_FILE_U16:assert(x >= 0 && x <= 65535);      SlWriteUint16(x);break;
 
		case SLE_FILE_I32:
 
		case SLE_FILE_U32:                                   SlWriteUint32((uint32)x);break;
 
		case SLE_FILE_I64:
 
		case SLE_FILE_U64:                                   SlWriteUint64(x);break;
 
		default: NOT_REACHED();
 
		}
 
	} else { /* LOAD values */
 

	
 
		/* Read a value from the file */
 
		switch (GetVarFileType(conv)) {
 
		case SLE_FILE_I8:  x = (int8  )SlReadByte();   break;
 
		case SLE_FILE_U8:  x = (byte  )SlReadByte();   break;
 
		case SLE_FILE_I16: x = (int16 )SlReadUint16(); break;
 
		case SLE_FILE_U16: x = (uint16)SlReadUint16(); break;
 
		case SLE_FILE_I32: x = (int32 )SlReadUint32(); break;
 
		case SLE_FILE_U32: x = (uint32)SlReadUint32(); break;
 
		case SLE_FILE_I64: x = (int64 )SlReadUint64(); break;
 
		case SLE_FILE_U64: x = (uint64)SlReadUint64(); break;
 
		case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break;
 
		default: NOT_REACHED();
 
		}
 

	
 
		/* Write The value to the struct. These ARE endian safe. */
 
		WriteValue(ptr, conv, x);
 
	}
 
}
 

	
 
/** Calculate the net length of a string. This is in almost all cases
 
 * just strlen(), but if the string is not properly terminated, we'll
 
 * resort to the maximum length of the buffer.
 
 * @param ptr pointer to the stringbuffer
 
 * @param length maximum length of the string (buffer). If -1 we don't care
 
 * about a maximum length, but take string length as it is.
 
 * @return return the net length of the string */
 
static inline size_t SlCalcNetStringLen(const char *ptr, size_t length)
 
{
 
	return minu(strlen(ptr), length - 1);
 
}
 

	
 
/** Calculate the gross length of the string that it
 
 * will occupy in the savegame. This includes the real length, returned
 
 * by SlCalcNetStringLen and the length that the index will occupy.
 
 * @param ptr pointer to the stringbuffer
 
 * @param length maximum length of the string (buffer size, etc.)
 
 * @return return the gross length of the string */
 
static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType conv)
 
{
 
	size_t len;
 
	const char *str;
 

	
 
	switch (GetVarMemType(conv)) {
 
		default: NOT_REACHED();
 
		case SLE_VAR_STR:
 
		case SLE_VAR_STRQ:
 
			str = *(const char**)ptr;
 
			len = -1;
 
			break;
 
		case SLE_VAR_STRB:
 
		case SLE_VAR_STRBQ:
 
			str = (const char*)ptr;
 
			len = length;
 
			break;
 
	}
 

	
 
	len = SlCalcNetStringLen(str, len);
 
	return len + SlGetArrayLength(len); // also include the length of the index
 
}
 

	
 
/**
 
 * Save/Load a string.
 
 * @param ptr the string being manipulated
 
 * @param the length of the string (full length)
 
 * @param conv must be SLE_FILE_STRING */
 
static void SlString(void *ptr, size_t length, VarType conv)
 
{
 
	size_t len;
 

	
 
	if (_sl.save) { /* SAVE string */
 
		switch (GetVarMemType(conv)) {
 
			default: NOT_REACHED();
 
			case SLE_VAR_STRB:
 
			case SLE_VAR_STRBQ:
 
				len = SlCalcNetStringLen(ptr, length);
 
				break;
 
			case SLE_VAR_STR:
 
			case SLE_VAR_STRQ:
 
				ptr = *(char**)ptr;
 
				len = SlCalcNetStringLen(ptr, -1);
 
				break;
 
		}
 

	
 
		SlWriteArrayLength(len);
 
		SlCopyBytes(ptr, len);
 
	} else { /* LOAD string */
 
		len = SlReadArrayLength();
 

	
 
		switch (GetVarMemType(conv)) {
 
			default: NOT_REACHED();
 
			case SLE_VAR_STRB:
 
			case SLE_VAR_STRBQ:
 
				if (len >= length) {
 
					DEBUG(sl, 1, "String length in savegame is bigger than buffer, truncating");
 
					SlCopyBytes(ptr, length);
 
					SlSkipBytes(len - length);
 
					len = length - 1;
 
				} else {
 
					SlCopyBytes(ptr, len);
 
				}
 
				break;
 
			case SLE_VAR_STR:
 
			case SLE_VAR_STRQ: /* Malloc'd string, free previous incarnation, and allocate */
 
				free(*(char**)ptr);
 
				*(char**)ptr = malloc(len + 1); // terminating '\0'
 
				ptr = *(char**)ptr;
 
				SlCopyBytes(ptr, len);
 
				break;
 
		}
 

	
 
		((char*)ptr)[len] = '\0'; // properly terminate the string
 
	}
 
}
 

	
 
/**
 
 * Return the size in bytes of a certain type of atomic array
 
 * @param length The length of the array counted in elements
 
 * @param conv @VarType type of the variable that is used in calculating the size
 
 */
 
static inline size_t SlCalcArrayLen(uint length, VarType conv)
 
{
 
	return SlCalcConvFileLen(conv) * length;
 
}
 

	
 
/**
 
 * Save/Load an array.
 
 * @param array The array being manipulated
 
 * @param length The length of the array in elements
 
 * @param conv @VarType type of the atomic array (int, byte, uint64, etc.)
 
 */
 
void SlArray(void *array, uint length, VarType conv)
 
{
 
	// Automatically calculate the length?
 
	if (_sl.need_length != NL_NONE) {
 
		SlSetLength(SlCalcArrayLen(length, conv));
 
		// Determine length only?
 
		if (_sl.need_length == NL_CALCLENGTH) return;
 
	}
 

	
 
	/* NOTICE - handle some buggy stuff, in really old versions everything was saved
 
	 * as a byte-type. So detect this, and adjust array size accordingly */
 
	if (!_sl.save && _sl_version == 0) {
 
		if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
 
				conv == SLE_INT32 || conv == SLE_UINT32) {
 
			length *= SlCalcConvFileLen(conv);
 
			conv = SLE_INT8;
 
		}
 
	}
 

	
 
	/* If the size of elements is 1 byte both in file and memory, no special
 
	 * conversion is needed, use specialized copy-copy function to speed up things */
 
	if (conv == SLE_INT8 || conv == SLE_UINT8) {
 
		SlCopyBytes(array, length);
 
	} else {
 
		byte *a = (byte*)array;
 
		byte mem_size = SlCalcConvMemLen(conv);
 

	
 
		for (; length != 0; length --) {
 
			SlSaveLoadConv(a, conv);
 
			a += mem_size; // get size
 
		}
 
	}
 
}
 

	
 
/* Are we going to save this object or not? */
 
static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld)
 
{
 
	if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false;
 
	if (sld->conv & SLF_SAVE_NO) return false;
 

	
 
	return true;
 
}
 

	
 
/** Are we going to load this variable when loading a savegame or not?
 
 * @note If the variable is skipped it is skipped in the savegame
 
 * bytestream itself as well, so there is no need to skip it somewhere else */
 
static inline bool SlSkipVariableOnLoad(const SaveLoad *sld)
 
{
 
	if ((sld->conv & SLF_NETWORK_NO) && !_sl.save && _networking && !_network_server) {
 
		SlSkipBytes(SlCalcConvMemLen(sld->conv) * sld->length);
 
		return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
/**
 
 * Calculate the size of an object.
 
 * @param sld The @SaveLoad description of the object so we know how to manipulate it
 
 */
 
static size_t SlCalcObjLength(const void *object, const SaveLoad *sld)
 
{
 
	size_t length = 0;
 

	
 
	// Need to determine the length and write a length tag.
 
	for (; sld->cmd != SL_END; sld++) {
 
		length += SlCalcObjMemberLength(object, sld);
 
	}
 
	return length;
 
}
 

	
 
size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld)
 
{
 
	assert(_sl.save);
 

	
 
	switch (sld->cmd) {
 
		case SL_VAR:
 
		case SL_REF:
 
		case SL_ARR:
 
		case SL_STR:
 
			/* CONDITIONAL saveload types depend on the savegame version */
 
			if (!SlIsObjectValidInSavegame(sld)) break;
 

	
 
			switch (sld->cmd) {
 
			case SL_VAR: return SlCalcConvFileLen(sld->conv);
 
			case SL_REF: return SlCalcRefLen();
 
			case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv);
 
			case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv);
 
			default: NOT_REACHED();
 
			}
 
			break;
 
		case SL_WRITEBYTE: return 1; // a byte is logically of size 1
 
		case SL_INCLUDE: return SlCalcObjLength(object, _sl.includes[sld->version_from]);
 
		default: NOT_REACHED();
 
	}
 
	return 0;
 
}
 

	
 

	
 
static uint ReferenceToInt(const void* obj, SLRefType rt);
 
static void* IntToReference(uint index, SLRefType rt);
 

	
 

	
 
bool SlObjectMember(void *ptr, const SaveLoad *sld)
 
{
 
	VarType conv = GB(sld->conv, 0, 8);
 
	switch (sld->cmd) {
 
	case SL_VAR:
 
	case SL_REF:
 
	case SL_ARR:
 
	case SL_STR:
 
		/* CONDITIONAL saveload types depend on the savegame version */
 
		if (!SlIsObjectValidInSavegame(sld)) return false;
 
		if (SlSkipVariableOnLoad(sld)) return false;
 

	
 
		switch (sld->cmd) {
 
		case SL_VAR: SlSaveLoadConv(ptr, conv); break;
 
		case SL_REF: /* Reference variable, translate */
 
			/// @todo XXX - another artificial limitof 65K elements of pointers?
 
			if (_sl.save) { // XXX - read/write pointer as uint16? What is with higher indeces?
 
				SlWriteUint16(ReferenceToInt(*(void**)ptr, conv));
 
			} else {
 
				*(void**)ptr = IntToReference(SlReadUint16(), conv);
 
			}
 
			break;
 
		case SL_ARR: SlArray(ptr, sld->length, conv); break;
 
		case SL_STR: SlString(ptr, sld->length, conv); break;
 
		default: NOT_REACHED();
 
		}
 
		break;
 

	
 
	/* SL_WRITEBYTE translates a value of a variable to another one upon
 
   * saving or loading.
 
   * XXX - variable renaming abuse
 
   * game_value: the value of the variable ingame is abused by sld->version_from
 
   * file_value: the value of the variable in the savegame is abused by sld->version_to */
 
	case SL_WRITEBYTE:
 
		if (_sl.save) {
 
			SlWriteByte(sld->version_to);
 
		} else {
 
			*(byte*)ptr = sld->version_from;
 
		}
 
		break;
 

	
 
	/* SL_INCLUDE loads common code for a type
 
	 * XXX - variable renaming abuse
 
	 * include_index: common code to include from _desc_includes[], abused by sld->version_from */
 
	case SL_INCLUDE:
 
		SlObject(ptr, _sl.includes[sld->version_from]);
 
		break;
 
	default: NOT_REACHED();
 
	}
 
	return true;
 
}
 

	
 
/**
 
 * Main SaveLoad function.
 
 * @param object The object that is being saved or loaded
 
 * @param sld The @SaveLoad description of the object so we know how to manipulate it
 
 */
 
void SlObject(void *object, const SaveLoad *sld)
 
{
 
	// Automatically calculate the length?
 
	if (_sl.need_length != NL_NONE) {
 
		SlSetLength(SlCalcObjLength(object, sld));
 
		if (_sl.need_length == NL_CALCLENGTH) return;
 
	}
 

	
 
	for (; sld->cmd != SL_END; sld++) {
 
		void *ptr = GetVariableAddress(object, sld);
 
		SlObjectMember(ptr, sld);
 
	}
 
}
 

	
 
/**
 
 * Save or Load (a list of) global variables
 
 * @param desc The global variable that is being loaded or saved
 
 */
 
void SlGlobList(const SaveLoadGlobVarList *sldg)
 
{
 
	if (_sl.need_length != NL_NONE) {
 
		SlSetLength(SlCalcObjLength(NULL, (const SaveLoad*)sldg));
 
		if (_sl.need_length == NL_CALCLENGTH) return;
 
	}
 

	
 
	for (; sldg->cmd != SL_END; sldg++) {
 
		SlObjectMember(sldg->address, (const SaveLoad*)sldg);
 
	}
 
}
 

	
 
/**
 
 * Do something of which I have no idea what it is :P
 
 * @param proc The callback procedure that is called
 
 * @param arg The variable that will be used for the callback procedure
 
 */
 
void SlAutolength(AutolengthProc *proc, void *arg)
 
{
 
	uint32 offs;
 

	
 
	assert(_sl.save);
 

	
 
	// Tell it to calculate the length
 
	_sl.need_length = NL_CALCLENGTH;
 
	_sl.obj_len = 0;
 
	proc(arg);
 

	
 
	// Setup length
 
	_sl.need_length = NL_WANTLENGTH;
 
	SlSetLength(_sl.obj_len);
 

	
 
	offs = SlGetOffs() + _sl.obj_len;
 

	
 
	// And write the stuff
 
	proc(arg);
 

	
 
	assert(offs == SlGetOffs());
 
}
 

	
 
/**
 
 * Load a chunk of data (eg vehicles, stations, etc.)
 
 * @param ch The chunkhandler that will be used for the operation
 
 */
 
static void SlLoadChunk(const ChunkHandler *ch)
 
{
 
	byte m = SlReadByte();
 
	size_t len;
 
	uint32 endoffs;
 

	
 
	_sl.block_mode = m;
 
	_sl.obj_len = 0;
 

	
 
	switch (m) {
 
	case CH_ARRAY:
 
		_sl.array_index = 0;
 
		ch->load_proc();
 
		break;
 
	case CH_SPARSE_ARRAY:
 
		ch->load_proc();
 
		break;
 
	default:
 
		if ((m & 0xF) == CH_RIFF) {
 
			// Read length
 
			len = (SlReadByte() << 16) | ((m >> 4) << 24);
 
			len += SlReadUint16();
 
			_sl.obj_len = len;
 
			endoffs = SlGetOffs() + len;
 
			ch->load_proc();
 
			assert(SlGetOffs() == endoffs);
 
		} else {
 
			SlError("Invalid chunk type");
 
		}
 
		break;
 
	}
 
}
 

	
 
/* Stub Chunk handlers to only calculate length and do nothing else */
 
static ChunkSaveLoadProc *_tmp_proc_1;
 
static inline void SlStubSaveProc2(void *arg) {_tmp_proc_1();}
 
static void SlStubSaveProc(void) {SlAutolength(SlStubSaveProc2, NULL);}
 

	
 
/** Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is
 
 * prefixed by an ID identifying it, followed by data, and terminator where appropiate
 
 * @param ch The chunkhandler that will be used for the operation
 
 */
 
static void SlSaveChunk(const ChunkHandler *ch)
 
{
 
	ChunkSaveLoadProc *proc = ch->save_proc;
 

	
 
	SlWriteUint32(ch->id);
 
	DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
 

	
 
	if (ch->flags & CH_AUTO_LENGTH) {
 
		// Need to calculate the length. Solve that by calling SlAutoLength in the save_proc.
 
		_tmp_proc_1 = proc;
 
		proc = SlStubSaveProc;
 
	}
 

	
 
	_sl.block_mode = ch->flags & CH_TYPE_MASK;
 
	switch (ch->flags & CH_TYPE_MASK) {
 
	case CH_RIFF:
 
		_sl.need_length = NL_WANTLENGTH;
 
		proc();
 
		break;
 
	case CH_ARRAY:
 
		_sl.last_array_index = 0;
 
		SlWriteByte(CH_ARRAY);
 
		proc();
 
		SlWriteArrayLength(0); // Terminate arrays
 
		break;
 
	case CH_SPARSE_ARRAY:
 
		SlWriteByte(CH_SPARSE_ARRAY);
 
		proc();
 
		SlWriteArrayLength(0); // Terminate arrays
 
		break;
 
	default: NOT_REACHED();
 
	}
 
}
 

	
 
/** Save all chunks */
 
static void SlSaveChunks(void)
 
{
 
	const ChunkHandler *ch;
 
	const ChunkHandler* const *chsc;
 
	uint p;
 

	
 
	for (p = 0; p != CH_NUM_PRI_LEVELS; p++) {
 
		for (chsc = _sl.chs; (ch = *chsc++) != NULL;) {
 
			while (true) {
 
				if (((ch->flags >> CH_PRI_SHL) & (CH_NUM_PRI_LEVELS - 1)) == p)
 
					SlSaveChunk(ch);
 
				if (ch->flags & CH_LAST)
 
					break;
 
				ch++;
 
			}
 
		}
 
	}
 

	
 
	// Terminator
 
	SlWriteUint32(0);
 
}
 

	
 
/** Find the ChunkHandler that will be used for processing the found
 
 * chunk in the savegame or in memory
 
 * @param id the chunk in question
 
 * @return returns the appropiate chunkhandler
 
 */
 
static const ChunkHandler *SlFindChunkHandler(uint32 id)
 
{
 
	const ChunkHandler *ch;
 
	const ChunkHandler *const *chsc;
 
	for (chsc = _sl.chs; (ch=*chsc++) != NULL;) {
 
		for (;;) {
 
			if (ch->id == id) return ch;
 
			if (ch->flags & CH_LAST) break;
 
			ch++;
 
		}
 
	}
 
	return NULL;
 
}
 

	
 
/** Load all chunks */
 
static void SlLoadChunks(void)
 
{
 
	uint32 id;
 
	const ChunkHandler *ch;
 

	
 
	for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
 
		DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
 

	
 
		ch = SlFindChunkHandler(id);
 
		if (ch == NULL) SlError("found unknown tag in savegame (sync error)");
 
		SlLoadChunk(ch);
 
	}
 
}
 

	
 
//*******************************************
 
//********** START OF LZO CODE **************
 
//*******************************************
 
#define LZO_SIZE 8192
 

	
 
#include "minilzo.h"
 

	
 
static uint ReadLZO(void)
 
{
 
	byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8];
 
	uint32 tmp[2];
 
	uint32 size;
 
	uint len;
 

	
 
	// Read header
 
	if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError("file read failed");
 

	
 
	// Check if size is bad
 
	((uint32*)out)[0] = size = tmp[1];
 

	
 
	if (_sl_version != 0) {
 
		tmp[0] = TO_BE32(tmp[0]);
 
		size = TO_BE32(size);
 
	}
 

	
 
	if (size >= sizeof(out)) SlError("inconsistent size");
 

	
 
	// Read block
 
	if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError("file read failed");
 

	
 
	// Verify checksum
 
	if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError("bad checksum");
 

	
 
	// Decompress
 
	lzo1x_decompress(out + sizeof(uint32)*1, size, _sl.buf, &len, NULL);
 
	return len;
 
}
 

	
 
// p contains the pointer to the buffer, len contains the pointer to the length.
 
// len bytes will be written, p and l will be updated to reflect the next buffer.
 
static void WriteLZO(uint size)
 
{
 
	byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8];
 
	byte wrkmem[sizeof(byte*)*4096];
 
	uint outlen;
 

	
 
	lzo1x_1_compress(_sl.buf, size, out + sizeof(uint32)*2, &outlen, wrkmem);
 
	((uint32*)out)[1] = TO_BE32(outlen);
 
	((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32)));
 
	if (fwrite(out, outlen + sizeof(uint32)*2, 1, _sl.fh) != 1) SlError("file write failed");
 
}
 

	
 
static bool InitLZO(void)
 
{
 
	_sl.bufsize = LZO_SIZE;
 
	_sl.buf = _sl.buf_ori = (byte*)malloc(LZO_SIZE);
 
	return true;
 
}
 

	
 
static void UninitLZO(void)
 
{
 
	free(_sl.buf_ori);
 
}
 

	
 
//*********************************************
 
//******** START OF NOCOMP CODE (uncompressed)*
 
//*********************************************
 
static uint ReadNoComp(void)
 
{
 
	return fread(_sl.buf, 1, LZO_SIZE, _sl.fh);
 
}
 

	
 
static void WriteNoComp(uint size)
 
{
 
	fwrite(_sl.buf, 1, size, _sl.fh);
 
}
 

	
 
static bool InitNoComp(void)
 
{
 
	_sl.bufsize = LZO_SIZE;
 
	_sl.buf = _sl.buf_ori =(byte*)malloc(LZO_SIZE);
 
	return true;
 
}
 

	
 
static void UninitNoComp(void)
 
{
 
	free(_sl.buf_ori);
 
}
 

	
 
//********************************************
 
//********** START OF MEMORY CODE (in ram)****
 
//********************************************
 

	
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "gfx.h"
 
#include "gui.h"
 

	
 
typedef struct ThreadedSave {
 
	uint count;
 
	bool ff_state;
 
	bool saveinprogress;
 
	CursorID cursor;
 
} ThreadedSave;
 

	
 
/* A maximum size of of 128K * 500 = 64.000KB savegames */
 
STATIC_OLD_POOL(Savegame, byte, 17, 500, NULL, NULL)
 
static ThreadedSave _ts;
 

	
 
static bool InitMem(void)
 
{
 
	_ts.count = 0;
 

	
 
	CleanPool(&_Savegame_pool);
 
	AddBlockToPool(&_Savegame_pool);
 

	
 
	/* A block from the pool is a contigious area of memory, so it is safe to write to it sequentially */
 
	_sl.bufsize = GetSavegamePoolSize();
 
	_sl.buf = GetSavegame(_ts.count);
 
	return true;
 
}
 

	
 
static void UnInitMem(void)
 
{
 
	CleanPool(&_Savegame_pool);
 
}
 

	
 
static void WriteMem(uint size)
 
{
 
	_ts.count += size;
 
	/* Allocate new block and new buffer-pointer */
 
	AddBlockIfNeeded(&_Savegame_pool, _ts.count);
 
	_sl.buf = GetSavegame(_ts.count);
 
}
 

	
 
//********************************************
 
//********** START OF ZLIB CODE **************
 
//********************************************
 

	
 
#if defined(WITH_ZLIB)
 
#include <zlib.h>
 

	
 
static z_stream _z;
 

	
 
static bool InitReadZlib(void)
 
{
 
	memset(&_z, 0, sizeof(_z));
 
	if (inflateInit(&_z) != Z_OK) return false;
 

	
 
	_sl.bufsize = 4096;
 
	_sl.buf = _sl.buf_ori = (byte*)malloc(4096 + 4096); // also contains fread buffer
 
	return true;
 
}
 

	
 
static uint ReadZlib(void)
 
{
 
	int r;
 

	
 
	_z.next_out = _sl.buf;
 
	_z.avail_out = 4096;
 

	
 
	do {
 
		// read more bytes from the file?
 
		if (_z.avail_in == 0) {
 
			_z.avail_in = fread(_z.next_in = _sl.buf + 4096, 1, 4096, _sl.fh);
 
		}
 

	
 
		// inflate the data
 
		r = inflate(&_z, 0);
 
		if (r == Z_STREAM_END)
 
			break;
 

	
 
		if (r != Z_OK)
 
			SlError("inflate() failed");
 
	} while (_z.avail_out);
 

	
 
	return 4096 - _z.avail_out;
 
}
 

	
 
static void UninitReadZlib(void)
 
{
 
	inflateEnd(&_z);
 
	free(_sl.buf_ori);
 
}
 

	
 
static bool InitWriteZlib(void)
 
{
 
	memset(&_z, 0, sizeof(_z));
 
	if (deflateInit(&_z, 6) != Z_OK) return false;
 

	
 
	_sl.bufsize = 4096;
 
	_sl.buf = _sl.buf_ori = (byte*)malloc(4096); // also contains fread buffer
 
	return true;
 
}
 

	
 
static void WriteZlibLoop(z_streamp z, byte *p, uint len, int mode)
 
{
 
	byte buf[1024]; // output buffer
 
	int r;
 
	uint n;
 
	z->next_in = p;
 
	z->avail_in = len;
 
	do {
 
		z->next_out = buf;
 
		z->avail_out = sizeof(buf);
 
		r = deflate(z, mode);
 
			// bytes were emitted?
 
		if ((n=sizeof(buf) - z->avail_out) != 0) {
 
			if (fwrite(buf, n, 1, _sl.fh) != 1) SlError("file write error");
 
		}
 
		if (r == Z_STREAM_END)
 
			break;
 
		if (r != Z_OK) SlError("zlib returned error code");
 
	} while (z->avail_in || !z->avail_out);
 
}
 

	
 
static void WriteZlib(uint len)
 
{
 
	WriteZlibLoop(&_z, _sl.buf, len, 0);
 
}
 

	
 
static void UninitWriteZlib(void)
 
{
 
	// flush any pending output.
 
	if (_sl.fh) WriteZlibLoop(&_z, NULL, 0, Z_FINISH);
 
	deflateEnd(&_z);
 
	free(_sl.buf_ori);
 
}
 

	
 
#endif /* WITH_ZLIB */
 

	
 
//*******************************************
 
//************* END OF CODE *****************
 
//*******************************************
 

	
 
// these define the chunks
 
extern const ChunkHandler _misc_chunk_handlers[];
 
extern const ChunkHandler _setting_chunk_handlers[];
 
extern const ChunkHandler _player_chunk_handlers[];
 
extern const ChunkHandler _engine_chunk_handlers[];
 
extern const ChunkHandler _veh_chunk_handlers[];
 
extern const ChunkHandler _waypoint_chunk_handlers[];
 
extern const ChunkHandler _depot_chunk_handlers[];
 
extern const ChunkHandler _order_chunk_handlers[];
 
extern const ChunkHandler _town_chunk_handlers[];
 
extern const ChunkHandler _sign_chunk_handlers[];
 
extern const ChunkHandler _station_chunk_handlers[];
 
extern const ChunkHandler _industry_chunk_handlers[];
 
extern const ChunkHandler _economy_chunk_handlers[];
 
extern const ChunkHandler _animated_tile_chunk_handlers[];
 
extern const ChunkHandler _newgrf_chunk_handlers[];
 

	
 
static const ChunkHandler * const _chunk_handlers[] = {
 
	_misc_chunk_handlers,
 
	_setting_chunk_handlers,
 
	_veh_chunk_handlers,
 
	_waypoint_chunk_handlers,
 
	_depot_chunk_handlers,
 
	_order_chunk_handlers,
 
	_industry_chunk_handlers,
 
	_economy_chunk_handlers,
 
	_engine_chunk_handlers,
 
	_town_chunk_handlers,
 
	_sign_chunk_handlers,
 
	_station_chunk_handlers,
 
	_player_chunk_handlers,
 
	_animated_tile_chunk_handlers,
 
	_newgrf_chunk_handlers,
 
	NULL,
 
};
 

	
 
// used to include a vehicle desc in another desc.
 
extern const SaveLoad _common_veh_desc[];
 
static const SaveLoad* const _desc_includes[] = {
 
	_common_veh_desc
 
};
 

	
 
/**
 
 * Pointers cannot be saved to a savegame, so this functions gets
 
 * the index of the item, and if not available, it hussles with
 
 * pointers (looks really bad :()
 
 * Remember that a NULL item has value 0, and all
 
 * indeces have +1, so vehicle 0 is saved as index 1.
 
 * @param obj The object that we want to get the index of
 
 * @param rt @SLRefType type of the object the index is being sought of
 
 * @return Return the pointer converted to an index of the type pointed to
 
 */
 
static uint ReferenceToInt(const void *obj, SLRefType rt)
 
{
 
	if (obj == NULL) return 0;
 

	
 
	switch (rt) {
 
		case REF_VEHICLE_OLD: // Old vehicles we save as new onces
 
		case REF_VEHICLE:   return ((const  Vehicle*)obj)->index + 1;
 
		case REF_STATION:   return ((const  Station*)obj)->index + 1;
 
		case REF_TOWN:      return ((const     Town*)obj)->index + 1;
 
		case REF_ORDER:     return ((const    Order*)obj)->index + 1;
 
		case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
 
		case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
 
		default: NOT_REACHED();
 
	}
 

	
 
	return 0; // avoid compiler warning
 
}
 

	
 
/**
 
 * Pointers cannot be loaded from a savegame, so this function
 
 * gets the index from the savegame and returns the appropiate
 
 * pointer from the already loaded base.
 
 * Remember that an index of 0 is a NULL pointer so all indeces
 
 * are +1 so vehicle 0 is saved as 1.
 
 * @param index The index that is being converted to a pointer
 
 * @param rt @SLRefType type of the object the pointer is sought of
 
 * @return Return the index converted to a pointer of any type
 
 */
 
static void *IntToReference(uint index, SLRefType rt)
 
{
 
	/* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE,
 
	 * and should be loaded like that */
 
	if (rt == REF_VEHICLE_OLD && !CheckSavegameVersionOldStyle(4, 4))
 
		rt = REF_VEHICLE;
 

	
 
	/* No need to look up NULL pointers, just return immediately */
 
	if (rt != REF_VEHICLE_OLD && index == 0)
 
		return NULL;
 

	
 
	index--; // correct for the NULL index
 

	
 
	switch (rt) {
 
		case REF_ORDER: {
 
			if (!AddBlockIfNeeded(&_Order_pool, index))
 
				error("Orders: failed loading savegame: too many orders");
 
			return GetOrder(index);
 
		}
 
		case REF_VEHICLE: {
 
			if (!AddBlockIfNeeded(&_Vehicle_pool, index))
 
				error("Vehicles: failed loading savegame: too many vehicles");
 
			return GetVehicle(index);
 
		}
 
		case REF_STATION: {
 
			if (!AddBlockIfNeeded(&_Station_pool, index))
 
				error("Stations: failed loading savegame: too many stations");
 
			return GetStation(index);
 
		}
 
		case REF_TOWN: {
 
			if (!AddBlockIfNeeded(&_Town_pool, index))
 
				error("Towns: failed loading savegame: too many towns");
 
			return GetTown(index);
 
		}
 
		case REF_ROADSTOPS: {
 
			if (!AddBlockIfNeeded(&_RoadStop_pool, index))
 
				error("RoadStops: failed loading savegame: too many RoadStops");
 
			return GetRoadStop(index);
 
		}
 
		case REF_ENGINE_RENEWS: {
 
			if (!AddBlockIfNeeded(&_EngineRenew_pool, index))
 
				error("EngineRenews: failed loading savegame: too many EngineRenews");
 
			return GetEngineRenew(index);
 
		}
 

	
 
		case REF_VEHICLE_OLD: {
 
			/* Old vehicles were saved differently:
 
			 * invalid vehicle was 0xFFFF,
 
			 * and the index was not - 1.. correct for this */
 
			index++;
 
			if (index == INVALID_VEHICLE)
 
				return NULL;
 

	
 
			if (!AddBlockIfNeeded(&_Vehicle_pool, index))
 
				error("Vehicles: failed loading savegame: too many vehicles");
 
			return GetVehicle(index);
 
		}
 
		default: NOT_REACHED();
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/** The format for a reader/writer type of a savegame */
 
typedef struct {
 
	const char *name;           /// name of the compressor/decompressor (debug-only)
 
	uint32 tag;                 /// the 4-letter tag by which it is identified in the savegame
 

	
 
	bool (*init_read)(void);    /// function executed upon initalization of the loader
 
	ReaderProc *reader;         /// function that loads the data from the file
 
	void (*uninit_read)(void);  /// function executed when reading is finished
 

	
 
	bool (*init_write)(void);   /// function executed upon intialization of the saver
 
	WriterProc *writer;         /// function that saves the data to the file
 
	void (*uninit_write)(void); /// function executed when writing is done
 
} SaveLoadFormat;
 

	
 
static const SaveLoadFormat _saveload_formats[] = {
 
	{"memory", 0,                NULL,         NULL,       NULL,           InitMem,       WriteMem,    UnInitMem},
 
	{"lzo",    TO_BE32X('OTTD'), InitLZO,      ReadLZO,    UninitLZO,      InitLZO,       WriteLZO,    UninitLZO},
 
	{"none",   TO_BE32X('OTTN'), InitNoComp,   ReadNoComp, UninitNoComp,   InitNoComp,    WriteNoComp, UninitNoComp},
 
#if defined(WITH_ZLIB)
 
	{"zlib",   TO_BE32X('OTTZ'), InitReadZlib, ReadZlib,   UninitReadZlib, InitWriteZlib, WriteZlib,   UninitWriteZlib},
 
#else
 
	{"zlib",   TO_BE32X('OTTZ'), NULL,         NULL,       NULL,           NULL,          NULL,        NULL},
 
#endif
 
};
 

	
 
/**
 
 * Return the savegameformat of the game. Whether it was create with ZLIB compression
 
 * uncompressed, or another type
 
 * @param s Name of the savegame format. If NULL it picks the first available one
 
 * @return Pointer to @SaveLoadFormat struct giving all characteristics of this type of savegame
 
 */
 
static const SaveLoadFormat *GetSavegameFormat(const char *s)
 
{
 
	const SaveLoadFormat *def = endof(_saveload_formats) - 1;
 

	
 
	// find default savegame format, the highest one with which files can be written
 
	while (!def->init_write) def--;
 

	
 
	if (s != NULL && s[0] != '\0') {
 
		const SaveLoadFormat *slf;
 
		for (slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) {
 
			if (slf->init_write != NULL && strcmp(s, slf->name) == 0)
 
				return slf;
 
		}
 

	
 
		ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name);
 
	}
 
	return def;
 
}
 

	
 
// actual loader/saver function
 
void InitializeGame(int mode, uint size_x, uint size_y);
 
extern bool AfterLoadGame(void);
 
extern void BeforeSaveGame(void);
 
extern bool LoadOldSaveGame(const char *file);
 

	
 
/** Small helper function to close the to be loaded savegame an signal error */
 
static inline SaveOrLoadResult AbortSaveLoad(void)
 
{
 
	if (_sl.fh != NULL) fclose(_sl.fh);
 

	
 
	_sl.fh = NULL;
 
	return SL_ERROR;
 
}
 

	
 
/** Update the gui accordingly when starting saving
 
 * and set locks on saveload. Also turn off fast-forward cause with that
 
 * saving takes Aaaaages */
 
void SaveFileStart(void)
 
{
 
	_ts.ff_state = _fast_forward;
 
	_fast_forward = false;
 
	if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ);
 

	
 
	SendWindowMessage(WC_STATUS_BAR, 0, true, 0, 0);
 
	_ts.saveinprogress = true;
 
}
 

	
 
/** Update the gui accordingly when saving is done and release locks
 
 * on saveload */
 
void SaveFileDone(void)
 
{
 
	_fast_forward = _ts.ff_state;
 
	if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
 

	
 
	SendWindowMessage(WC_STATUS_BAR, 0, false, 0, 0);
 
	_ts.saveinprogress = false;
 
}
 

	
 
/** Show a gui message when saving has failed */
 
void SaveFileError(void)
 
{
 
	ShowErrorMessage(STR_4007_GAME_SAVE_FAILED, STR_NULL, 0, 0);
 
	SaveFileDone();
 
}
 

	
 
static OTTDThread* save_thread;
 

	
 
/** We have written the whole game into memory, _Savegame_pool, now find
 
 * and appropiate compressor and start writing to file.
 
 */
 
static void* SaveFileToDisk(void *arg)
 
{
 
	const SaveLoadFormat *fmt;
 
	uint32 hdr[2];
 

	
 
	/* XXX - Setup setjmp error handler if an error occurs anywhere deep during
 
	 * loading/saving execute a longjmp() and continue execution here */
 
	if (setjmp(_sl.excpt)) {
 
		AbortSaveLoad();
 
		_sl.excpt_uninit();
 

	
 
		fprintf(stderr, "Save game failed: %s.", _sl.excpt_msg);
 
		if (arg != NULL) {
 
			OTTD_SendThreadMessage(MSG_OTTD_SAVETHREAD_ERROR);
 
		} else {
 
			SaveFileError();
 
		}
 
		return NULL;
 
	}
 

	
 
	fmt = GetSavegameFormat(_savegame_format);
 

	
 
	/* We have written our stuff to memory, now write it to file! */
 
	hdr[0] = fmt->tag;
 
	hdr[1] = TO_BE32(SAVEGAME_VERSION << 16);
 
	if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError("file write failed");
 

	
 
	if (!fmt->init_write()) SlError("cannot initialize compressor");
 

	
 
	{
 
		uint i;
 
		uint count = 1 << Savegame_POOL_BLOCK_SIZE_BITS;
 

	
 
		assert(_ts.count == _sl.offs_base);
 
		for (i = 0; i != _Savegame_pool.current_blocks - 1; i++) {
 
			_sl.buf = _Savegame_pool.blocks[i];
 
			fmt->writer(count);
 
		}
 

	
 
		/* The last block is (almost) always not fully filled, so only write away
 
		 * as much data as it is in there */
 
		_sl.buf = _Savegame_pool.blocks[i];
 
		fmt->writer(_ts.count - (i * count));
 
	}
 

	
 
	fmt->uninit_write();
 
	assert(_ts.count == _sl.offs_base);
 
	GetSavegameFormat("memory")->uninit_write(); // clean the memorypool
 
	fclose(_sl.fh);
 

	
 
	if (arg != NULL) OTTD_SendThreadMessage(MSG_OTTD_SAVETHREAD_DONE);
 
	return NULL;
 
}
 

	
 
void WaitTillSaved(void)
 
{
 
	OTTDJoinThread(save_thread);
 
	save_thread = NULL;
 
}
 

	
 
/**
 
 * Main Save or Load function where the high-level saveload functions are
 
 * handled. It opens the savegame, selects format and checks versions
 
 * @param filename The name of the savegame being created/loaded
 
 * @param mode Save or load. Load can also be a TTD(Patch) game. Use SL_LOAD, SL_OLD_LOAD or SL_SAVE
 
 * @return Return the results of the action. SL_OK, SL_ERROR or SL_REINIT ("unload" the game)
 
 */
 
SaveOrLoadResult SaveOrLoad(const char *filename, int mode)
 
{
 
	uint32 hdr[2];
 
	const SaveLoadFormat *fmt;
 

	
 
	/* An instance of saving is already active, so don't go saving again */
 
	if (_ts.saveinprogress && mode == SL_SAVE) {
 
		// if not an autosave, but a user action, show error message
 
		if (!_do_autosave) ShowErrorMessage(INVALID_STRING_ID, STR_SAVE_STILL_IN_PROGRESS, 0, 0);
 
		return SL_OK;
 
	}
 
	WaitTillSaved();
 

	
 
	/* Load a TTDLX or TTDPatch game */
 
	if (mode == SL_OLD_LOAD) {
 
		InitializeGame(IG_DATE_RESET, 256, 256); // set a mapsize of 256x256 for TTDPatch games or it might get confused
 
		if (!LoadOldSaveGame(filename)) return SL_REINIT;
 
		_sl_version = 0;
 
		AfterLoadGame();
 
		return SL_OK;
 
	}
 

	
 
	_sl.fh = (mode == SL_SAVE) ? fopen(filename, "wb") : fopen(filename, "rb");
 
	if (_sl.fh == NULL) {
 
		DEBUG(sl, 0, "Cannot open savegame '%s' for saving/loading.", filename);
 
		return SL_ERROR;
 
	}
 

	
 
	_sl.bufe = _sl.bufp = NULL;
 
	_sl.offs_base = 0;
 
	_sl.save = mode;
 
	_sl.includes = _desc_includes;
 
	_sl.chs = _chunk_handlers;
 

	
 
	/* XXX - Setup setjmp error handler if an error occurs anywhere deep during
 
	 * loading/saving execute a longjmp() and continue execution here */
 
	if (setjmp(_sl.excpt)) {
 
		AbortSaveLoad();
 

	
 
		// deinitialize compressor.
 
		_sl.excpt_uninit();
 

	
 
		/* A saver/loader exception!! reinitialize all variables to prevent crash! */
 
		if (mode == SL_LOAD) {
 
			ShowInfoF("Load game failed: %s.", _sl.excpt_msg);
 
			return SL_REINIT;
 
		}
 

	
 
		ShowInfoF("Save game failed: %s.", _sl.excpt_msg);
 
		return SL_ERROR;
 
	}
 

	
 
	/* General tactic is to first save the game to memory, then use an available writer
 
	 * to write it to file, either in threaded mode if possible, or single-threaded */
 
	if (mode == SL_SAVE) { /* SAVE game */
 
		fmt = GetSavegameFormat("memory"); // write to memory
 

	
 
		_sl.write_bytes = fmt->writer;
 
		_sl.excpt_uninit = fmt->uninit_write;
 
		if (!fmt->init_write()) {
 
			DEBUG(sl, 0, "Initializing writer '%s' failed.", fmt->name);
 
			return AbortSaveLoad();
 
		}
 

	
 
		_sl_version = SAVEGAME_VERSION;
 

	
 
		BeforeSaveGame();
 
		SlSaveChunks();
 
		SlWriteFill(); // flush the save buffer
 

	
 
		SaveFileStart();
 
		if (_network_server ||
 
					(save_thread = OTTDCreateThread(&SaveFileToDisk, (void*)"")) == NULL) {
 
			DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
 
			SaveFileToDisk(NULL);
 
			SaveFileDone();
 
		}
 

	
 
	} else { /* LOAD game */
 
		assert(mode == SL_LOAD);
 

	
 
		if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) {
 
			DEBUG(sl, 0, "Cannot read savegame header, aborting");
 
			return AbortSaveLoad();
 
		}
 

	
 
		// see if we have any loader for this type.
 
		for (fmt = _saveload_formats; ; fmt++) {
 
			/* No loader found, treat as version 0 and use LZO format */
 
			if (fmt == endof(_saveload_formats)) {
 
				DEBUG(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
 
				rewind(_sl.fh);
 
				_sl_version = 0;
 
				_sl_minor_version = 0;
 
				fmt = _saveload_formats + 1; // LZO
 
				break;
 
			}
 

	
 
			if (fmt->tag == hdr[0]) {
 
				// check version number
 
				_sl_version = TO_BE32(hdr[1]) >> 16;
 
				/* Minor is not used anymore from version 18.0, but it is still needed
 
				 *  in versions before that (4 cases) which can't be removed easy.
 
				 *  Therefor it is loaded, but never saved (or, it saves a 0 in any scenario).
 
				 *  So never EVER use this minor version again. -- TrueLight -- 22-11-2005 */
 
				_sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
 

	
 
				DEBUG(sl, 1, "Loading savegame version %d", _sl_version);
 

	
 
				/* Is the version higher than the current? */
 
				if (_sl_version > SAVEGAME_VERSION) {
 
					DEBUG(sl, 0, "Savegame version invalid");
 
					return AbortSaveLoad();
 
				}
 
				break;
 
			}
 
		}
 

	
 
		_sl.read_bytes = fmt->reader;
 
		_sl.excpt_uninit = fmt->uninit_read;
 

	
 
		// loader for this savegame type is not implemented?
 
		if (fmt->init_read == NULL) {
 
			ShowInfoF("Loader for '%s' is not available.", fmt->name);
 
			return AbortSaveLoad();
 
		}
 

	
 
		if (!fmt->init_read()) {
 
			DEBUG(sl, 0, "Initializing loader '%s' failed", fmt->name);
 
			return AbortSaveLoad();
 
		}
 

	
 
		/* Old maps were hardcoded to 256x256 and thus did not contain
 
		 * any mapsize information. Pre-initialize to 256x256 to not to
 
		 * confuse old games */
 
		InitializeGame(IG_DATE_RESET, 256, 256);
 

	
 
		SlLoadChunks();
 
		fmt->uninit_read();
 
		fclose(_sl.fh);
 

	
 
		/* After loading fix up savegame for any internal changes that
 
		 * might've occured since then. If it fails, load back the old game */
 
		if (!AfterLoadGame()) return SL_REINIT;
 
	}
 

	
 
	return SL_OK;
 
}
 

	
 
/** Do a save when exiting the game (patch option) _patches.autosave_on_exit */
 
void DoExitSave(void)
 
{
 
	char buf[200];
 
	snprintf(buf, sizeof(buf), "%s%sexit.sav", _paths.autosave_dir, PATHSEP);
 
	SaveOrLoad(buf, SL_SAVE);
 
}
 

	
 
#if 0
 
/**
 
 * Function to get the type of the savegame by looking at the file header.
 
 * NOTICE: Not used right now, but could be used if extensions of savegames are garbled
 
 * @param file Savegame to be checked
 
 * @return SL_OLD_LOAD or SL_LOAD of the file
 
 */
 
int GetSavegameType(char *file)
 
{
 
	const SaveLoadFormat *fmt;
 
	uint32 hdr;
 
	FILE *f;
 
	int mode = SL_OLD_LOAD;
 

	
 
	f = fopen(file, "rb");
 
	if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
 
		DEBUG(sl, 0, "Savegame is obsolete or invalid format");
 
		mode = SL_LOAD; // don't try to get filename, just show name as it is written
 
	} else {
 
		// see if we have any loader for this type.
 
		for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) {
 
			if (fmt->tag == hdr) {
 
				mode = SL_LOAD; // new type of savegame
 
				break;
 
			}
 
		}
 
	}
 

	
 
	fclose(f);
 
	return mode;
 
}
 
#endif
src/screenshot.c
Show inline comments
 
deleted file
src/screenshot.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "strings.h"
 
#include "table/strings.h"
 
#include "gfx.h"
 
#include "hal.h"
 
#include "viewport.h"
 
#include "player.h"
 
#include "screenshot.h"
 
#include "variables.h"
 
#include "date.h"
 

	
 
char _screenshot_format_name[8];
 
uint _num_screenshot_formats;
 
uint _cur_screenshot_format;
 
ScreenshotType current_screenshot_type;
 

	
 
// called by the ScreenShot proc to generate screenshot lines.
 
typedef void ScreenshotCallback(void *userdata, Pixel *buf, uint y, uint pitch, uint n);
 
typedef bool ScreenshotHandlerProc(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette);
 

	
 
typedef struct {
 
	const char *name;
 
	const char *extension;
 
	ScreenshotHandlerProc *proc;
 
} ScreenshotFormat;
 

	
 
//************************************************
 
//*** SCREENSHOT CODE FOR WINDOWS BITMAP (.BMP)
 
//************************************************
 
#if defined(_MSC_VER) || defined(__WATCOMC__)
 
#pragma pack(push, 1)
 
#endif
 

	
 
typedef struct BitmapFileHeader {
 
	uint16 type;
 
	uint32 size;
 
	uint32 reserved;
 
	uint32 off_bits;
 
} GCC_PACK BitmapFileHeader;
 
assert_compile(sizeof(BitmapFileHeader) == 14);
 

	
 
#if defined(_MSC_VER) || defined(__WATCOMC__)
 
#pragma pack(pop)
 
#endif
 

	
 
typedef struct BitmapInfoHeader {
 
	uint32 size;
 
	int32 width, height;
 
	uint16 planes, bitcount;
 
	uint32 compression, sizeimage, xpels, ypels, clrused, clrimp;
 
} BitmapInfoHeader;
 
assert_compile(sizeof(BitmapInfoHeader) == 40);
 

	
 
typedef struct RgbQuad {
 
	byte blue, green, red, reserved;
 
} RgbQuad;
 
assert_compile(sizeof(RgbQuad) == 4);
 

	
 
// generic .BMP writer
 
static bool MakeBmpImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
 
{
 
	BitmapFileHeader bfh;
 
	BitmapInfoHeader bih;
 
	RgbQuad rq[256];
 
	Pixel *buff;
 
	FILE *f;
 
	uint i, padw;
 
	uint n, maxlines;
 

	
 
	// only implemented for 8bit images so far.
 
	if (pixelformat != 8)
 
		return false;
 

	
 
	f = fopen(name, "wb");
 
	if (f == NULL) return false;
 

	
 
	// each scanline must be aligned on a 32bit boundary
 
	padw = ALIGN(w, 4);
 

	
 
	// setup the file header
 
	bfh.type = TO_LE16('MB');
 
	bfh.size = TO_LE32(sizeof(bfh) + sizeof(bih) + sizeof(RgbQuad) * 256 + padw * h);
 
	bfh.reserved = 0;
 
	bfh.off_bits = TO_LE32(sizeof(bfh) + sizeof(bih) + sizeof(RgbQuad) * 256);
 

	
 
	// setup the info header
 
	bih.size = TO_LE32(sizeof(BitmapInfoHeader));
 
	bih.width = TO_LE32(w);
 
	bih.height = TO_LE32(h);
 
	bih.planes = TO_LE16(1);
 
	bih.bitcount = TO_LE16(8);
 
	bih.compression = 0;
 
	bih.sizeimage = 0;
 
	bih.xpels = 0;
 
	bih.ypels = 0;
 
	bih.clrused = 0;
 
	bih.clrimp = 0;
 

	
 
	// convert the palette to the windows format
 
	for (i = 0; i != 256; i++) {
 
		rq[i].red   = palette[i].r;
 
		rq[i].green = palette[i].g;
 
		rq[i].blue  = palette[i].b;
 
		rq[i].reserved = 0;
 
	}
 

	
 
	// write file header and info header and palette
 
	if (fwrite(&bfh, sizeof(bfh), 1, f) != 1) return false;
 
	if (fwrite(&bih, sizeof(bih), 1, f) != 1) return false;
 
	if (fwrite(rq, sizeof(rq), 1, f) != 1) return false;
 

	
 
	// use by default 64k temp memory
 
	maxlines = clamp(65536 / padw, 16, 128);
 

	
 
	// now generate the bitmap bits
 
	buff = malloc(padw * maxlines); // by default generate 128 lines at a time.
 
	if (buff == NULL) {
 
		fclose(f);
 
		return false;
 
	}
 
	memset(buff, 0, padw * maxlines); // zero the buffer to have the padding bytes set to 0
 

	
 
	// start at the bottom, since bitmaps are stored bottom up.
 
	do {
 
		// determine # lines
 
		n = min(h, maxlines);
 
		h -= n;
 

	
 
		// render the pixels
 
		callb(userdata, buff, h, padw, n);
 

	
 
		// write each line
 
		while (n)
 
			if (fwrite(buff + (--n) * padw, padw, 1, f) != 1) {
 
				free(buff);
 
				fclose(f);
 
				return false;
 
			}
 
	} while (h != 0);
 

	
 
	free(buff);
 
	fclose(f);
 

	
 
	return true;
 
}
 

	
 
//********************************************************
 
//*** SCREENSHOT CODE FOR PORTABLE NETWORK GRAPHICS (.PNG)
 
//********************************************************
 
#if defined(WITH_PNG)
 
#include <png.h>
 

	
 
static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message)
 
{
 
	DEBUG(misc, 0, "[libpng] error: %s - %s", message, (char *)png_get_error_ptr(png_ptr));
 
	longjmp(png_ptr->jmpbuf, 1);
 
}
 

	
 
static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message)
 
{
 
	DEBUG(misc, 1, "[libpng] warning: %s - %s", message, (char *)png_get_error_ptr(png_ptr));
 
}
 

	
 
static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
 
{
 
	png_color rq[256];
 
	Pixel *buff;
 
	FILE *f;
 
	uint i, y, n;
 
	uint maxlines;
 
	png_structp png_ptr;
 
	png_infop info_ptr;
 

	
 
	// only implemented for 8bit images so far.
 
	if (pixelformat != 8)
 
		return false;
 

	
 
	f = fopen(name, "wb");
 
	if (f == NULL) return false;
 

	
 
	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (char *)name, png_my_error, png_my_warning);
 

	
 
	if (png_ptr == NULL) {
 
		fclose(f);
 
		return false;
 
	}
 

	
 
	info_ptr = png_create_info_struct(png_ptr);
 
	if (info_ptr == NULL) {
 
		png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
 
		fclose(f);
 
		return false;
 
	}
 

	
 
	if (setjmp(png_jmpbuf(png_ptr))) {
 
		png_destroy_write_struct(&png_ptr, &info_ptr);
 
		fclose(f);
 
		return false;
 
	}
 

	
 
	png_init_io(png_ptr, f);
 

	
 
	png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
 

	
 
	png_set_IHDR(png_ptr, info_ptr, w, h, pixelformat, PNG_COLOR_TYPE_PALETTE,
 
		PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
 

	
 
	// convert the palette to the .PNG format.
 
	for (i = 0; i != 256; i++) {
 
		rq[i].red   = palette[i].r;
 
		rq[i].green = palette[i].g;
 
		rq[i].blue  = palette[i].b;
 
	}
 

	
 
	png_set_PLTE(png_ptr, info_ptr, rq, 256);
 
	png_write_info(png_ptr, info_ptr);
 
	png_set_flush(png_ptr, 512);
 

	
 
	// use by default 64k temp memory
 
	maxlines = clamp(65536 / w, 16, 128);
 

	
 
	// now generate the bitmap bits
 
	buff = malloc(w * maxlines); // by default generate 128 lines at a time.
 
	if (buff == NULL) {
 
		png_destroy_write_struct(&png_ptr, &info_ptr);
 
		fclose(f);
 
		return false;
 
	}
 
	memset(buff, 0, w * maxlines); // zero the buffer to have the padding bytes set to 0
 

	
 
	y = 0;
 
	do {
 
		// determine # lines to write
 
		n = min(h - y, maxlines);
 

	
 
		// render the pixels into the buffer
 
		callb(userdata, buff, y, w, n);
 
		y += n;
 

	
 
		// write them to png
 
		for (i = 0; i != n; i++)
 
			png_write_row(png_ptr, buff + i * w);
 
	} while (y != h);
 

	
 
	png_write_end(png_ptr, info_ptr);
 
	png_destroy_write_struct(&png_ptr, &info_ptr);
 

	
 
	free(buff);
 
	fclose(f);
 
	return true;
 
}
 
#endif /* WITH_PNG */
 

	
 

	
 
//************************************************
 
//*** SCREENSHOT CODE FOR ZSOFT PAINTBRUSH (.PCX)
 
//************************************************
 

	
 
typedef struct {
 
	byte manufacturer;
 
	byte version;
 
	byte rle;
 
	byte bpp;
 
	uint32 unused;
 
	uint16 xmax, ymax;
 
	uint16 hdpi, vdpi;
 
	byte pal_small[16*3];
 
	byte reserved;
 
	byte planes;
 
	uint16 pitch;
 
	uint16 cpal;
 
	uint16 width;
 
	uint16 height;
 
	byte filler[54];
 
} PcxHeader;
 
assert_compile(sizeof(PcxHeader) == 128);
 

	
 
static bool MakePCXImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
 
{
 
	Pixel *buff;
 
	FILE *f;
 
	uint maxlines;
 
	uint y;
 
	PcxHeader pcx;
 
	bool success;
 

	
 
	if (pixelformat != 8 || w == 0)
 
		return false;
 

	
 
	f = fopen(name, "wb");
 
	if (f == NULL) return false;
 

	
 
	memset(&pcx, 0, sizeof(pcx));
 

	
 
	// setup pcx header
 
	pcx.manufacturer = 10;
 
	pcx.version = 5;
 
	pcx.rle = 1;
 
	pcx.bpp = 8;
 
	pcx.xmax = TO_LE16(w - 1);
 
	pcx.ymax = TO_LE16(h - 1);
 
	pcx.hdpi = TO_LE16(320);
 
	pcx.vdpi = TO_LE16(320);
 

	
 
	pcx.planes = 1;
 
	pcx.cpal = TO_LE16(1);
 
	pcx.width = pcx.pitch = TO_LE16(w);
 
	pcx.height = TO_LE16(h);
 

	
 
	// write pcx header
 
	if (fwrite(&pcx, sizeof(pcx), 1, f) != 1) {
 
		fclose(f);
 
		return false;
 
	}
 

	
 
	// use by default 64k temp memory
 
	maxlines = clamp(65536 / w, 16, 128);
 

	
 
	// now generate the bitmap bits
 
	buff = malloc(w * maxlines); // by default generate 128 lines at a time.
 
	if (buff == NULL) {
 
		fclose(f);
 
		return false;
 
	}
 
	memset(buff, 0, w * maxlines); // zero the buffer to have the padding bytes set to 0
 

	
 
	y = 0;
 
	do {
 
		// determine # lines to write
 
		uint n = min(h - y, maxlines);
 
		uint i;
 

	
 
		// render the pixels into the buffer
 
		callb(userdata, buff, y, w, n);
 
		y += n;
 

	
 
		// write them to pcx
 
		for (i = 0; i != n; i++) {
 
			const Pixel* bufp = buff + i * w;
 
			byte runchar = bufp[0];
 
			uint runcount = 1;
 
			uint j;
 

	
 
			// for each pixel...
 
			for (j = 1; j < w; j++) {
 
				Pixel ch = bufp[j];
 

	
 
				if (ch != runchar || runcount >= 0x3f) {
 
					if (runcount > 1 || (runchar & 0xC0) == 0xC0)
 
						if (fputc(0xC0 | runcount, f) == EOF) {
 
							free(buff);
 
							fclose(f);
 
							return false;
 
						}
 
					if (fputc(runchar, f) == EOF) {
 
						free(buff);
 
						fclose(f);
 
						return false;
 
					}
 
					runcount = 0;
 
					runchar = ch;
 
				}
 
				runcount++;
 
			}
 

	
 
			// write remaining bytes..
 
			if (runcount > 1 || (runchar & 0xC0) == 0xC0)
 
				if (fputc(0xC0 | runcount, f) == EOF) {
 
					free(buff);
 
					fclose(f);
 
					return false;
 
				}
 
			if (fputc(runchar, f) == EOF) {
 
				free(buff);
 
				fclose(f);
 
				return false;
 
			}
 
		}
 
	} while (y != h);
 

	
 
	free(buff);
 

	
 
	// write 8-bit color palette
 
	if (fputc(12, f) == EOF) {
 
		fclose(f);
 
		return false;
 
	}
 

	
 
	if (sizeof(*palette) == 3) {
 
		success = fwrite(palette, 256 * sizeof(*palette), 1, f) == 1;
 
	} else {
 
		/* If the palette is word-aligned, copy it to a temporary byte array */
 
		byte tmp[256 * 3];
 
		uint i;
 

	
 
		for (i = 0; i < 256; i++) {
 
			tmp[i * 3 + 0] = palette[i].r;
 
			tmp[i * 3 + 1] = palette[i].g;
 
			tmp[i * 3 + 2] = palette[i].b;
 
		}
 
		success = fwrite(tmp, sizeof(tmp), 1, f) == 1;
 
	}
 

	
 
	fclose(f);
 

	
 
	return success;
 
}
 

	
 
//************************************************
 
//*** GENERIC SCREENSHOT CODE
 
//************************************************
 

	
 
static const ScreenshotFormat _screenshot_formats[] = {
 
#if defined(WITH_PNG)
 
	{"PNG", "png", &MakePNGImage},
 
#endif
 
	{"BMP", "bmp", &MakeBmpImage},
 
	{"PCX", "pcx", &MakePCXImage},
 
};
 

	
 
void InitializeScreenshotFormats(void)
 
{
 
	int i, j;
 
	for (i = 0, j = 0; i != lengthof(_screenshot_formats); i++)
 
		if (!strcmp(_screenshot_format_name, _screenshot_formats[i].extension)) {
 
			j = i;
 
			break;
 
		}
 
	_cur_screenshot_format = j;
 
	_num_screenshot_formats = lengthof(_screenshot_formats);
 
	current_screenshot_type = SC_NONE;
 
}
 

	
 
const char *GetScreenshotFormatDesc(int i)
 
{
 
	return _screenshot_formats[i].name;
 
}
 

	
 
void SetScreenshotFormat(int i)
 
{
 
	_cur_screenshot_format = i;
 
	strcpy(_screenshot_format_name, _screenshot_formats[i].extension);
 
}
 

	
 
// screenshot generator that dumps the current video buffer
 
static void CurrentScreenCallback(void *userdata, Pixel *buf, uint y, uint pitch, uint n)
 
{
 
	for (; n > 0; --n) {
 
		memcpy(buf, _screen.dst_ptr + y * _screen.pitch, _screen.width);
 
		++y;
 
		buf += pitch;
 
	}
 
}
 

	
 
// generate a large piece of the world
 
static void LargeWorldCallback(void *userdata, Pixel *buf, uint y, uint pitch, uint n)
 
{
 
	ViewPort *vp = (ViewPort *)userdata;
 
	DrawPixelInfo dpi, *old_dpi;
 
	int wx, left;
 

	
 
	old_dpi = _cur_dpi;
 
	_cur_dpi = &dpi;
 

	
 
	dpi.dst_ptr = buf;
 
	dpi.height = n;
 
	dpi.width = vp->width;
 
	dpi.pitch = pitch;
 
	dpi.zoom = 0;
 
	dpi.left = 0;
 
	dpi.top = y;
 

	
 
	left = 0;
 
	while (vp->width - left != 0) {
 
		wx = min(vp->width - left, 1600);
 
		left += wx;
 

	
 
		ViewportDoDraw(vp,
 
			((left - wx - vp->left) << vp->zoom) + vp->virtual_left,
 
			((y - vp->top) << vp->zoom) + vp->virtual_top,
 
			((left - vp->left) << vp->zoom) + vp->virtual_left,
 
			(((y + n) - vp->top) << vp->zoom) + vp->virtual_top
 
		);
 
	}
 

	
 
	_cur_dpi = old_dpi;
 
}
 

	
 
static char *MakeScreenshotName(const char *ext)
 
{
 
	static char filename[256];
 
	char *base;
 
	int serial;
 

	
 
	if (_game_mode == GM_EDITOR || _game_mode == GM_MENU || _local_player == PLAYER_SPECTATOR) {
 
		sprintf(_screenshot_name, "screenshot");
 
	} else {
 
		const Player* p = GetPlayer(_local_player);
 
		SetDParam(0, p->name_1);
 
		SetDParam(1, p->name_2);
 
		SetDParam(2, _date);
 
		GetString(_screenshot_name, STR_4004, lastof(_screenshot_name));
 
	}
 

	
 
	base = strchr(_screenshot_name, 0);
 
	base[0] = '.'; strcpy(base + 1, ext);
 

	
 
	serial = 0;
 
	for (;;) {
 
		snprintf(filename, sizeof(filename), "%s%s", _paths.personal_dir, _screenshot_name);
 
		if (!FileExists(filename))
 
			break;
 
		sprintf(base, " #%d.%s", ++serial, ext);
 
	}
 

	
 
	return filename;
 
}
 

	
 
void SetScreenshotType(ScreenshotType t)
 
{
 
	current_screenshot_type = t;
 
}
 

	
 
bool IsScreenshotRequested(void)
 
{
 
	return (current_screenshot_type != SC_NONE);
 
}
 

	
 
static bool MakeSmallScreenshot(void)
 
{
 
	const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format;
 
	return sf->proc(MakeScreenshotName(sf->extension), CurrentScreenCallback, NULL, _screen.width, _screen.height, 8, _cur_palette);
 
}
 

	
 
static bool MakeWorldScreenshot(void)
 
{
 
	ViewPort vp;
 
	const ScreenshotFormat *sf;
 

	
 
	vp.zoom = 0;
 
	vp.left = 0;
 
	vp.top = 0;
 
	vp.virtual_left = -(int)MapMaxX() * TILE_PIXELS;
 
	vp.virtual_top = 0;
 
	vp.virtual_width = (MapMaxX() + MapMaxY()) * TILE_PIXELS;
 
	vp.width = vp.virtual_width;
 
	vp.virtual_height = (MapMaxX() + MapMaxY()) * TILE_PIXELS >> 1;
 
	vp.height = vp.virtual_height;
 

	
 
	sf = _screenshot_formats + _cur_screenshot_format;
 
	return sf->proc(MakeScreenshotName(sf->extension), LargeWorldCallback, &vp, vp.width, vp.height, 8, _cur_palette);
 
}
 

	
 
bool MakeScreenshot(void)
 
{
 
	switch (current_screenshot_type) {
 
		case SC_VIEWPORT:
 
			UndrawMouseCursor();
 
			DrawDirtyBlocks();
 
			current_screenshot_type = SC_NONE;
 
			return MakeSmallScreenshot();
 
		case SC_WORLD:
 
			current_screenshot_type = SC_NONE;
 
			return MakeWorldScreenshot();
 
		default: return false;
 
	}
 
}
 

	
 

	
 

	
src/sdl.c
Show inline comments
 
deleted file
src/sdl.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 

	
 
#ifdef WITH_SDL
 

	
 
#include "openttd.h"
 
#include "sdl.h"
 
#include <SDL.h>
 

	
 
#ifdef UNIX
 
#include <signal.h>
 

	
 
#ifdef __MORPHOS__
 
	// The system supplied definition of SIG_DFL is wrong on MorphOS
 
	#undef SIG_DFL
 
	#define SIG_DFL (void (*)(int))0
 
#endif
 
#endif
 

	
 
static int _sdl_usage;
 

	
 
#ifdef DYNAMICALLY_LOADED_SDL
 

	
 
#include "win32.h"
 

	
 
#define M(x) x "\0"
 
static const char sdl_files[] =
 
	M("sdl.dll")
 
	M("SDL_Init")
 
	M("SDL_InitSubSystem")
 
	M("SDL_GetError")
 
	M("SDL_QuitSubSystem")
 
	M("SDL_UpdateRect")
 
	M("SDL_UpdateRects")
 
	M("SDL_SetColors")
 
	M("SDL_WM_SetCaption")
 
	M("SDL_ShowCursor")
 
	M("SDL_FreeSurface")
 
	M("SDL_PollEvent")
 
	M("SDL_WarpMouse")
 
	M("SDL_GetTicks")
 
	M("SDL_OpenAudio")
 
	M("SDL_PauseAudio")
 
	M("SDL_CloseAudio")
 
	M("SDL_LockSurface")
 
	M("SDL_UnlockSurface")
 
	M("SDL_GetModState")
 
	M("SDL_Delay")
 
	M("SDL_Quit")
 
	M("SDL_SetVideoMode")
 
	M("SDL_EnableKeyRepeat")
 
	M("SDL_EnableUNICODE")
 
	M("SDL_VideoDriverName")
 
	M("SDL_ListModes")
 
	M("SDL_GetKeyState")
 
	M("SDL_LoadBMP_RW")
 
	M("SDL_RWFromFile")
 
	M("SDL_SetColorKey")
 
	M("SDL_WM_SetIcon")
 
	M("SDL_MapRGB")
 
	M("")
 
;
 
#undef M
 

	
 
SDLProcs sdl_proc;
 

	
 
static const char *LoadSdlDLL(void)
 
{
 
	if (sdl_proc.SDL_Init != NULL)
 
		return NULL;
 
	if (!LoadLibraryList((Function *)(void *)&sdl_proc, sdl_files))
 
		return "Unable to load sdl.dll";
 
	return NULL;
 
}
 

	
 
#endif // DYNAMICALLY_LOADED_SDL
 

	
 

	
 
#ifdef UNIX
 
static void SdlAbort(int sig)
 
{
 
	/* Own hand-made parachute for the cases of failed assertions. */
 
	SDL_CALL SDL_Quit();
 

	
 
	switch (sig) {
 
		case SIGSEGV:
 
		case SIGFPE:
 
			signal(sig, SIG_DFL);
 
			raise(sig);
 
			break;
 

	
 
		default:
 
			break;
 
	}
 
}
 
#endif
 

	
 

	
 
const char* SdlOpen(uint32 x)
 
{
 
#ifdef DYNAMICALLY_LOADED_SDL
 
	{
 
		const char *s = LoadSdlDLL();
 
		if (s != NULL) return s;
 
	}
 
#endif
 
	if (_sdl_usage++ == 0) {
 
		if (SDL_CALL SDL_Init(x) == -1)
 
			return SDL_CALL SDL_GetError();
 
	} else if (x != 0) {
 
		if (SDL_CALL SDL_InitSubSystem(x) == -1)
 
			return SDL_CALL SDL_GetError();
 
	}
 

	
 
#ifdef UNIX
 
	signal(SIGABRT, SdlAbort);
 
	signal(SIGSEGV, SdlAbort);
 
	signal(SIGFPE, SdlAbort);
 
#endif
 

	
 
	return NULL;
 
}
 

	
 
void SdlClose(uint32 x)
 
{
 
	if (x != 0)
 
		SDL_CALL SDL_QuitSubSystem(x);
 
	if (--_sdl_usage == 0) {
 
		SDL_CALL SDL_Quit();
 
		#ifdef UNIX
 
		signal(SIGABRT, SIG_DFL);
 
		signal(SIGSEGV, SIG_DFL);
 
		signal(SIGFPE, SIG_DFL);
 
		#endif
 
	}
 
}
 

	
 
#endif
src/settings.c
Show inline comments
 
deleted file
src/settings.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/** @file
 
 * All actions handling saving and loading of the settings/configuration goes on in this file.
 
 * The file consists of four parts:
 
 * <ol>
 
 * <li>Parsing the configuration file (openttd.cfg). This is achieved with the ini_ functions which
 
 *     handle various types, such as normal 'key = value' pairs, lists and value combinations of
 
 *     lists, strings, integers, 'bit'-masks and element selections.
 
 * <li>Defining the data structures that go into the configuration. These include for example
 
 *     the _patches struct, but also network-settings, banlists, newgrf, etc. There are a lot
 
 *     of helper macros available for the various types, and also saving/loading of these settings
 
 *     in a savegame is handled inside these structures.
 
 * <li>Handle reading and writing to the setting-structures from inside the game either from
 
 *     the console for example or through the gui with CMD_ functions.
 
 * <li>Handle saving/loading of the PATS chunk inside the savegame.
 
 * </ol>
 
 * @see SettingDesc
 
 * @see SaveLoad
 
 */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "currency.h"
 
#include "functions.h"
 
#include "macros.h"
 
#include "screenshot.h"
 
#include "sound.h"
 
#include "string.h"
 
#include "variables.h"
 
#include "network/network.h"
 
#include "settings.h"
 
#include "command.h"
 
#include "console.h"
 
#include "saveload.h"
 
#include "npf.h"
 
#include "yapf/yapf.h"
 
#include "newgrf.h"
 
#include "newgrf_config.h"
 
#include "genworld.h"
 
#include "date.h"
 
#include "rail.h"
 
#ifdef WITH_FREETYPE
 
#include "gfx.h"
 
#include "fontcache.h"
 
#endif
 

	
 
/** The patch values that are used for new games and/or modified in config file */
 
Patches _patches_newgame;
 

	
 
typedef struct IniFile IniFile;
 
typedef struct IniItem IniItem;
 
typedef struct IniGroup IniGroup;
 
typedef struct SettingsMemoryPool SettingsMemoryPool;
 

	
 
typedef const char *SettingListCallbackProc(const IniItem *item, uint index);
 
typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const char *grpname, void *object);
 
typedef void SettingDescProcList(IniFile *ini, const char *grpname, char **list, uint len, SettingListCallbackProc proc);
 

	
 
static void pool_init(SettingsMemoryPool **pool);
 
static void *pool_alloc(SettingsMemoryPool **pool, uint size);
 
static void *pool_strdup(SettingsMemoryPool **pool, const char *mem, uint size);
 
static void pool_free(SettingsMemoryPool **pool);
 
static bool IsSignedVarMemType(VarType vt);
 

	
 
struct SettingsMemoryPool {
 
	uint pos,size;
 
	SettingsMemoryPool *next;
 
	byte mem[1];
 
};
 

	
 
static SettingsMemoryPool *pool_new(uint minsize)
 
{
 
	SettingsMemoryPool *p;
 
	if (minsize < 4096 - 12) minsize = 4096 - 12;
 

	
 
	p = malloc(sizeof(SettingsMemoryPool) - 1 + minsize);
 
	p->pos = 0;
 
	p->size = minsize;
 
	p->next = NULL;
 
	return p;
 
}
 

	
 
static void pool_init(SettingsMemoryPool **pool)
 
{
 
	*pool = pool_new(0);
 
}
 

	
 
static void *pool_alloc(SettingsMemoryPool **pool, uint size)
 
{
 
	uint pos;
 
	SettingsMemoryPool *p = *pool;
 

	
 
	size = ALIGN(size, sizeof(void*));
 

	
 
	// first check if there's memory in the next pool
 
	if (p->next && p->next->pos + size <= p->next->size) {
 
		p = p->next;
 
	// then check if there's not memory in the cur pool
 
	} else if (p->pos + size > p->size) {
 
		SettingsMemoryPool *n = pool_new(size);
 
		*pool = n;
 
		n->next = p;
 
		p = n;
 
	}
 

	
 
	pos = p->pos;
 
	p->pos += size;
 
	return p->mem + pos;
 
}
 

	
 
static void *pool_strdup(SettingsMemoryPool **pool, const char *mem, uint size)
 
{
 
	byte *p = pool_alloc(pool, size + 1);
 
	p[size] = 0;
 
	memcpy(p, mem, size);
 
	return p;
 
}
 

	
 
static void pool_free(SettingsMemoryPool **pool)
 
{
 
	SettingsMemoryPool *p = *pool, *n;
 
	*pool = NULL;
 
	while (p) {
 
		n = p->next;
 
		free(p);
 
		p = n;
 
	}
 
}
 

	
 
// structs describing the ini format.
 
struct IniItem {
 
	char *name;
 
	char *value;
 
	char *comment;
 
	IniItem *next;
 
};
 

	
 
struct IniGroup {
 
	char *name; // name of group
 
	char *comment; //comment for group
 
	IniItem *item, **last_item;
 
	IniGroup *next;
 
	IniFile *ini;
 
	IniGroupType type; // type of group
 
};
 

	
 
struct IniFile {
 
	SettingsMemoryPool *pool;
 
	IniGroup *group, **last_group;
 
	char *comment; // last comment in file
 
};
 

	
 
// allocate an inifile object
 
static IniFile *ini_alloc(void)
 
{
 
	IniFile *ini;
 
	SettingsMemoryPool *pool;
 
	pool_init(&pool);
 
	ini = (IniFile*)pool_alloc(&pool, sizeof(IniFile));
 
	ini->pool = pool;
 
	ini->group = NULL;
 
	ini->last_group = &ini->group;
 
	ini->comment = NULL;
 
	return ini;
 
}
 

	
 
// allocate an ini group object
 
static IniGroup *ini_group_alloc(IniFile *ini, const char *grpt, int len)
 
{
 
	IniGroup *grp = pool_alloc(&ini->pool, sizeof(IniGroup));
 
	grp->ini = ini;
 
	grp->name = pool_strdup(&ini->pool, grpt, len);
 
	if (!strcmp(grp->name, "newgrf") || !strcmp(grp->name, "servers") || !strcmp(grp->name, "bans")) {
 
		grp->type = IGT_LIST;
 
	} else {
 
		grp->type = IGT_VARIABLES;
 
	}
 
	grp->next = NULL;
 
	grp->item = NULL;
 
	grp->comment = NULL;
 
	grp->last_item = &grp->item;
 
	*ini->last_group = grp;
 
	ini->last_group = &grp->next;
 
	return grp;
 
}
 

	
 
static IniItem *ini_item_alloc(IniGroup *group, const char *name, int len)
 
{
 
	IniItem *item = pool_alloc(&group->ini->pool, sizeof(IniItem));
 
	item->name = pool_strdup(&group->ini->pool, name, len);
 
	item->next = NULL;
 
	item->comment = NULL;
 
	item->value = NULL;
 
	*group->last_item = item;
 
	group->last_item = &item->next;
 
	return item;
 
}
 

	
 
// load an ini file into the "abstract" format
 
static IniFile *ini_load(const char *filename)
 
{
 
	char buffer[1024], c, *s, *t, *e;
 
	FILE *in;
 
	IniFile *ini;
 
	IniGroup *group = NULL;
 
	IniItem *item;
 

	
 
	char *comment = NULL;
 
	uint comment_size = 0;
 
	uint comment_alloc = 0;
 

	
 
	ini = ini_alloc();
 

	
 
	in = fopen(filename, "r");
 
	if (in == NULL) return ini;
 

	
 
	// for each line in the file
 
	while (fgets(buffer, sizeof(buffer), in)) {
 

	
 
		// trim whitespace from the left side
 
		for (s = buffer; *s == ' ' || *s == '\t'; s++);
 

	
 
		// trim whitespace from right side.
 
		e = s + strlen(s);
 
		while (e > s && ((c=e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
 
		*e = '\0';
 

	
 
		// skip comments and empty lines
 
		if (*s == '#' || *s == ';' || *s == '\0') {
 
			uint ns = comment_size + (e - s + 1);
 
			uint a = comment_alloc;
 
			uint pos;
 
			// add to comment
 
			if (ns > a) {
 
				a = max(a, 128);
 
				do a*=2; while (a < ns);
 
				comment = realloc(comment, comment_alloc = a);
 
			}
 
			pos = comment_size;
 
			comment_size += (e - s + 1);
 
			comment[pos + e - s] = '\n'; // comment newline
 
			memcpy(comment + pos, s, e - s); // copy comment contents
 
			continue;
 
		}
 

	
 
		// it's a group?
 
		if (s[0] == '[') {
 
			if (e[-1] != ']') {
 
				ShowInfoF("ini: invalid group name '%s'", buffer);
 
			} else {
 
				e--;
 
			}
 
			s++; // skip [
 
			group = ini_group_alloc(ini, s, e - s);
 
			if (comment_size) {
 
				group->comment = pool_strdup(&ini->pool, comment, comment_size);
 
				comment_size = 0;
 
			}
 
		} else if (group) {
 
			// find end of keyname
 
			for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++);
 

	
 
			// it's an item in an existing group
 
			item = ini_item_alloc(group, s, t-s);
 
			if (comment_size) {
 
				item->comment = pool_strdup(&ini->pool, comment, comment_size);
 
				comment_size = 0;
 
			}
 

	
 
			// find start of parameter
 
			while (*t == '=' || *t == ' ' || *t == '\t') t++;
 

	
 

	
 
			// remove starting quotation marks
 
			if (*t == '\"') t++;
 
			// remove ending quotation marks
 
			e = t + strlen(t);
 
			if (e > t && e[-1] == '\"') e--;
 
			*e = '\0';
 

	
 
			item->value = pool_strdup(&ini->pool, t, e - t);
 
		} else {
 
			// it's an orphan item
 
			ShowInfoF("ini: '%s' outside of group", buffer);
 
		}
 
	}
 

	
 
	if (comment_size > 0) {
 
		ini->comment = pool_strdup(&ini->pool, comment, comment_size);
 
		comment_size = 0;
 
	}
 

	
 
	free(comment);
 
	fclose(in);
 

	
 
	return ini;
 
}
 

	
 
// lookup a group or make a new one
 
static IniGroup *ini_getgroup(IniFile *ini, const char *name, int len)
 
{
 
	IniGroup *group;
 

	
 
	if (len == -1) len = strlen(name);
 

	
 
	// does it exist already?
 
	for (group = ini->group; group; group = group->next)
 
		if (!memcmp(group->name, name, len) && group->name[len] == 0)
 
			return group;
 

	
 
	// otherwise make a new one
 
	group = ini_group_alloc(ini, name, len);
 
	group->comment = pool_strdup(&ini->pool, "\n", 1);
 
	return group;
 
}
 

	
 
// lookup an item or make a new one
 
static IniItem *ini_getitem(IniGroup *group, const char *name, bool create)
 
{
 
	IniItem *item;
 
	uint len = strlen(name);
 

	
 
	for (item = group->item; item; item = item->next)
 
		if (strcmp(item->name, name) == 0) return item;
 

	
 
	if (!create) return NULL;
 

	
 
	// otherwise make a new one
 
	return ini_item_alloc(group, name, len);
 
}
 

	
 
// save ini file from the "abstract" format.
 
static bool ini_save(const char *filename, IniFile *ini)
 
{
 
	FILE *f;
 
	IniGroup *group;
 
	IniItem *item;
 

	
 
	f = fopen(filename, "w");
 
	if (f == NULL) return false;
 

	
 
	for (group = ini->group; group != NULL; group = group->next) {
 
		if (group->comment) fputs(group->comment, f);
 
		fprintf(f, "[%s]\n", group->name);
 
		for (item = group->item; item != NULL; item = item->next) {
 
			assert(item->value != NULL);
 
			if (item->comment != NULL) fputs(item->comment, f);
 

	
 
			/* Don't give an equal sign to list items that don't have a parameter */
 
			if (group->type == IGT_LIST && *item->value == '\0') {
 
				fprintf(f, "%s\n", item->name);
 
			} else {
 
				fprintf(f, "%s = %s\n", item->name, item->value);
 
			}
 
		}
 
	}
 
	if (ini->comment) fputs(ini->comment, f);
 

	
 
	fclose(f);
 
	return true;
 
}
 

	
 
static void ini_free(IniFile *ini)
 
{
 
	pool_free(&ini->pool);
 
}
 

	
 
/** Find the index value of a ONEofMANY type in a string seperated by |
 
 * @param many full domain of values the ONEofMANY setting can have
 
 * @param one the current value of the setting for which a value needs found
 
 * @param onelen force calculation of the *one parameter
 
 * @return the integer index of the full-list, or -1 if not found */
 
static int lookup_oneofmany(const char *many, const char *one, int onelen)
 
{
 
	const char *s;
 
	int idx;
 

	
 
	if (onelen == -1) onelen = strlen(one);
 

	
 
	// check if it's an integer
 
	if (*one >= '0' && *one <= '9')
 
		return strtoul(one, NULL, 0);
 

	
 
	idx = 0;
 
	for (;;) {
 
		// find end of item
 
		s = many;
 
		while (*s != '|' && *s != 0) s++;
 
		if (s - many == onelen && !memcmp(one, many, onelen)) return idx;
 
		if (*s == 0) return -1;
 
		many = s + 1;
 
		idx++;
 
	}
 
}
 

	
 
/** Find the set-integer value MANYofMANY type in a string
 
 * @param many full domain of values the MANYofMANY setting can have
 
 * @param str the current string value of the setting, each individual
 
 * of seperated by a whitespace\tab or | character
 
 * @return the 'fully' set integer, or -1 if a set is not found */
 
static uint32 lookup_manyofmany(const char *many, const char *str)
 
{
 
	const char *s;
 
	int r;
 
	uint32 res = 0;
 

	
 
	for (;;) {
 
		// skip "whitespace"
 
		while (*str == ' ' || *str == '\t' || *str == '|') str++;
 
		if (*str == 0) break;
 

	
 
		s = str;
 
		while (*s != 0 && *s != ' ' && *s != '\t' && *s != '|') s++;
 

	
 
		r = lookup_oneofmany(many, str, s - str);
 
		if (r == -1) return (uint32)-1;
 

	
 
		SETBIT(res, r); // value found, set it
 
		if (*s == 0) break;
 
		str = s + 1;
 
	}
 
	return res;
 
}
 

	
 
/** Parse an integerlist string and set each found value
 
 * @param p the string to be parsed. Each element in the list is seperated by a
 
 * comma or a space character
 
 * @param items pointer to the integerlist-array that will be filled with values
 
 * @param maxitems the maximum number of elements the integerlist-array has
 
 * @return returns the number of items found, or -1 on an error */
 
static int parse_intlist(const char *p, int *items, int maxitems)
 
{
 
	int n = 0, v;
 
	char *end;
 

	
 
	for (;;) {
 
		v = strtol(p, &end, 0);
 
		if (p == end || n == maxitems) return -1;
 
		p = end;
 
		items[n++] = v;
 
		if (*p == '\0') break;
 
		if (*p != ',' && *p != ' ') return -1;
 
		p++;
 
	}
 

	
 
	return n;
 
}
 

	
 
/** Load parsed string-values into an integer-array (intlist)
 
 * @param str the string that contains the values (and will be parsed)
 
 * @param array pointer to the integer-arrays that will be filled
 
 * @param nelems the number of elements the array holds. Maximum is 64 elements
 
 * @param type the type of elements the array holds (eg INT8, UINT16, etc.)
 
 * @return return true on success and false on error */
 
static bool load_intlist(const char *str, void *array, int nelems, VarType type)
 
{
 
	int items[64];
 
	int i, nitems;
 

	
 
	if (str == NULL) {
 
		memset(items, 0, sizeof(items));
 
		nitems = nelems;
 
	} else {
 
		nitems = parse_intlist(str, items, lengthof(items));
 
		if (nitems != nelems) return false;
 
	}
 

	
 
	switch (type) {
 
	case SLE_VAR_BL:
 
	case SLE_VAR_I8:
 
	case SLE_VAR_U8:
 
		for (i = 0; i != nitems; i++) ((byte*)array)[i] = items[i];
 
		break;
 
	case SLE_VAR_I16:
 
	case SLE_VAR_U16:
 
		for (i = 0; i != nitems; i++) ((uint16*)array)[i] = items[i];
 
		break;
 
	case SLE_VAR_I32:
 
	case SLE_VAR_U32:
 
		for (i = 0; i != nitems; i++) ((uint32*)array)[i] = items[i];
 
		break;
 
	default: NOT_REACHED();
 
	}
 

	
 
	return true;
 
}
 

	
 
/** Convert an integer-array (intlist) to a string representation. Each value
 
 * is seperated by a comma or a space character
 
 * @param buf output buffer where the string-representation will be stored
 
 * @param array pointer to the integer-arrays that is read from
 
 * @param nelems the number of elements the array holds.
 
 * @param type the type of elements the array holds (eg INT8, UINT16, etc.) */
 
static void make_intlist(char *buf, const void *array, int nelems, VarType type)
 
{
 
	int i, v = 0;
 
	const byte *p = (const byte*)array;
 

	
 
	for (i = 0; i != nelems; i++) {
 
		switch (type) {
 
		case SLE_VAR_BL:
 
		case SLE_VAR_I8:  v = *(int8*)p;   p += 1; break;
 
		case SLE_VAR_U8:  v = *(byte*)p;   p += 1; break;
 
		case SLE_VAR_I16: v = *(int16*)p;  p += 2; break;
 
		case SLE_VAR_U16: v = *(uint16*)p; p += 2; break;
 
		case SLE_VAR_I32: v = *(int32*)p;  p += 4; break;
 
		case SLE_VAR_U32: v = *(uint32*)p; p += 4; break;
 
		default: NOT_REACHED();
 
		}
 
		buf += sprintf(buf, (i == 0) ? "%d" : ",%d", v);
 
	}
 
}
 

	
 
/** Convert a ONEofMANY structure to a string representation.
 
 * @param buf output buffer where the string-representation will be stored
 
 * @param many the full-domain string of possible values
 
 * @param id the value of the variable and whose string-representation must be found */
 
static void make_oneofmany(char *buf, const char *many, int id)
 
{
 
	int orig_id = id;
 

	
 
	// Look for the id'th element
 
	while (--id >= 0) {
 
		for (; *many != '|'; many++) {
 
			if (*many == '\0') { // not found
 
				sprintf(buf, "%d", orig_id);
 
				return;
 
			}
 
		}
 
		many++; // pass the |-character
 
	}
 

	
 
	// copy string until next item (|) or the end of the list if this is the last one
 
	while (*many != '\0' && *many != '|') *buf++ = *many++;
 
	*buf = '\0';
 
}
 

	
 
/** Convert a MANYofMANY structure to a string representation.
 
 * @param buf output buffer where the string-representation will be stored
 
 * @param many the full-domain string of possible values
 
 * @param x the value of the variable and whose string-representation must
 
 *        be found in the bitmasked many string */
 
static void make_manyofmany(char *buf, const char *many, uint32 x)
 
{
 
	const char *start;
 
	int i = 0;
 
	bool init = true;
 

	
 
	for (; x != 0; x >>= 1, i++) {
 
		start = many;
 
		while (*many != 0 && *many != '|') many++; // advance to the next element
 

	
 
		if (HASBIT(x, 0)) { // item found, copy it
 
			if (!init) *buf++ = '|';
 
			init = false;
 
			if (start == many) {
 
				buf += sprintf(buf, "%d", i);
 
			} else {
 
				memcpy(buf, start, many - start);
 
				buf += many - start;
 
			}
 
		}
 

	
 
		if (*many == '|') many++;
 
	}
 

	
 
	*buf = '\0';
 
}
 

	
 
/** Convert a string representation (external) of a setting to the internal rep.
 
 * @param desc SettingDesc struct that holds all information about the variable
 
 * @param str input string that will be parsed based on the type of desc
 
 * @return return the parsed value of the setting */
 
static const void *string_to_val(const SettingDescBase *desc, const char *str)
 
{
 
	switch (desc->cmd) {
 
	case SDT_NUMX: {
 
		char *end;
 
		unsigned long val = strtoul(str, &end, 0);
 
		if (*end != '\0') ShowInfoF("ini: trailing characters at end of setting '%s'", desc->name);
 
		return (void*)val;
 
	}
 
	case SDT_ONEOFMANY: {
 
		long r = lookup_oneofmany(desc->many, str, -1);
 
		if (r != -1) return (void*)r;
 
		ShowInfoF("ini: invalid value '%s' for '%s'", str, desc->name);
 
		return 0;
 
	}
 
	case SDT_MANYOFMANY: {
 
		unsigned long r = lookup_manyofmany(desc->many, str);
 
		if (r != (unsigned long)-1) return (void*)r;
 
		ShowInfoF("ini: invalid value '%s' for '%s'", str, desc->name);
 
		return 0;
 
	}
 
	case SDT_BOOLX:
 
		if (strcmp(str, "true")  == 0 || strcmp(str, "on")  == 0 || strcmp(str, "1") == 0)
 
			return (void*)true;
 
		if (strcmp(str, "false") == 0 || strcmp(str, "off") == 0 || strcmp(str, "0") == 0)
 
			return (void*)false;
 
		ShowInfoF("ini: invalid setting value '%s' for '%s'", str, desc->name);
 
		break;
 

	
 
	case SDT_STRING:
 
	case SDT_INTLIST: return str;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/** Set the value of a setting and if needed clamp the value to
 
 * the preset minimum and maximum.
 
 * @param ptr the variable itself
 
 * @param sd pointer to the 'information'-database of the variable
 
 * @param val signed long version of the new value
 
 * @pre SettingDesc is of type SDT_BOOLX, SDT_NUMX,
 
 * SDT_ONEOFMANY or SDT_MANYOFMANY. Other types are not supported as of now */
 
static void Write_ValidateSetting(void *ptr, const SettingDesc *sd, int32 val)
 
{
 
	const SettingDescBase *sdb = &sd->desc;
 

	
 
	if (sdb->cmd != SDT_BOOLX &&
 
			sdb->cmd != SDT_NUMX &&
 
			sdb->cmd != SDT_ONEOFMANY &&
 
			sdb->cmd != SDT_MANYOFMANY) {
 
		return;
 
	}
 

	
 
	/* We cannot know the maximum value of a bitset variable, so just have faith */
 
	if (sdb->cmd != SDT_MANYOFMANY) {
 
		/* We need to take special care of the uint32 type as we receive from the function
 
		 * a signed integer. While here also bail out on 64-bit settings as those are not
 
		 * supported. Unsigned 8 and 16-bit variables are safe since they fit into a signed
 
		 * 32-bit variable
 
		 * TODO: Support 64-bit settings/variables */
 
		switch (GetVarMemType(sd->save.conv)) {
 
			case SLE_VAR_BL:
 
			case SLE_VAR_I8:
 
			case SLE_VAR_U8:
 
			case SLE_VAR_I16:
 
			case SLE_VAR_U16:
 
			case SLE_VAR_I32: {
 
				/* Override the minimum value. No value below sdb->min, except special value 0 */
 
				int32 min = ((sdb->flags & SGF_0ISDISABLED) && val <= sdb->min) ? 0 : sdb->min;
 
				val = clamp(val, min, sdb->max);
 
			} break;
 
			case SLE_VAR_U32: {
 
				/* Override the minimum value. No value below sdb->min, except special value 0 */
 
				uint min = ((sdb->flags & SGF_0ISDISABLED) && (uint)val <= (uint)sdb->min) ? 0 : sdb->min;
 
				WriteValue(ptr, SLE_VAR_U32, (int64)clampu(val, min, sdb->max));
 
				return;
 
			}
 
			case SLE_VAR_I64:
 
			case SLE_VAR_U64:
 
			default: NOT_REACHED(); break;
 
		}
 
	}
 

	
 
	WriteValue(ptr, sd->save.conv, (int64)val);
 
}
 

	
 
/** Load values from a group of an IniFile structure into the internal representation
 
 * @param ini pointer to IniFile structure that holds administrative information
 
 * @param sd pointer to SettingDesc structure whose internally pointed variables will
 
 *        be given values
 
 * @param grpname the group of the IniFile to search in for the new values */
 
static void ini_load_settings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object)
 
{
 
	IniGroup *group;
 
	IniGroup *group_def = ini_getgroup(ini, grpname, -1);
 
	IniItem *item;
 
	const void *p;
 
	void *ptr;
 
	const char *s;
 

	
 
	for (; sd->save.cmd != SL_END; sd++) {
 
		const SettingDescBase *sdb = &sd->desc;
 
		const SaveLoad        *sld = &sd->save;
 

	
 
		if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to)) continue;
 

	
 
		// XXX - wtf is this?? (group override?)
 
		s = strchr(sdb->name, '.');
 
		if (s != NULL) {
 
			group = ini_getgroup(ini, sdb->name, s - sdb->name);
 
			s++;
 
		} else {
 
			s = sdb->name;
 
			group = group_def;
 
		}
 

	
 
		item = ini_getitem(group, s, false);
 
		p = (item == NULL) ? sdb->def : string_to_val(sdb, item->value);
 
		ptr = GetVariableAddress(object, sld);
 

	
 
		switch (sdb->cmd) {
 
		case SDT_BOOLX: /* All four are various types of (integer) numbers */
 
		case SDT_NUMX:
 
		case SDT_ONEOFMANY:
 
		case SDT_MANYOFMANY:
 
			Write_ValidateSetting(ptr, sd, (unsigned long)p); break;
 

	
 
		case SDT_STRING:
 
			switch (GetVarMemType(sld->conv)) {
 
				case SLE_VAR_STRB:
 
				case SLE_VAR_STRBQ:
 
					if (p != NULL) ttd_strlcpy((char*)ptr, p, sld->length);
 
					break;
 
				case SLE_VAR_STR:
 
				case SLE_VAR_STRQ:
 
					if (p != NULL) {
 
						free(*(char**)ptr);
 
						*(char**)ptr = strdup((const char*)p);
 
					}
 
					break;
 
				case SLE_VAR_CHAR: *(char*)ptr = *(char*)p; break;
 
				default: NOT_REACHED(); break;
 
			}
 
			break;
 

	
 
		case SDT_INTLIST: {
 
			if (!load_intlist(p, ptr, sld->length, GetVarMemType(sld->conv)))
 
				ShowInfoF("ini: error in array '%s'", sdb->name);
 
			break;
 
		}
 
		default: NOT_REACHED(); break;
 
		}
 
	}
 
}
 

	
 
/** Save the values of settings to the inifile.
 
 * @param ini pointer to IniFile structure
 
 * @param sd read-only SettingDesc structure which contains the unmodified,
 
 *        loaded values of the configuration file and various information about it
 
 * @param grpname holds the name of the group (eg. [network]) where these will be saved
 
 * The function works as follows: for each item in the SettingDesc structure we
 
 * have a look if the value has changed since we started the game (the original
 
 * values are reloaded when saving). If settings indeed have changed, we get
 
 * these and save them.
 
 */
 
static void ini_save_settings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object)
 
{
 
	IniGroup *group_def = NULL, *group;
 
	IniItem *item;
 
	char buf[512];
 
	const char *s;
 
	void *ptr;
 

	
 
	for (; sd->save.cmd != SL_END; sd++) {
 
		const SettingDescBase *sdb = &sd->desc;
 
		const SaveLoad        *sld = &sd->save;
 

	
 
		/* If the setting is not saved to the configuration
 
		 * file, just continue with the next setting */
 
		if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to)) continue;
 
		if (sld->conv & SLF_CONFIG_NO) continue;
 

	
 
		// XXX - wtf is this?? (group override?)
 
		s = strchr(sdb->name, '.');
 
		if (s != NULL) {
 
			group = ini_getgroup(ini, sdb->name, s - sdb->name);
 
			s++;
 
		} else {
 
			if (group_def == NULL) group_def = ini_getgroup(ini, grpname, -1);
 
			s = sdb->name;
 
			group = group_def;
 
		}
 

	
 
		item = ini_getitem(group, s, true);
 
		ptr = GetVariableAddress(object, sld);
 

	
 
		if (item->value != NULL) {
 
			// check if the value is the same as the old value
 
			const void *p = string_to_val(sdb, item->value);
 

	
 
			/* The main type of a variable/setting is in bytes 8-15
 
			 * The subtype (what kind of numbers do we have there) is in 0-7 */
 
			switch (sdb->cmd) {
 
			case SDT_BOOLX:
 
			case SDT_NUMX:
 
			case SDT_ONEOFMANY:
 
			case SDT_MANYOFMANY:
 
				switch (GetVarMemType(sld->conv)) {
 
				case SLE_VAR_BL:
 
					if (*(bool*)ptr == (bool)(unsigned long)p) continue;
 
					break;
 
				case SLE_VAR_I8:
 
				case SLE_VAR_U8:
 
					if (*(byte*)ptr == (byte)(unsigned long)p) continue;
 
					break;
 
				case SLE_VAR_I16:
 
				case SLE_VAR_U16:
 
					if (*(uint16*)ptr == (uint16)(unsigned long)p) continue;
 
					break;
 
				case SLE_VAR_I32:
 
				case SLE_VAR_U32:
 
					if (*(uint32*)ptr == (uint32)(unsigned long)p) continue;
 
					break;
 
				default: NOT_REACHED();
 
				}
 
				break;
 
			default: break; /* Assume the other types are always changed */
 
			}
 
		}
 

	
 
		/* Value has changed, get the new value and put it into a buffer */
 
		switch (sdb->cmd) {
 
		case SDT_BOOLX:
 
		case SDT_NUMX:
 
		case SDT_ONEOFMANY:
 
		case SDT_MANYOFMANY: {
 
			uint32 i = (uint32)ReadValue(ptr, sld->conv);
 

	
 
			switch (sdb->cmd) {
 
			case SDT_BOOLX:      strcpy(buf, (i != 0) ? "true" : "false"); break;
 
			case SDT_NUMX:       sprintf(buf, IsSignedVarMemType(sld->conv) ? "%d" : "%u", i); break;
 
			case SDT_ONEOFMANY:  make_oneofmany(buf, sdb->many, i); break;
 
			case SDT_MANYOFMANY: make_manyofmany(buf, sdb->many, i); break;
 
			default: NOT_REACHED();
 
			}
 
		} break;
 

	
 
		case SDT_STRING:
 
			switch (GetVarMemType(sld->conv)) {
 
			case SLE_VAR_STRB: strcpy(buf, (char*)ptr); break;
 
			case SLE_VAR_STRBQ:sprintf(buf, "\"%s\"", (char*)ptr); break;
 
			case SLE_VAR_STR:  strcpy(buf, *(char**)ptr); break;
 
			case SLE_VAR_STRQ: sprintf(buf, "\"%s\"", *(char**)ptr); break;
 
			case SLE_VAR_CHAR: sprintf(buf, "\"%c\"", *(char*)ptr); break;
 
			default: NOT_REACHED();
 
			}
 
			break;
 

	
 
		case SDT_INTLIST:
 
			make_intlist(buf, ptr, sld->length, GetVarMemType(sld->conv));
 
			break;
 
		default: NOT_REACHED();
 
		}
 

	
 
		/* The value is different, that means we have to write it to the ini */
 
		item->value = pool_strdup(&ini->pool, buf, strlen(buf));
 
	}
 
}
 

	
 
/** Loads all items from a 'grpname' section into a list
 
 * The list parameter can be a NULL pointer, in this case nothing will be
 
 * saved and a callback function should be defined that will take over the
 
 * list-handling and store the data itself somewhere.
 
 * @param IniFile handle to the ini file with the source data
 
 * @param grpname character string identifying the section-header of the ini
 
 * file that will be parsed
 
 * @param list pointer to an string(pointer) array that will store the parsed
 
 * entries of the given section
 
 * @param len the maximum number of items available for the above list
 
 * @param proc callback function that can override how the values are stored
 
 * inside the list */
 
static void ini_load_setting_list(IniFile *ini, const char *grpname, char **list, uint len, SettingListCallbackProc proc)
 
{
 
	IniGroup *group = ini_getgroup(ini, grpname, -1);
 
	IniItem *item;
 
	const char *entry;
 
	uint i, j;
 

	
 
	if (group == NULL) return;
 

	
 
	for (i = j = 0, item = group->item; item != NULL; item = item->next) {
 
		entry = (proc != NULL) ? proc(item, i++) : item->name;
 

	
 
		if (entry == NULL || list == NULL) continue;
 

	
 
		if (j == len) break;
 
		list[j++] = strdup(entry);
 
	}
 
}
 

	
 
/** Saves all items from a list into the 'grpname' section
 
 * The list parameter can be a NULL pointer, in this case a callback function
 
 * should be defined that will provide the source data to be saved.
 
 * @param IniFile handle to the ini file where the destination data is saved
 
 * @param grpname character string identifying the section-header of the ini file
 
 * @param list pointer to an string(pointer) array that will be used as the
 
 * source to be saved into the relevant ini section
 
 * @param len the maximum number of items available for the above list
 
 * @param proc callback function that can will provide the source data if defined */
 
static void ini_save_setting_list(IniFile *ini, const char *grpname, char **list, uint len, SettingListCallbackProc proc)
 
{
 
	IniGroup *group = ini_getgroup(ini, grpname, -1);
 
	IniItem *item = NULL;
 
	const char *entry;
 
	uint i;
 
	bool first = true;
 

	
 
	if (proc == NULL && list == NULL) return;
 
	if (group == NULL) return;
 
	group->item = NULL;
 

	
 
	for (i = 0; i != len; i++) {
 
		entry = (proc != NULL) ? proc(NULL, i) : list[i];
 

	
 
		if (entry == NULL || *entry == '\0') continue;
 

	
 
		if (first) { // add first item to the head of the group
 
			item = ini_item_alloc(group, entry, strlen(entry));
 
			item->value = item->name;
 
			group->item = item;
 
			first = false;
 
		} else { // all other items are attached to the previous one
 
			item->next = ini_item_alloc(group, entry, strlen(entry));
 
			item = item->next;
 
			item->value = item->name;
 
		}
 
	}
 
}
 

	
 
//***************************
 
// OTTD specific INI stuff
 
//***************************
 

	
 
/** Settings-macro usage:
 
 * The list might look daunting at first, but is in general easy to understand.
 
 * We have two types of list:
 
 * 1. SDTG_something
 
 * 2. SDT_something
 
 * The 'G' stands for global, so this is the one you will use for a
 
 * SettingDescGlobVarList section meaning global variables. The other uses a
 
 * Base/Offset and runtime variable selection mechanism, known from the saveload * convention (it also has global so it should not be hard).
 
 * Of each type there are again two versions, the normal one and one prefixed
 
 * with 'COND'.
 
 * COND means that the setting is only valid in certain savegame versions
 
 * (since patches are saved to the savegame, this bookkeeping is necessary.
 
 * Now there are a lot of types. Easy ones are:
 
 * - VAR:  any number type, 'type' field specifies what number. eg int8 or uint32
 
 * - BOOL: a boolean number type
 
 * - STR:  a string or character. 'type' field specifies what string. Normal, string, or quoted
 
 * A bit more difficult to use are MMANY (meaning ManyOfMany) and OMANY (OneOfMany)
 
 * These are actually normal numbers, only bitmasked. In MMANY several bits can
 
 * be set, in the other only one.
 
 * The most complex type is INTLIST. This is basically an array of numbers. If
 
 * the intlist is only valid in certain savegame versions because for example
 
 * it has grown in size its length cannot be automatically be calculated so
 
 * use SDT(G)_CONDLISTO() meaning Old.
 
 * If nothing fits you, you can use the GENERAL macros, but it exposes the
 
 * internal structure somewhat so it needs a little looking. There are _NULL()
 
 * macros as well, these fill up space so you can add more patches there (in
 
 * place) and you DON'T have to increase the savegame version. */
 

	
 
#define NSD_GENERAL(name, def, cmd, guiflags, min, max, interval, many, str, proc)\
 
	{name, (const void*)(def), cmd, guiflags, min, max, interval, many, str, proc}
 

	
 
/* Macros for various objects to go in the configuration file.
 
 * This section is for global variables */
 
#define SDTG_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, proc, from, to)\
 
{NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, proc), SLEG_GENERAL(sle_cmd, var, type | flags, length, from, to)}
 

	
 
#define SDTG_CONDVAR(name, type, flags, guiflags, var, def, min, max, interval, str, proc, from, to)\
 
	SDTG_GENERAL(name, SDT_NUMX, SL_VAR, type, flags, guiflags, var, 0, def, min, max, interval, NULL, str, proc, from, to)
 
#define SDTG_VAR(name, type, flags, guiflags, var, def, min, max, interval, str, proc)\
 
	SDTG_CONDVAR(name, type, flags, guiflags, var, def, min, max, interval, str, proc, 0, SL_MAX_VERSION)
 

	
 
#define SDTG_CONDBOOL(name, flags, guiflags, var, def, str, proc, from, to)\
 
	SDTG_GENERAL(name, SDT_BOOLX, SL_VAR, SLE_UINT8, flags, guiflags, var, 0, def, 0, 1, 0, NULL, str, proc, from, to)
 
#define SDTG_BOOL(name, flags, guiflags, var, def, str, proc)\
 
	SDTG_CONDBOOL(name, flags, guiflags, var, def, str, proc, 0, SL_MAX_VERSION)
 

	
 
#define SDTG_CONDLIST(name, type, length, flags, guiflags, var, def, str, proc, from, to)\
 
	SDTG_GENERAL(name, SDT_INTLIST, SL_ARR, type, flags, guiflags, var, length, def, 0, 0, 0, NULL, str, proc, from, to)
 
#define SDTG_LIST(name, type, flags, guiflags, var, def, str, proc)\
 
	SDTG_GENERAL(name, SDT_INTLIST, SL_ARR, type, flags, guiflags, var, lengthof(var), def, 0, 0, 0, NULL, str, proc, 0, SL_MAX_VERSION)
 

	
 
#define SDTG_CONDSTR(name, type, length, flags, guiflags, var, def, str, proc, from, to)\
 
	SDTG_GENERAL(name, SDT_STRING, SL_STR, type, flags, guiflags, var, length, def, 0, 0, 0, NULL, str, proc, from, to)
 
#define SDTG_STR(name, type, flags, guiflags, var, def, str, proc)\
 
	SDTG_GENERAL(name, SDT_STRING, SL_STR, type, flags, guiflags, var, lengthof(var), def, 0, 0, 0, NULL, str, proc, 0, SL_MAX_VERSION)
 

	
 
#define SDTG_CONDOMANY(name, type, flags, guiflags, var, def, max, full, str, proc, from, to)\
 
	SDTG_GENERAL(name, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, max, 0, full, str, proc, from, to)
 
#define SDTG_OMANY(name, type, flags, guiflags, var, def, max, full, str, proc)\
 
	SDTG_CONDOMANY(name, type, flags, guiflags, var, def, max full, str, proc, 0, SL_MAX_VERSION)
 

	
 
#define SDTG_CONDMMANY(name, type, flags, guiflags, var, def, full, str, proc, from, to)\
 
	SDTG_GENERAL(name, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, 0, 0, full, str, proc, from, to)
 
#define SDTG_MMANY(name, type, flags, guiflags, var, def, full, str, proc)\
 
	SDTG_CONDMMANY(name, type, flags, guiflags, var, def, full, str, proc, 0, SL_MAX_VERSION)
 

	
 
#define SDTG_CONDNULL(length, from, to)\
 
	{{"", NULL, 0, 0, 0, 0, 0, NULL, STR_NULL, NULL}, SLEG_CONDNULL(length, from, to)}
 

	
 
#define SDTG_END() {{NULL, NULL, 0, 0, 0, 0, 0, NULL, STR_NULL, NULL}, SLEG_END()}
 

	
 
/* Macros for various objects to go in the configuration file.
 
 * This section is for structures where their various members are saved */
 
#define SDT_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, proc, from, to)\
 
	{NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, proc), SLE_GENERAL(sle_cmd, base, var, type | flags, length, from, to)}
 

	
 
#define SDT_CONDVAR(base, var, type, from, to, flags, guiflags, def, min, max, interval, str, proc)\
 
	SDT_GENERAL(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, base, var, 1, def, min, max, interval, NULL, str, proc, from, to)
 
#define SDT_VAR(base, var, type, flags, guiflags, def, min, max, interval, str, proc)\
 
	SDT_CONDVAR(base, var, type, 0, SL_MAX_VERSION, flags, guiflags, def, min, max, interval, str, proc)
 

	
 
#define SDT_CONDBOOL(base, var, from, to, flags, guiflags, def, str, proc)\
 
	SDT_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, base, var, 1, def, 0, 1, 0, NULL, str, proc, from, to)
 
#define SDT_BOOL(base, var, flags, guiflags, def, str, proc)\
 
	SDT_CONDBOOL(base, var, 0, SL_MAX_VERSION, flags, guiflags, def, str, proc)
 

	
 
#define SDT_CONDLIST(base, var, type, from, to, flags, guiflags, def, str, proc)\
 
	SDT_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, base, var, lengthof(((base*)8)->var), def, 0, 0, 0, NULL, str, proc, from, to)
 
#define SDT_LIST(base, var, type, flags, guiflags, def, str, proc)\
 
	SDT_CONDLIST(base, var, type, 0, SL_MAX_VERSION, flags, guiflags, def, str, proc)
 
#define SDT_CONDLISTO(base, var, length, type, from, to, flags, guiflags, def, str, proc)\
 
	SDT_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, base, var, length, def, 0, 0, 0, NULL, str, proc, from, to)
 

	
 
#define SDT_CONDSTR(base, var, type, from, to, flags, guiflags, def, str, proc)\
 
	SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, base, var, lengthof(((base*)8)->var), def, 0, 0, 0, NULL, str, proc, from, to)
 
#define SDT_STR(base, var, type, flags, guiflags, def, str, proc)\
 
	SDT_CONDSTR(base, var, type, 0, SL_MAX_VERSION, flags, guiflags, def, str, proc)
 
#define SDT_CONDSTRO(base, var, length, type, from, to, flags, def, str, proc)\
 
	SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, 0, base, var, length, def, 0, 0, NULL, str, proc, from, to)
 

	
 
#define SDT_CONDCHR(base, var, from, to, flags, guiflags, def, str, proc)\
 
	SDT_GENERAL(#var, SDT_STRING, SL_VAR, SLE_CHAR, flags, guiflags, base, var, 1, def, 0, 0, 0, NULL, str, proc, from, to)
 
#define SDT_CHR(base, var, flags, guiflags, def, str, proc)\
 
	SDT_CONDCHR(base, var, 0, SL_MAX_VERSION, flags, guiflags, def, str, proc)
 

	
 
#define SDT_CONDOMANY(base, var, type, from, to, flags, guiflags, def, max, full, str, proc)\
 
	SDT_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, max, 0, full, str, proc, from, to)
 
#define SDT_OMANY(base, var, type, flags, guiflags, def, max, full, str, proc)\
 
	SDT_CONDOMANY(base, var, type, 0, SL_MAX_VERSION, flags, guiflags, def, max, full, str, proc)
 

	
 
#define SDT_CONDMMANY(base, var, type, from, to, flags, guiflags, def, full, str, proc)\
 
	SDT_GENERAL(#var, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, 0, 0, full, str, proc, from, to)
 
#define SDT_MMANY(base, var, type, flags, guiflags, def, full, str, proc)\
 
	SDT_CONDMMANY(base, var, type, 0, SL_MAX_VERSION, flags, guiflags, def, full, str, proc)
 

	
 
#define SDT_CONDNULL(length, from, to)\
 
	{{"", NULL, 0, 0, 0, 0, 0, NULL, STR_NULL, NULL}, SLE_CONDNULL(length, from, to)}
 

	
 
#define SDT_END() {{NULL, NULL, 0, 0, 0, 0, 0, NULL, STR_NULL, NULL}, SLE_END()}
 

	
 
/* Shortcuts for macros below. Logically if we don't save the value
 
 * we also don't sync it in a network game */
 
#define S SLF_SAVE_NO | SLF_NETWORK_NO
 
#define NS SLF_SAVE_NO
 
#define C SLF_CONFIG_NO
 
#define N SLF_NETWORK_NO
 

	
 
#define D0 SGF_0ISDISABLED
 
#define NC SGF_NOCOMMA
 
#define MS SGF_MULTISTRING
 
#define NO SGF_NETWORK_ONLY
 
#define CR SGF_CURRENCY
 

	
 
#include "table/strings.h"
 

	
 
/* Begin - Callback Functions for the various settings */
 
#include "window.h"
 
#include "gui.h"
 
#include "town.h"
 
#include "gfx.h"
 
// virtual PositionMainToolbar function, calls the right one.
 
static int32 v_PositionMainToolbar(int32 p1)
 
{
 
	if (_game_mode != GM_MENU) PositionMainToolbar(NULL);
 
	return 0;
 
}
 

	
 
static int32 AiNew_PatchActive_Warning(int32 p1)
 
{
 
	if (p1 == 1) ShowErrorMessage(INVALID_STRING_ID, TEMP_AI_ACTIVATED, 0, 0);
 
	return 0;
 
}
 

	
 
static int32 Ai_In_Multiplayer_Warning(int32 p1)
 
{
 
	if (p1 == 1) {
 
		ShowErrorMessage(INVALID_STRING_ID, TEMP_AI_MULTIPLAYER, 0, 0);
 
		_patches.ainew_active = true;
 
	}
 
	return 0;
 
}
 

	
 
static int32 PopulationInLabelActive(int32 p1)
 
{
 
	Town* t;
 

	
 
	FOR_ALL_TOWNS(t) UpdateTownVirtCoord(t);
 

	
 
	return 0;
 
}
 

	
 
static int32 RedrawScreen(int32 p1)
 
{
 
	MarkWholeScreenDirty();
 
	return 0;
 
}
 

	
 
static int32 InValidateDetailsWindow(int32 p1)
 
{
 
	InvalidateWindowClasses(WC_VEHICLE_DETAILS);
 
	return 0;
 
}
 

	
 
static int32 InvalidateStationBuildWindow(int32 p1)
 
{
 
	InvalidateWindow(WC_BUILD_STATION, 0);
 
	return 0;
 
}
 

	
 
/* Check service intervals of vehicles, p1 is value of % or day based servicing */
 
static int32 CheckInterval(int32 p1)
 
{
 
	bool warning;
 
	const Patches *ptc = (_game_mode == GM_MENU) ? &_patches_newgame : &_patches;
 

	
 
	if (p1) {
 
		warning = ( (IS_INT_INSIDE(ptc->servint_trains,   5, 90+1) || ptc->servint_trains   == 0) &&
 
								(IS_INT_INSIDE(ptc->servint_roadveh,  5, 90+1) || ptc->servint_roadveh  == 0) &&
 
								(IS_INT_INSIDE(ptc->servint_aircraft, 5, 90+1) || ptc->servint_aircraft == 0) &&
 
								(IS_INT_INSIDE(ptc->servint_ships,    5, 90+1) || ptc->servint_ships    == 0) );
 
	} else {
 
		warning = ( (IS_INT_INSIDE(ptc->servint_trains,   30, 800+1) || ptc->servint_trains   == 0) &&
 
								(IS_INT_INSIDE(ptc->servint_roadveh,  30, 800+1) || ptc->servint_roadveh  == 0) &&
 
								(IS_INT_INSIDE(ptc->servint_aircraft, 30, 800+1) || ptc->servint_aircraft == 0) &&
 
								(IS_INT_INSIDE(ptc->servint_ships,    30, 800+1) || ptc->servint_ships    == 0) );
 
	}
 

	
 
	if (!warning)
 
		ShowErrorMessage(INVALID_STRING_ID, STR_CONFIG_PATCHES_SERVICE_INTERVAL_INCOMPATIBLE, 0, 0);
 

	
 
	return InValidateDetailsWindow(0);
 
}
 

	
 
static int32 EngineRenewUpdate(int32 p1)
 
{
 
	DoCommandP(0, 0, _patches.autorenew, NULL, CMD_SET_AUTOREPLACE);
 
	return 0;
 
}
 

	
 
static int32 EngineRenewMonthsUpdate(int32 p1)
 
{
 
	DoCommandP(0, 1, _patches.autorenew_months, NULL, CMD_SET_AUTOREPLACE);
 
	return 0;
 
}
 

	
 
static int32 EngineRenewMoneyUpdate(int32 p1)
 
{
 
	DoCommandP(0, 2, _patches.autorenew_money, NULL, CMD_SET_AUTOREPLACE);
 
	return 0;
 
}
 
/* End - Callback Functions */
 

	
 
#ifndef EXTERNAL_PLAYER
 
#define EXTERNAL_PLAYER "timidity"
 
#endif
 

	
 
static const SettingDesc _music_settings[] = {
 
	 SDT_VAR(MusicFileSettings, playlist,   SLE_UINT8, S, 0,   0, 0,   5, 1,  STR_NULL, NULL),
 
	 SDT_VAR(MusicFileSettings, music_vol,  SLE_UINT8, S, 0, 128, 0, 100, 1,  STR_NULL, NULL),
 
	 SDT_VAR(MusicFileSettings, effect_vol, SLE_UINT8, S, 0, 128, 0, 100, 1,  STR_NULL, NULL),
 
	SDT_LIST(MusicFileSettings, custom_1,   SLE_UINT8, S, 0, NULL,            STR_NULL, NULL),
 
	SDT_LIST(MusicFileSettings, custom_2,   SLE_UINT8, S, 0, NULL,            STR_NULL, NULL),
 
	SDT_BOOL(MusicFileSettings, playing,               S, 0, true,            STR_NULL, NULL),
 
	SDT_BOOL(MusicFileSettings, shuffle,               S, 0, false,           STR_NULL, NULL),
 
	 SDT_STR(MusicFileSettings, extmidi,     SLE_STRB, S, 0, EXTERNAL_PLAYER, STR_NULL, NULL),
 
	 SDT_END()
 
};
 

	
 
/* win32_v.c only settings */
 
#ifdef WIN32
 
extern bool _force_full_redraw, _double_size, _window_maximize;
 
extern uint _display_hz, _fullscreen_bpp;
 

	
 
static const SettingDescGlobVarList _win32_settings[] = {
 
	 SDTG_VAR("display_hz",     SLE_UINT, S, 0, _display_hz,       0, 0, 120, 0, STR_NULL, NULL),
 
	SDTG_BOOL("force_full_redraw",        S, 0, _force_full_redraw,false,        STR_NULL, NULL),
 
	 SDTG_VAR("fullscreen_bpp", SLE_UINT, S, 0, _fullscreen_bpp,   8, 8,  32, 0, STR_NULL, NULL),
 
	SDTG_BOOL("double_size",              S, 0, _double_size,      false,        STR_NULL, NULL),
 
	SDTG_BOOL("window_maximize",          S, 0, _window_maximize,  false,        STR_NULL, NULL),
 
	 SDTG_END()
 
};
 
#endif /* WIN32 */
 

	
 
static const SettingDescGlobVarList _misc_settings[] = {
 
	SDTG_MMANY("display_opt",     SLE_UINT8, S, 0, _display_opt,       (DO_SHOW_TOWN_NAMES|DO_SHOW_STATION_NAMES|DO_SHOW_SIGNS|DO_FULL_ANIMATION|DO_FULL_DETAIL|DO_TRANS_BUILDINGS|DO_WAYPOINTS), "SHOW_TOWN_NAMES|SHOW_STATION_NAMES|SHOW_SIGNS|FULL_ANIMATION|TRANS_BUILDINGS|FULL_DETAIL|WAYPOINTS", STR_NULL, NULL),
 
	  SDTG_VAR("news_display_opt", SLE_UINT, S, 0, _news_display_opt,0xAAAAAAAA,0,0xAAAAAAAA,0,STR_NULL, NULL), // default to all full messages: 10101010101010101010 = 0xAAAAAAAA
 
	 SDTG_BOOL("news_ticker_sound",          S, 0, _news_ticker_sound,     true,    STR_NULL, NULL),
 
	 SDTG_BOOL("fullscreen",                 S, 0, _fullscreen,           false,    STR_NULL, NULL),
 
	  SDTG_STR("videodriver",      SLE_STRB,C|S,0, _ini_videodriver,       NULL,    STR_NULL, NULL),
 
	  SDTG_STR("musicdriver",      SLE_STRB,C|S,0, _ini_musicdriver,       NULL,    STR_NULL, NULL),
 
	  SDTG_STR("sounddriver",      SLE_STRB,C|S,0, _ini_sounddriver,       NULL,    STR_NULL, NULL),
 
	  SDTG_STR("language",         SLE_STRB, S, 0, _dynlang.curr_file,     NULL,    STR_NULL, NULL),
 
	 SDTG_LIST("resolution",     SLE_UINT16, S, 0, _cur_resolution,   "640,480",    STR_NULL, NULL),
 
	  SDTG_STR("screenshot_format",SLE_STRB, S, 0, _screenshot_format_name,NULL,    STR_NULL, NULL),
 
	  SDTG_STR("savegame_format",  SLE_STRB, S, 0, _savegame_format,       NULL,    STR_NULL, NULL),
 
	 SDTG_BOOL("rightclick_emulate",         S, 0, _rightclick_emulate,   false,    STR_NULL, NULL),
 
#ifdef WITH_FREETYPE
 
	  SDTG_STR("small_font",       SLE_STRB, S, 0, _freetype.small_font,   NULL,    STR_NULL, NULL),
 
	  SDTG_STR("medium_font",      SLE_STRB, S, 0, _freetype.medium_font,  NULL,    STR_NULL, NULL),
 
	  SDTG_STR("large_font",       SLE_STRB, S, 0, _freetype.large_font,   NULL,    STR_NULL, NULL),
 
	  SDTG_VAR("small_size",       SLE_UINT, S, 0, _freetype.small_size,   6, 0, 72, 0, STR_NULL, NULL),
 
	  SDTG_VAR("medium_size",      SLE_UINT, S, 0, _freetype.medium_size, 10, 0, 72, 0, STR_NULL, NULL),
 
	  SDTG_VAR("large_size",       SLE_UINT, S, 0, _freetype.large_size,  16, 0, 72, 0, STR_NULL, NULL),
 
#endif
 
	  SDTG_END()
 
};
 

	
 
#ifdef ENABLE_NETWORK
 
static const SettingDescGlobVarList _network_settings[] = {
 
	 SDTG_VAR("sync_freq",           SLE_UINT16,C|S,0, _network_sync_freq,            100, 0,   100,   0, STR_NULL, NULL),
 
	 SDTG_VAR("frame_freq",           SLE_UINT8,C|S,0, _network_frame_freq,             0, 0,   100,   0, STR_NULL, NULL),
 
	 SDTG_VAR("max_join_time",       SLE_UINT16, S, 0, _network_max_join_time,        500, 0, 32000,   0, STR_NULL, NULL),
 
	SDTG_BOOL("pause_on_join",                   S, 0, _network_pause_on_join,        true,               STR_NULL, NULL),
 
	 SDTG_STR("server_bind_ip",        SLE_STRB, S, 0, _network_server_bind_ip_host,  "0.0.0.0",          STR_NULL, NULL),
 
	 SDTG_VAR("server_port",         SLE_UINT16, S, 0, _network_server_port,          NETWORK_DEFAULT_PORT, 0, 65535, 0, STR_NULL, NULL),
 
	SDTG_BOOL("server_advertise",                S, 0, _network_advertise,            false,              STR_NULL, NULL),
 
	 SDTG_VAR("lan_internet",         SLE_UINT8, S, 0, _network_lan_internet,           0, 0,     1,   0, STR_NULL, NULL),
 
	 SDTG_STR("player_name",           SLE_STRB, S, 0, _network_player_name,          NULL,               STR_NULL, NULL),
 
	 SDTG_STR("server_password",       SLE_STRB, S, 0, _network_server_password,      NULL,               STR_NULL, NULL),
 
	 SDTG_STR("rcon_password",         SLE_STRB, S, 0, _network_rcon_password,        NULL,               STR_NULL, NULL),
 
	 SDTG_STR("server_name",           SLE_STRB, S, 0, _network_server_name,          NULL,               STR_NULL, NULL),
 
	 SDTG_STR("connect_to_ip",         SLE_STRB, S, 0, _network_default_ip,           NULL,               STR_NULL, NULL),
 
	 SDTG_STR("network_id",            SLE_STRB, S, 0, _network_unique_id,            NULL,               STR_NULL, NULL),
 
	SDTG_BOOL("autoclean_companies",             S, 0, _network_autoclean_companies,  false,              STR_NULL, NULL),
 
	 SDTG_VAR("autoclean_unprotected",SLE_UINT8, S, 0, _network_autoclean_unprotected,12, 0,     60,   0, STR_NULL, NULL),
 
	 SDTG_VAR("autoclean_protected",  SLE_UINT8, S, 0, _network_autoclean_protected,  36, 0,    180,   0, STR_NULL, NULL),
 
	 SDTG_VAR("max_companies",        SLE_UINT8, S, 0, _network_game_info.companies_max,   8, 0,  8,   0, STR_NULL, NULL),
 
	 SDTG_VAR("max_clients",          SLE_UINT8, S, 0, _network_game_info.clients_max,    10, 0, 10,   0, STR_NULL, NULL),
 
	 SDTG_VAR("max_spectators",       SLE_UINT8, S, 0, _network_game_info.spectators_max, 10, 0, 10,   0, STR_NULL, NULL),
 
	 SDTG_VAR("restart_game_year",    SLE_INT32, S,D0, _network_restart_game_year,    0, MIN_YEAR, MAX_YEAR, 1, STR_NULL, NULL),
 
	 SDTG_VAR("min_players",          SLE_UINT8, S, 0, _network_min_players,               0, 0, 10,   0, STR_NULL, NULL),
 
	 SDTG_END()
 
};
 
#endif /* ENABLE_NETWORK */
 

	
 
static const SettingDesc _gameopt_settings[] = {
 
	/* In version 4 a new difficulty setting has been added to the difficulty settings,
 
	 * town attitude towards demolishing. Needs special handling because some dimwit thought
 
	 * it funny to have the GameDifficulty struct be an array while it is a struct of
 
	 * same-sized members
 
	 * XXX - To save file-space and since values are never bigger than about 10? only
 
	 * save the first 16 bits in the savegame. Question is why the values are still int32
 
	 * and why not byte for example? */
 
	SDT_GENERAL("diff_custom", SDT_INTLIST, SL_ARR, (SLE_FILE_I16 | SLE_VAR_I32), 0, 0, GameOptions, diff, 17, 0, 0, 0, 0, NULL, STR_NULL, NULL, 0, 3),
 
	SDT_GENERAL("diff_custom", SDT_INTLIST, SL_ARR, (SLE_FILE_I16 | SLE_VAR_I32), 0, 0, GameOptions, diff, 18, 0, 0, 0, 0, NULL, STR_NULL, NULL, 4, SL_MAX_VERSION),
 
	    SDT_VAR(GameOptions, diff_level,SLE_UINT8, 0, 0, 9, 0,  9, 0, STR_NULL, NULL),
 
	  SDT_OMANY(GameOptions, currency,  SLE_UINT8, N, 0, 0, CUSTOM_CURRENCY_ID, "GBP|USD|EUR|YEN|ATS|BEF|CHF|CZK|DEM|DKK|ESP|FIM|FRF|GRD|HUF|ISK|ITL|NLG|NOK|PLN|ROL|RUR|SIT|SEK|YTL|SKK|BRR|custom", STR_NULL, NULL),
 
	  SDT_OMANY(GameOptions, units,     SLE_UINT8, N, 0, 1,     2, "imperial|metric|si", STR_NULL, NULL),
 
	  SDT_OMANY(GameOptions, town_name, SLE_UINT8, 0, 0, 0,    20, "english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovakish|norwegian|hungarian|austrian|romanian|czech|swiss|danish|turkish|italian|catalan", STR_NULL, NULL),
 
	  SDT_OMANY(GameOptions, landscape, SLE_UINT8, 0, 0, 0,     3, "normal|hilly|desert|candy", STR_NULL, NULL),
 
	    SDT_VAR(GameOptions, snow_line, SLE_UINT8, 0, 0, 1, 0, 56, 0, STR_NULL, NULL),
 
	SDT_CONDOMANY(GameOptions,autosave, SLE_UINT8, 0, 22,             N, 0, 0, 0, "", STR_NULL, NULL),
 
	SDT_CONDOMANY(GameOptions,autosave, SLE_UINT8,23, SL_MAX_VERSION, S, 0, 1, 4, "off|monthly|quarterly|half year|yearly", STR_NULL, NULL),
 
	  SDT_OMANY(GameOptions, road_side, SLE_UINT8, 0, 0, 1,   1, "left|right", STR_NULL, NULL),
 
	    SDT_END()
 
};
 

	
 
/* Some patches do not need to be synchronised when playing in multiplayer.
 
 * These include for example the GUI settings and will not be saved with the
 
 * savegame.
 
 * It is also a bit tricky since you would think that service_interval
 
 * for example doesn't need to be synched. Every client assigns the
 
 * service_interval value to the v->service_interval, meaning that every client
 
 * assigns his value. If the setting was player-based, that would mean that
 
 * vehicles could decide on different moments that they are heading back to a
 
 * service depot, causing desyncs on a massive scale. */
 
const SettingDesc _patch_settings[] = {
 
	/***************************************************************************/
 
	/* User-interface section of the GUI-configure patches window */
 
	SDT_BOOL(Patches, vehicle_speed,                 S, 0,  true,        STR_CONFIG_PATCHES_VEHICLESPEED,          NULL),
 
	SDT_BOOL(Patches, status_long_date,              S, 0,  true,        STR_CONFIG_PATCHES_LONGDATE,              NULL),
 
	SDT_BOOL(Patches, show_finances,                 S, 0,  true,        STR_CONFIG_PATCHES_SHOWFINANCES,          NULL),
 
	SDT_BOOL(Patches, autoscroll,                    S, 0, false,        STR_CONFIG_PATCHES_AUTOSCROLL,            NULL),
 
	SDT_BOOL(Patches, reverse_scroll,                S, 0, false,        STR_CONFIG_PATCHES_REVERSE_SCROLLING,     NULL),
 
	SDT_BOOL(Patches, measure_tooltip,               S, 0, false,        STR_CONFIG_PATCHES_MEASURE_TOOLTIP,       NULL),
 
	 SDT_VAR(Patches, errmsg_duration,    SLE_UINT8, S, 0,  5, 0, 20, 0, STR_CONFIG_PATCHES_ERRMSG_DURATION,       NULL),
 
	 SDT_VAR(Patches, toolbar_pos,        SLE_UINT8, S,MS,  0, 0,  2, 0, STR_CONFIG_PATCHES_TOOLBAR_POS,           v_PositionMainToolbar),
 
	 SDT_VAR(Patches, window_snap_radius, SLE_UINT8, S,D0, 10, 1, 32, 0, STR_CONFIG_PATCHES_SNAP_RADIUS,           NULL),
 
	SDT_BOOL(Patches, invisible_trees,               S, 0, false,        STR_CONFIG_PATCHES_INVISIBLE_TREES,       RedrawScreen),
 
	SDT_BOOL(Patches, population_in_label,           S, 0,  true,        STR_CONFIG_PATCHES_POPULATION_IN_LABEL,   PopulationInLabelActive),
 
	 SDT_VAR(Patches, map_x,              SLE_UINT8, S, 0,  8, 6, 11, 0, STR_CONFIG_PATCHES_MAP_X,                 NULL),
 
	 SDT_VAR(Patches, map_y,              SLE_UINT8, S, 0,  8, 6, 11, 0, STR_CONFIG_PATCHES_MAP_Y,                 NULL),
 
	SDT_BOOL(Patches, link_terraform_toolbar,        S, 0, false,        STR_CONFIG_PATCHES_LINK_TERRAFORM_TOOLBAR,NULL),
 
	 SDT_VAR(Patches, liveries,           SLE_UINT8, S,MS,  2, 0,  2, 0, STR_CONFIG_PATCHES_LIVERIES,              RedrawScreen),
 
	SDT_BOOL(Patches, prefer_teamchat,               S, 0, false,        STR_CONFIG_PATCHES_PREFER_TEAMCHAT,       NULL),
 

	
 
	/***************************************************************************/
 
	/* Construction section of the GUI-configure patches window */
 
	SDT_BOOL(Patches, build_on_slopes,               0, 0,  true,        STR_CONFIG_PATCHES_BUILDONSLOPES,       NULL),
 
	SDT_BOOL(Patches, extra_dynamite,                0, 0, false,        STR_CONFIG_PATCHES_EXTRADYNAMITE,       NULL),
 
	SDT_BOOL(Patches, longbridges,                   0, 0,  true,        STR_CONFIG_PATCHES_LONGBRIDGES,         NULL),
 
	SDT_BOOL(Patches, signal_side,                   N, 0,  true,        STR_CONFIG_PATCHES_SIGNALSIDE,          RedrawScreen),
 
	SDT_BOOL(Patches, always_small_airport,          0, 0, false,        STR_CONFIG_PATCHES_SMALL_AIRPORTS,      NULL),
 
	 SDT_VAR(Patches, drag_signals_density,SLE_UINT8,S, 0,  4, 1, 20, 0, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY,NULL),
 

	
 
	/***************************************************************************/
 
	/* Vehicle section of the GUI-configure patches window */
 
	SDT_BOOL(Patches, realistic_acceleration,        0, 0, false,                    STR_CONFIG_PATCHES_REALISTICACCEL,       NULL),
 
	SDT_BOOL(Patches, forbid_90_deg,                 0, 0, false,                    STR_CONFIG_PATCHES_FORBID_90_DEG,        NULL),
 
	SDT_BOOL(Patches, mammoth_trains,                0, 0,  true,                    STR_CONFIG_PATCHES_MAMMOTHTRAINS,        NULL),
 
	SDT_BOOL(Patches, gotodepot,                     0, 0,  true,                    STR_CONFIG_PATCHES_GOTODEPOT,            NULL),
 
	SDT_BOOL(Patches, roadveh_queue,                 0, 0,  true,                    STR_CONFIG_PATCHES_ROADVEH_QUEUE,        NULL),
 
	SDT_BOOL(Patches, new_pathfinding_all,           0, 0, false,                    STR_CONFIG_PATCHES_NEW_PATHFINDING_ALL,  NULL),
 

	
 
	SDT_CONDBOOL(Patches, yapf.ship_use_yapf,      28, SL_MAX_VERSION, 0, 0, false,  STR_CONFIG_PATCHES_YAPF_SHIPS,      NULL),
 
	SDT_CONDBOOL(Patches, yapf.road_use_yapf,      28, SL_MAX_VERSION, 0, 0,  true,  STR_CONFIG_PATCHES_YAPF_ROAD,       NULL),
 
	SDT_CONDBOOL(Patches, yapf.rail_use_yapf,      28, SL_MAX_VERSION, 0, 0,  true,  STR_CONFIG_PATCHES_YAPF_RAIL,       NULL),
 

	
 
	SDT_BOOL(Patches, train_income_warn,             S, 0,  true,                    STR_CONFIG_PATCHES_WARN_INCOME_LESS,     NULL),
 
	 SDT_VAR(Patches, order_review_system,SLE_UINT8, S,MS,     2,     0,       2, 0, STR_CONFIG_PATCHES_ORDER_REVIEW,         NULL),
 
	SDT_BOOL(Patches, never_expire_vehicles,         0, 0, false,                    STR_CONFIG_PATCHES_NEVER_EXPIRE_VEHICLES,NULL),
 
	SDT_BOOL(Patches, lost_train_warn,               S, 0,  true,                    STR_CONFIG_PATCHES_WARN_LOST_TRAIN,      NULL),
 
	SDT_BOOL(Patches, autorenew,                     S, 0, false,                    STR_CONFIG_PATCHES_AUTORENEW_VEHICLE,    EngineRenewUpdate),
 
	 SDT_VAR(Patches, autorenew_months,   SLE_INT16, S, 0,     6,   -12,      12, 0, STR_CONFIG_PATCHES_AUTORENEW_MONTHS,     EngineRenewMonthsUpdate),
 
	 SDT_VAR(Patches, autorenew_money,     SLE_UINT, S,CR,100000,     0, 2000000, 0, STR_CONFIG_PATCHES_AUTORENEW_MONEY,      EngineRenewMoneyUpdate),
 
	 SDT_VAR(Patches, max_trains,        SLE_UINT16, 0, 0,   500,     0,    5000, 0, STR_CONFIG_PATCHES_MAX_TRAINS,           NULL),
 
	 SDT_VAR(Patches, max_roadveh,       SLE_UINT16, 0, 0,   500,     0,    5000, 0, STR_CONFIG_PATCHES_MAX_ROADVEH,          NULL),
 
	 SDT_VAR(Patches, max_aircraft,      SLE_UINT16, 0, 0,   200,     0,    5000, 0, STR_CONFIG_PATCHES_MAX_AIRCRAFT,         NULL),
 
	 SDT_VAR(Patches, max_ships,         SLE_UINT16, 0, 0,   300,     0,    5000, 0, STR_CONFIG_PATCHES_MAX_SHIPS,            NULL),
 
	SDT_BOOL(Patches, servint_ispercent,             0, 0, false,                    STR_CONFIG_PATCHES_SERVINT_ISPERCENT,    CheckInterval),
 
	 SDT_VAR(Patches, servint_trains,    SLE_UINT16, 0,D0,   150,     5,     800, 0, STR_CONFIG_PATCHES_SERVINT_TRAINS,       InValidateDetailsWindow),
 
	 SDT_VAR(Patches, servint_roadveh,   SLE_UINT16, 0,D0,   150,     5,     800, 0, STR_CONFIG_PATCHES_SERVINT_ROADVEH,      InValidateDetailsWindow),
 
	 SDT_VAR(Patches, servint_ships,     SLE_UINT16, 0,D0,   360,     5,     800, 0, STR_CONFIG_PATCHES_SERVINT_SHIPS,        InValidateDetailsWindow),
 
	 SDT_VAR(Patches, servint_aircraft,  SLE_UINT16, 0,D0,   100,     5,     800, 0, STR_CONFIG_PATCHES_SERVINT_AIRCRAFT,     InValidateDetailsWindow),
 
	SDT_BOOL(Patches, no_servicing_if_no_breakdowns, 0, 0, false,                    STR_CONFIG_PATCHES_NOSERVICE,            NULL),
 
	SDT_BOOL(Patches, wagon_speed_limits,            0, 0,  true,                    STR_CONFIG_PATCHES_WAGONSPEEDLIMITS,     NULL),
 
	SDT_CONDBOOL(Patches, disable_elrails, 38, SL_MAX_VERSION, 0, 0, false,          STR_CONFIG_PATCHES_DISABLE_ELRAILS,      SettingsDisableElrail),
 
	SDT_CONDVAR(Patches, freight_trains, SLE_UINT8, 39, SL_MAX_VERSION, 0, 0, 1, 1, 255, 1, STR_CONFIG_PATCHES_FREIGHT_TRAINS, NULL),
 

	
 
	/***************************************************************************/
 
	/* Station section of the GUI-configure patches window */
 
	SDT_BOOL(Patches, join_stations,           0, 0,  true,        STR_CONFIG_PATCHES_JOINSTATIONS,       NULL),
 
	SDT_BOOL(Patches, full_load_any,           0, 0,  true,        STR_CONFIG_PATCHES_FULLLOADANY,        NULL),
 
	SDT_BOOL(Patches, improved_load,           0, 0, false,        STR_CONFIG_PATCHES_IMPROVEDLOAD,       NULL),
 
	SDT_BOOL(Patches, selectgoods,             0, 0,  true,        STR_CONFIG_PATCHES_SELECTGOODS,        NULL),
 
	SDT_BOOL(Patches, new_nonstop,             0, 0, false,        STR_CONFIG_PATCHES_NEW_NONSTOP,        NULL),
 
	SDT_BOOL(Patches, nonuniform_stations,     0, 0,  true,        STR_CONFIG_PATCHES_NONUNIFORM_STATIONS,NULL),
 
	 SDT_VAR(Patches, station_spread,SLE_UINT8,0, 0, 12, 4, 64, 0, STR_CONFIG_PATCHES_STATION_SPREAD,     InvalidateStationBuildWindow),
 
	SDT_BOOL(Patches, serviceathelipad,        0, 0,  true,        STR_CONFIG_PATCHES_SERVICEATHELIPAD,   NULL),
 
	SDT_BOOL(Patches, modified_catchment,      0, 0,  true,        STR_CONFIG_PATCHES_CATCHMENT,          NULL),
 
	SDT_CONDBOOL(Patches, gradual_loading, 40, SL_MAX_VERSION, 0, 0,  true, STR_CONFIG_PATCHES_GRADUAL_LOADING,    NULL),
 

	
 
	/***************************************************************************/
 
	/* Economy section of the GUI-configure patches window */
 
	SDT_BOOL(Patches, inflation,                  0, 0,  true,            STR_CONFIG_PATCHES_INFLATION,        NULL),
 
	SDT_BOOL(Patches, build_rawmaterial_ind,      0, 0, false,            STR_CONFIG_PATCHES_BUILDXTRAIND,     NULL),
 
	SDT_BOOL(Patches, multiple_industry_per_town, 0, 0, false,            STR_CONFIG_PATCHES_MULTIPINDTOWN,    NULL),
 
	SDT_BOOL(Patches, same_industry_close,        0, 0, false,            STR_CONFIG_PATCHES_SAMEINDCLOSE,     NULL),
 
	SDT_BOOL(Patches, bribe,                      0, 0,  true,            STR_CONFIG_PATCHES_BRIBE,            NULL),
 
	 SDT_VAR(Patches, snow_line_height,SLE_UINT8, 0, 0,     7,  2, 13, 0, STR_CONFIG_PATCHES_SNOWLINE_HEIGHT,  NULL),
 
	 SDT_VAR(Patches, colored_news_year,SLE_INT32, 0,NC,  2000, MIN_YEAR, MAX_YEAR, 1, STR_CONFIG_PATCHES_COLORED_NEWS_YEAR,NULL),
 
	 SDT_VAR(Patches, starting_year,    SLE_INT32, 0,NC,  1950, MIN_YEAR, MAX_YEAR, 1, STR_CONFIG_PATCHES_STARTING_YEAR,NULL),
 
	 SDT_VAR(Patches, ending_year,      SLE_INT32,0,NC|NO,2051, MIN_YEAR, MAX_YEAR, 1, STR_CONFIG_PATCHES_ENDING_YEAR,  NULL),
 
	SDT_BOOL(Patches, smooth_economy,             0, 0,  true,            STR_CONFIG_PATCHES_SMOOTH_ECONOMY,   NULL),
 
	SDT_BOOL(Patches, allow_shares,               0, 0,  true,            STR_CONFIG_PATCHES_ALLOW_SHARES,     NULL),
 

	
 
	/***************************************************************************/
 
	/* AI section of the GUI-configure patches window */
 
	SDT_BOOL(Patches, ainew_active,           0, 0, false, STR_CONFIG_PATCHES_AINEW_ACTIVE,      AiNew_PatchActive_Warning),
 
	SDT_BOOL(Patches, ai_in_multiplayer,      0, 0, false, STR_CONFIG_PATCHES_AI_IN_MULTIPLAYER, Ai_In_Multiplayer_Warning),
 
	SDT_BOOL(Patches, ai_disable_veh_train,   0, 0, false, STR_CONFIG_PATCHES_AI_BUILDS_TRAINS,  NULL),
 
	SDT_BOOL(Patches, ai_disable_veh_roadveh, 0, 0, false, STR_CONFIG_PATCHES_AI_BUILDS_ROADVEH, NULL),
 
	SDT_BOOL(Patches, ai_disable_veh_aircraft,0, 0, false, STR_CONFIG_PATCHES_AI_BUILDS_AIRCRAFT,NULL),
 
	SDT_BOOL(Patches, ai_disable_veh_ship,    0, 0, false, STR_CONFIG_PATCHES_AI_BUILDS_SHIPS,   NULL),
 

	
 
	/***************************************************************************/
 
	/* Patches without any GUI representation */
 
	SDT_BOOL(Patches, keep_all_autosave,              S, 0, false,         STR_NULL, NULL),
 
	SDT_BOOL(Patches, autosave_on_exit,               S, 0, false,         STR_NULL, NULL),
 
	 SDT_VAR(Patches, max_num_autosaves,   SLE_UINT8, S, 0, 16, 0, 255, 0, STR_NULL, NULL),
 
	SDT_BOOL(Patches, bridge_pillars,                 S, 0,  true,         STR_NULL, NULL),
 
	 SDT_VAR(Patches, extend_vehicle_life, SLE_UINT8, 0, 0,  0, 0, 100, 0, STR_NULL, NULL),
 
	SDT_BOOL(Patches, auto_euro,                      S, 0,  true,         STR_NULL, NULL),
 
	 SDT_VAR(Patches, dist_local_authority,SLE_UINT8, 0, 0, 20, 5,  60, 0, STR_NULL, NULL),
 
	 SDT_VAR(Patches, wait_oneway_signal,  SLE_UINT8, 0, 0, 15, 2, 100, 0, STR_NULL, NULL),
 
	 SDT_VAR(Patches, wait_twoway_signal,  SLE_UINT8, 0, 0, 41, 2, 100, 0, STR_NULL, NULL),
 

	
 
	/***************************************************************************/
 
	/* New Pathfinding patch settings */
 
	SDT_VAR(Patches, pf_maxlength,      SLE_UINT16, 0, 0,  4096,  64,  65535, 0, STR_NULL, NULL),
 
	SDT_VAR(Patches, pf_maxdepth,        SLE_UINT8, 0, 0,    48,   4,    255, 0, STR_NULL, NULL),
 
	/* The maximum number of nodes to search */
 
	SDT_VAR(Patches, npf_max_search_nodes,SLE_UINT, 0, 0, 10000, 500, 100000, 0, STR_NULL, NULL),
 

	
 
	/* When a red signal is encountered, a small detour can be made around
 
	 * it. This specifically occurs when a track is doubled, in which case
 
	 * the detour is typically 2 tiles. It is also often used at station
 
	 * entrances, when there is a choice of multiple platforms. If we take
 
	 * a typical 4 platform station, the detour is 4 tiles. To properly
 
	 * support larger stations we increase this value.
 
	 * We want to prevent that trains that want to leave at one side of a
 
	 * station, leave through the other side, turn around, enter the
 
	 * station on another platform and exit the station on the right side
 
	 * again, just because the sign at the right side was red. If we take
 
	 * a typical 5 length station, this detour is 10 or 11 tiles (not
 
	 * sure), so we set the default penalty at 10 (the station tile
 
	 * penalty will further prevent this.
 
	 * We give presignal exits (and combo's) a different (larger) penalty, because
 
	 * we really don't want trains waiting in front of a presignal exit. */
 
	SDT_VAR(Patches, npf_rail_firstred_penalty,     SLE_UINT, 0, 0, (10 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL),
 
	SDT_VAR(Patches, npf_rail_firstred_exit_penalty,SLE_UINT, 0, 0, (100 * NPF_TILE_LENGTH),0, 100000, 0, STR_NULL, NULL),
 
	/* This penalty is for when the last signal before the target is red.
 
	 * This is useful for train stations, where there are multiple
 
	 * platforms to choose from, which lie in different signal blocks.
 
	 * Every target in a occupied signal block (ie an occupied platform)
 
	 * will get this penalty. */
 
	SDT_VAR(Patches, npf_rail_lastred_penalty, SLE_UINT, 0, 0, (10 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL),
 
	/* When a train plans a route over a station tile, this penalty is
 
	 * applied. We want that trains plan a route around a typical, 4x5
 
	 * station, which means two tiles to the right, and two tiles back to
 
	 * the left around it, or 5 tiles of station through it. If we assign
 
	 * a penalty of 1 tile for every station tile passed, the route will
 
	 * be around it. */
 
	SDT_VAR(Patches, npf_rail_station_penalty, SLE_UINT, 0, 0, (1 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL),
 
	SDT_VAR(Patches, npf_rail_slope_penalty,   SLE_UINT, 0, 0, (1 * NPF_TILE_LENGTH), 0, 100000, 0, STR_NULL, NULL),
 
	/* This penalty is applied when a train makes a turn. Its value of 1 makes
 
	 * sure that it has a minimal impact on the pathfinding, only when two
 
	 * paths have equal length it will make a difference */
 
	SDT_VAR(Patches, npf_rail_curve_penalty,        SLE_UINT, 0, 0, 1,                      0, 100000, 0, STR_NULL, NULL),
 
	/* Ths penalty is applied when a vehicle reverses inside a depot (doesn't
 
	 * apply to ships, as they can just come out the other end). XXX: Is this a
 
	 * good value? */
 
	SDT_VAR(Patches, npf_rail_depot_reverse_penalty,SLE_UINT, 0, 0, (NPF_TILE_LENGTH * 50), 0, 100000, 0, STR_NULL, NULL),
 
	SDT_VAR(Patches, npf_buoy_penalty,              SLE_UINT, 0, 0, (2 * NPF_TILE_LENGTH),  0, 100000, 0, STR_NULL, NULL),
 
	/* This penalty is applied when a ship makes a turn. It is bigger than the
 
	 * rail curve penalty, since ships (realisticly) have more trouble with
 
	 * making turns */
 
	SDT_VAR(Patches, npf_water_curve_penalty,       SLE_UINT, 0, 0, (NPF_TILE_LENGTH / 4),  0, 100000, 0, STR_NULL, NULL),
 
	/* This is the penalty for road, same as for rail. */
 
	SDT_VAR(Patches, npf_road_curve_penalty,        SLE_UINT, 0, 0, 1,                      0, 100000, 0, STR_NULL, NULL),
 
	/* This is the penalty for level crossings, for both road and rail vehicles */
 
	SDT_VAR(Patches, npf_crossing_penalty,          SLE_UINT, 0, 0, (3 * NPF_TILE_LENGTH),  0, 100000, 0, STR_NULL, NULL),
 

	
 

	
 
	// The maximum number of nodes to search
 
	SDT_CONDBOOL(Patches, yapf.disable_node_optimization  ,           28, SL_MAX_VERSION, 0, 0, false                   ,                       STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.max_search_nodes           , SLE_UINT, 28, SL_MAX_VERSION, 0, 0, 10000                   ,      500, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDBOOL(Patches, yapf.rail_firstred_twoway_eol   ,           28, SL_MAX_VERSION, 0, 0,  true                   ,                       STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_firstred_penalty      , SLE_UINT, 28, SL_MAX_VERSION, 0, 0,    10 * YAPF_TILE_LENGTH,        0, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_firstred_exit_penalty , SLE_UINT, 28, SL_MAX_VERSION, 0, 0,   100 * YAPF_TILE_LENGTH,        0, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_lastred_penalty       , SLE_UINT, 28, SL_MAX_VERSION, 0, 0,    10 * YAPF_TILE_LENGTH,        0, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_lastred_exit_penalty  , SLE_UINT, 28, SL_MAX_VERSION, 0, 0,   100 * YAPF_TILE_LENGTH,        0, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_station_penalty       , SLE_UINT, 28, SL_MAX_VERSION, 0, 0,    30 * YAPF_TILE_LENGTH,        0, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_slope_penalty         , SLE_UINT, 28, SL_MAX_VERSION, 0, 0,     2 * YAPF_TILE_LENGTH,        0, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_curve45_penalty       , SLE_UINT, 28, SL_MAX_VERSION, 0, 0,     1 * YAPF_TILE_LENGTH,        0, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_curve90_penalty       , SLE_UINT, 28, SL_MAX_VERSION, 0, 0,     6 * YAPF_TILE_LENGTH,        0, 1000000, 0, STR_NULL, NULL),
 
	// This penalty is applied when a train reverses inside a depot
 
	SDT_CONDVAR (Patches, yapf.rail_depot_reverse_penalty , SLE_UINT, 28, SL_MAX_VERSION, 0, 0,    50 * YAPF_TILE_LENGTH,        0, 1000000, 0, STR_NULL, NULL),
 
	// This is the penalty for level crossings (for trains only)
 
	SDT_CONDVAR (Patches, yapf.rail_crossing_penalty      , SLE_UINT, 28, SL_MAX_VERSION, 0, 0,     3 * YAPF_TILE_LENGTH,        0, 1000000, 0, STR_NULL, NULL),
 
	// look-ahead how many signals are checked
 
	SDT_CONDVAR (Patches, yapf.rail_look_ahead_max_signals, SLE_UINT, 28, SL_MAX_VERSION, 0, 0,    10                   ,        1,     100, 0, STR_NULL, NULL),
 
	// look-ahead n-th red signal penalty polynomial: penalty = p2 * n^2 + p1 * n + p0
 
	SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p0  , SLE_INT , 28, SL_MAX_VERSION, 0, 0,   500                   , -1000000, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p1  , SLE_INT , 28, SL_MAX_VERSION, 0, 0,  -100                   , -1000000, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_look_ahead_signal_p2  , SLE_INT , 28, SL_MAX_VERSION, 0, 0,     5                   , -1000000, 1000000, 0, STR_NULL, NULL),
 
	// penalties for too long or too short station platforms
 
	SDT_CONDVAR (Patches, yapf.rail_longer_platform_penalty,           SLE_UINT, 33, SL_MAX_VERSION, 0, 0,  8 * YAPF_TILE_LENGTH, 0,   20000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_longer_platform_per_tile_penalty,  SLE_UINT, 33, SL_MAX_VERSION, 0, 0,  0 * YAPF_TILE_LENGTH, 0,   20000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_shorter_platform_penalty,          SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 40 * YAPF_TILE_LENGTH, 0,   20000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.rail_shorter_platform_per_tile_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0,  0 * YAPF_TILE_LENGTH, 0,   20000, 0, STR_NULL, NULL),
 
	// road vehicles - penalties
 
	SDT_CONDVAR (Patches, yapf.road_slope_penalty                    , SLE_UINT, 33, SL_MAX_VERSION, 0, 0,  2 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.road_curve_penalty                    , SLE_UINT, 33, SL_MAX_VERSION, 0, 0,  1 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
 
	SDT_CONDVAR (Patches, yapf.road_crossing_penalty                 , SLE_UINT, 33, SL_MAX_VERSION, 0, 0,  3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
 

	
 
	/***************************************************************************/
 
	/* Terrain genation related patch options */
 
	SDT_CONDVAR(Patches,      land_generator,           SLE_UINT8,  30, SL_MAX_VERSION, 0, MS,   1,                   0,    1,               0, STR_CONFIG_PATCHES_LAND_GENERATOR,           NULL),
 
	SDT_CONDVAR(Patches,      oil_refinery_limit,       SLE_UINT8,  30, SL_MAX_VERSION, 0, 0,   16,                  12,   48,               0, STR_CONFIG_PATCHES_OIL_REF_EDGE_DISTANCE,    NULL),
 
	SDT_CONDVAR(Patches,      tgen_smoothness,          SLE_UINT8,  30, SL_MAX_VERSION, 0, MS,   1,                   0,    3,               0, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN,     NULL),
 
	SDT_CONDVAR(Patches,      generation_seed,          SLE_UINT32, 30, SL_MAX_VERSION, 0, 0,    GENERATE_NEW_SEED,   0, MAX_UVALUE(uint32), 0, STR_NULL,                                    NULL),
 
	SDT_CONDVAR(Patches,      tree_placer,              SLE_UINT8,  30, SL_MAX_VERSION, 0, MS,   2,                   0,    2,               0, STR_CONFIG_PATCHES_TREE_PLACER,              NULL),
 
	SDT_VAR    (Patches,      heightmap_rotation,       SLE_UINT8,                      S, MS,   0,                   0,    1,               0, STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION,       NULL),
 
	SDT_VAR    (Patches,      se_flat_world_height,     SLE_UINT8,                      S, 0,    0,                   0,   15,               0, STR_CONFIG_PATCHES_SE_FLAT_WORLD_HEIGHT,     NULL),
 

	
 
	SDT_END()
 
};
 

	
 
static const SettingDesc _currency_settings[] = {
 
	SDT_VAR(CurrencySpec, rate,    SLE_UINT16, S, 0,  1, 0, 100, 0, STR_NULL, NULL),
 
	SDT_CHR(CurrencySpec, separator,           S, 0,        ".",    STR_NULL, NULL),
 
	SDT_VAR(CurrencySpec, to_euro,  SLE_INT32, S, 0,  0, 0,1000, 0, STR_NULL, NULL),
 
	SDT_STR(CurrencySpec, prefix,   SLE_STRBQ, S, 0,       NULL,    STR_NULL, NULL),
 
	SDT_STR(CurrencySpec, suffix,   SLE_STRBQ, S, 0, " credits",    STR_NULL, NULL),
 
	SDT_END()
 
};
 

	
 
/* Undefine for the shortcut macros above */
 
#undef S
 
#undef C
 
#undef N
 

	
 
#undef D0
 
#undef NC
 
#undef MS
 
#undef NO
 
#undef CR
 

	
 

	
 
/* Load a GRF configuration from the given group name */
 
static GRFConfig *GRFLoadConfig(IniFile *ini, const char *grpname, bool is_static)
 
{
 
	IniGroup *group = ini_getgroup(ini, grpname, -1);
 
	IniItem *item;
 
	GRFConfig *first = NULL;
 
	GRFConfig **curr = &first;
 

	
 
	if (group == NULL) return NULL;
 

	
 
	for (item = group->item; item != NULL; item = item->next) {
 
		GRFConfig *c = calloc(1, sizeof(*c));
 
		c->filename = strdup(item->name);
 

	
 
		/* Parse parameters */
 
		if (*item->value != '\0') {
 
			c->num_params = parse_intlist(item->value, (int*)c->param, lengthof(c->param));
 
			if (c->num_params == (byte)-1) {
 
				ShowInfoF("ini: error in array '%s'", item->name);
 
				c->num_params = 0;
 
			}
 
		}
 

	
 
		/* Check if item is valid */
 
		if (!FillGRFDetails(c, is_static)) {
 
			const char *msg;
 

	
 
			if (HASBIT(c->flags, GCF_NOT_FOUND)) {
 
				msg = "not found";
 
			} else if (HASBIT(c->flags, GCF_UNSAFE)) {
 
				msg = "unsafe for static use";
 
			} else if (HASBIT(c->flags, GCF_SYSTEM)) {
 
				msg = "system NewGRF";
 
			} else {
 
				msg = "unknown";
 
			}
 

	
 
			ShowInfoF("ini: ignoring invalid NewGRF '%s': %s", item->name, msg);
 
			ClearGRFConfig(&c);
 
			continue;
 
		}
 

	
 
		/* Mark file as static to avoid saving in savegame. */
 
		if (is_static) SETBIT(c->flags, GCF_STATIC);
 

	
 
		/* Add item to list */
 
		*curr = c;
 
		curr = &c->next;
 
	}
 

	
 
	return first;
 
}
 

	
 

	
 
/* Save a GRF configuration to the given group name */
 
static void GRFSaveConfig(IniFile *ini, const char *grpname, const GRFConfig *list)
 
{
 
	IniGroup *group = ini_getgroup(ini, grpname, -1);
 
	IniItem **item;
 
	const GRFConfig *c;
 

	
 
	if (group == NULL) return;
 
	group->item = NULL;
 
	item = &group->item;
 

	
 
	for (c = list; c != NULL; c = c->next) {
 
		char params[512];
 
		GRFBuildParamList(params, c, lastof(params));
 

	
 
		*item = ini_item_alloc(group, c->filename, strlen(c->filename));
 
		(*item)->value = pool_strdup(&ini->pool, params, strlen(params));
 
		item = &(*item)->next;
 
	}
 
}
 

	
 
/* Common handler for saving/loading variables to the configuration file */
 
static void HandleSettingDescs(IniFile *ini, SettingDescProc *proc, SettingDescProcList *proc_list)
 
{
 
	proc(ini, (const SettingDesc*)_misc_settings,    "misc",  NULL);
 
	proc(ini, (const SettingDesc*)_music_settings,   "music", &msf);
 
#ifdef WIN32
 
	proc(ini, (const SettingDesc*)_win32_settings,   "win32", NULL);
 
#endif /* WIN32 */
 

	
 
	proc(ini, _gameopt_settings, "gameopt",  &_opt_newgame);
 
	proc(ini, _patch_settings,   "patches",  &_patches_newgame);
 
	proc(ini, _currency_settings,"currency", &_custom_currency);
 

	
 
#ifdef ENABLE_NETWORK
 
	proc(ini, (const SettingDesc*)_network_settings, "network", NULL);
 
	proc_list(ini, "servers", _network_host_list, lengthof(_network_host_list), NULL);
 
	proc_list(ini, "bans",    _network_ban_list,  lengthof(_network_ban_list), NULL);
 
#endif /* ENABLE_NETWORK */
 
}
 

	
 
/** Load the values from the configuration files */
 
void LoadFromConfig(void)
 
{
 
	IniFile *ini = ini_load(_config_file);
 
	HandleSettingDescs(ini, ini_load_settings, ini_load_setting_list);
 
	_grfconfig_newgame = GRFLoadConfig(ini, "newgrf", false);
 
	_grfconfig_static  = GRFLoadConfig(ini, "newgrf-static", true);
 
	ini_free(ini);
 
}
 

	
 
/** Save the values to the configuration file */
 
void SaveToConfig(void)
 
{
 
	IniFile *ini = ini_load(_config_file);
 
	HandleSettingDescs(ini, ini_save_settings, ini_save_setting_list);
 
	GRFSaveConfig(ini, "newgrf", _grfconfig_newgame);
 
	GRFSaveConfig(ini, "newgrf-static", _grfconfig_static);
 
	ini_save(_config_file, ini);
 
	ini_free(ini);
 
}
 

	
 
static const SettingDesc *GetSettingDescription(uint index)
 
{
 
	if (index >= lengthof(_patch_settings)) return NULL;
 
	return &_patch_settings[index];
 
}
 

	
 
/** Network-safe changing of patch-settings (server-only).
 
 * @param p1 the index of the patch in the SettingDesc array which identifies it
 
 * @param p2 the new value for the patch
 
 * The new value is properly clamped to its minimum/maximum when setting
 
 * @see _patch_settings
 
 */
 
int32 CmdChangePatchSetting(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	const SettingDesc *sd = GetSettingDescription(p1);
 

	
 
	if (sd == NULL) return CMD_ERROR;
 
	if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		Patches *patches_ptr = (_game_mode == GM_MENU) ? &_patches_newgame : &_patches;
 
		void *var = GetVariableAddress(patches_ptr, &sd->save);
 
		Write_ValidateSetting(var, sd, (int32)p2);
 
		if (sd->desc.proc != NULL) sd->desc.proc((int32)ReadValue(var, sd->save.conv));
 

	
 
		InvalidateWindow(WC_GAME_OPTIONS, 0);
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Top function to save the new value of an element of the Patches struct
 
 * @param index offset in the SettingDesc array of the Patches struct which
 
 * identifies the patch member we want to change
 
 * @param object pointer to a valid patches struct that has its settings change.
 
 * This only affects patch-members that are not needed to be the same on all
 
 * clients in a network game.
 
 * @param value new value of the patch */
 
bool SetPatchValue(uint index, const Patches *object, int32 value)
 
{
 
	const SettingDesc *sd = &_patch_settings[index];
 
	/* If an item is player-based, we do not send it over the network
 
	 * (if any) to change. Also *hack*hack* we update the _newgame version
 
	 * of patches because changing a player-based setting in a game also
 
	 * changes its defaults. At least that is the convention we have chosen */
 
	if (sd->save.conv & SLF_NETWORK_NO) {
 
		void *var = GetVariableAddress(object, &sd->save);
 
		Write_ValidateSetting(var, sd, value);
 

	
 
		if (_game_mode != GM_MENU) {
 
			void *var2 = GetVariableAddress(&_patches_newgame, &sd->save);
 
			Write_ValidateSetting(var2, sd, value);
 
		}
 
		if (sd->desc.proc != NULL) sd->desc.proc((int32)ReadValue(var, sd->save.conv));
 
		InvalidateWindow(WC_GAME_OPTIONS, 0);
 
		return true;
 
	}
 

	
 
	/* send non-player-based settings over the network */
 
	if (!_networking || (_networking && _network_server)) {
 
		return DoCommandP(0, index, value, NULL, CMD_CHANGE_PATCH_SETTING);
 
	}
 
	return false;
 
}
 

	
 
const SettingDesc *GetPatchFromName(const char *name, uint *i)
 
{
 
	const SettingDesc *sd;
 

	
 
	for (*i = 0, sd = _patch_settings; sd->save.cmd != SL_END; sd++, (*i)++) {
 
		if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
 
		if (strcmp(sd->desc.name, name) == 0) return sd;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/* Those 2 functions need to be here, else we have to make some stuff non-static
 
 * and besides, it is also better to keep stuff like this at the same place */
 
bool IConsoleSetPatchSetting(const char *name, int32 value)
 
{
 
	bool success;
 
	uint index;
 
	const SettingDesc *sd = GetPatchFromName(name, &index);
 
	const Patches *patches_ptr;
 
	void *ptr;
 

	
 
	if (sd == NULL) {
 
		IConsolePrintF(_icolour_warn, "'%s' is an unknown patch setting.", name);
 
		return true;
 
	}
 

	
 
	patches_ptr = (_game_mode == GM_MENU) ? &_patches_newgame : &_patches;
 
	ptr = GetVariableAddress(patches_ptr, &sd->save);
 

	
 
	success = SetPatchValue(index, patches_ptr, value);
 
	return success;
 
}
 

	
 
void IConsoleGetPatchSetting(const char *name)
 
{
 
	char value[20];
 
	uint index;
 
	const SettingDesc *sd = GetPatchFromName(name, &index);
 
	const void *ptr;
 

	
 
	if (sd == NULL) {
 
		IConsolePrintF(_icolour_warn, "'%s' is an unknown patch setting.", name);
 
		return;
 
	}
 

	
 
	ptr = GetVariableAddress((_game_mode == GM_MENU) ? &_patches_newgame : &_patches, &sd->save);
 

	
 
	if (sd->desc.cmd == SDT_BOOLX) {
 
		snprintf(value, sizeof(value), (*(bool*)ptr == 1) ? "on" : "off");
 
	} else {
 
		snprintf(value, sizeof(value), "%d", (int32)ReadValue(ptr, sd->save.conv));
 
	}
 

	
 
	IConsolePrintF(_icolour_warn, "Current value for '%s' is: '%s' (min: %s%d, max: %d)",
 
		name, value, (sd->desc.flags & SGF_0ISDISABLED) ? "(0) " : "", sd->desc.min, sd->desc.max);
 
}
 

	
 
/** Save and load handler for patches/settings
 
 * @param osd SettingDesc struct containing all information
 
 * @param object can be either NULL in which case we load global variables or
 
 * a pointer to a struct which is getting saved */
 
static void LoadSettings(const SettingDesc *osd, void *object)
 
{
 
	for (; osd->save.cmd != SL_END; osd++) {
 
		const SaveLoad *sld = &osd->save;
 
		void *ptr = GetVariableAddress(object, sld);
 

	
 
		if (!SlObjectMember(ptr, sld)) continue;
 
	}
 
}
 

	
 
/** Loadhandler for a list of global variables
 
 * @note this is actually a stub for LoadSettings with the
 
 * object pointer set to NULL */
 
static inline void LoadSettingsGlobList(const SettingDescGlobVarList *sdg)
 
{
 
	LoadSettings((const SettingDesc*)sdg, NULL);
 
}
 

	
 
/** Save and load handler for patches/settings
 
 * @param osd SettingDesc struct containing all information
 
 * @param object can be either NULL in which case we load global variables or
 
 * a pointer to a struct which is getting saved */
 
static void SaveSettings(const SettingDesc *sd, void *object)
 
{
 
	/* We need to write the CH_RIFF header, but unfortunately can't call
 
	 * SlCalcLength() because we have a different format. So do this manually */
 
	const SettingDesc *i;
 
	size_t length = 0;
 
	for (i = sd; i->save.cmd != SL_END; i++) {
 
		const void *ptr = GetVariableAddress(object, &i->save);
 
		length += SlCalcObjMemberLength(ptr, &i->save);
 
	}
 
	SlSetLength(length);
 

	
 
	for (i = sd; i->save.cmd != SL_END; i++) {
 
		void *ptr = GetVariableAddress(object, &i->save);
 
		SlObjectMember(ptr, &i->save);
 
	}
 
}
 

	
 
/** Savehandler for a list of global variables
 
 * @note this is actually a stub for SaveSettings with the
 
 * object pointer set to NULL */
 
static inline void SaveSettingsGlobList(const SettingDescGlobVarList *sdg)
 
{
 
	SaveSettings((const SettingDesc*)sdg, NULL);
 
}
 

	
 
static void Load_OPTS(void)
 
{
 
	/* Copy over default setting since some might not get loaded in
 
	 * a networking environment. This ensures for example that the local
 
	 * autosave-frequency stays when joining a network-server */
 
	_opt = _opt_newgame;
 
	LoadSettings(_gameopt_settings, &_opt);
 
}
 

	
 
static void Save_OPTS(void)
 
{
 
	SaveSettings(_gameopt_settings, &_opt);
 
}
 

	
 
static void Load_PATS(void)
 
{
 
	/* Copy over default setting since some might not get loaded in
 
	 * a networking environment. This ensures for example that the local
 
	 * signal_side stays when joining a network-server */
 
	_patches = _patches_newgame;
 
	LoadSettings(_patch_settings, &_patches);
 
}
 

	
 
static void Save_PATS(void)
 
{
 
	SaveSettings(_patch_settings, &_patches);
 
}
 

	
 
void CheckConfig(void)
 
{
 
	// fix up news_display_opt from old to new
 
	int i;
 
	uint32 tmp;
 
	for (i = 0, tmp = _news_display_opt; i != 10; i++, tmp >>= 2) {
 
		if ((tmp & 0x3) == 0x3) { // old settings
 
			_news_display_opt = 0xAAAAAAAA; // set all news-messages to full 1010101010...
 
			break;
 
		}
 
	}
 

	
 
	// Increase old default values for pf_maxdepth and pf_maxlength
 
	// to support big networks.
 
	if (_patches_newgame.pf_maxdepth == 16 && _patches_newgame.pf_maxlength == 512) {
 
		_patches_newgame.pf_maxdepth = 48;
 
		_patches_newgame.pf_maxlength = 4096;
 
	}
 
}
 

	
 
void UpdatePatches(void)
 
{
 
	/* Since old(er) savegames don't have any patches saved, we initialise
 
	 * them with the default values just as it was in the old days.
 
	 * Also new games need this copying-over */
 
	_patches = _patches_newgame; /* backwards compatibility */
 
}
 

	
 
const ChunkHandler _setting_chunk_handlers[] = {
 
	{ 'OPTS', Save_OPTS, Load_OPTS, CH_RIFF},
 
	{ 'PATS', Save_PATS, Load_PATS, CH_RIFF | CH_LAST},
 
};
 

	
 
static bool IsSignedVarMemType(VarType vt)
 
{
 
	switch (GetVarMemType(vt)) {
 
		case SLE_VAR_I8:
 
		case SLE_VAR_I16:
 
		case SLE_VAR_I32:
 
		case SLE_VAR_I64:
 
			return true;
 
	}
 
	return false;
 
}
src/settings_gui.c
Show inline comments
 
deleted file
src/settings_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "currency.h"
 
#include "functions.h"
 
#include "string.h"
 
#include "strings.h" // XXX GetCurrentCurrencyRate()
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "command.h"
 
#include "engine.h"
 
#include "screenshot.h"
 
#include "newgrf.h"
 
#include "network/network.h"
 
#include "town.h"
 
#include "variables.h"
 
#include "settings.h"
 
#include "vehicle.h"
 
#include "date.h"
 

	
 
static uint32 _difficulty_click_a;
 
static uint32 _difficulty_click_b;
 
static byte _difficulty_timeout;
 

	
 
static const StringID _units_dropdown[] = {
 
	STR_UNITS_IMPERIAL,
 
	STR_UNITS_METRIC,
 
	STR_UNITS_SI,
 
	INVALID_STRING_ID
 
};
 

	
 
static const StringID _driveside_dropdown[] = {
 
	STR_02E9_DRIVE_ON_LEFT,
 
	STR_02EA_DRIVE_ON_RIGHT,
 
	INVALID_STRING_ID
 
};
 

	
 
static const StringID _autosave_dropdown[] = {
 
	STR_02F7_OFF,
 
	STR_AUTOSAVE_1_MONTH,
 
	STR_02F8_EVERY_3_MONTHS,
 
	STR_02F9_EVERY_6_MONTHS,
 
	STR_02FA_EVERY_12_MONTHS,
 
	INVALID_STRING_ID,
 
};
 

	
 
static const StringID _designnames_dropdown[] = {
 
	STR_02BE_DEFAULT,
 
	STR_02BF_CUSTOM,
 
	INVALID_STRING_ID
 
};
 

	
 
static StringID *BuildDynamicDropdown(StringID base, int num)
 
{
 
	static StringID buf[32 + 1];
 
	StringID *p = buf;
 
	while (--num>=0) *p++ = base++;
 
	*p = INVALID_STRING_ID;
 
	return buf;
 
}
 

	
 
static int GetCurRes(void)
 
{
 
	int i;
 

	
 
	for (i = 0; i != _num_resolutions; i++) {
 
		if (_resolutions[i][0] == _screen.width &&
 
				_resolutions[i][1] == _screen.height) {
 
			break;
 
		}
 
	}
 
	return i;
 
}
 

	
 
static inline bool RoadVehiclesAreBuilt(void)
 
{
 
	const Vehicle* v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Road) return true;
 
	}
 
	return false;
 
}
 

	
 

	
 
static void ShowCustCurrency(void);
 

	
 
static void GameOptionsWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		int i;
 
		StringID str = STR_02BE_DEFAULT;
 

	
 
		SetWindowWidgetDisabledState(w, 21, !(_vehicle_design_names & 1));
 
		if (!IsWindowWidgetDisabled(w, 21)) str = STR_02BF_CUSTOM;
 
		SetDParam(0, str);
 
		SetDParam(1, _currency_specs[_opt_ptr->currency].name);
 
		SetDParam(2, STR_UNITS_IMPERIAL + _opt_ptr->units);
 
		SetDParam(3, STR_02E9_DRIVE_ON_LEFT + _opt_ptr->road_side);
 
		SetDParam(4, STR_TOWNNAME_ORIGINAL_ENGLISH + _opt_ptr->town_name);
 
		SetDParam(5, _autosave_dropdown[_opt_ptr->autosave]);
 
		SetDParam(6, SPECSTR_LANGUAGE_START + _dynlang.curr);
 
		i = GetCurRes();
 
		SetDParam(7, i == _num_resolutions ? STR_RES_OTHER : SPECSTR_RESOLUTION_START + i);
 
		SetDParam(8, SPECSTR_SCREENSHOT_START + _cur_screenshot_format);
 
		SetWindowWidgetLoweredState(w, 28, _fullscreen);
 

	
 
		DrawWindowWidgets(w);
 
		DrawString(20, 175, STR_OPTIONS_FULLSCREEN, 0); // fullscreen
 
	}	break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 4: case 5: /* Setup currencies dropdown */
 
			ShowDropDownMenu(w, BuildCurrencyDropdown(), _opt_ptr->currency, 5, _game_mode == GM_MENU ? 0 : ~GetMaskOfAllowedCurrencies(), 0);;
 
			return;
 
		case 7: case 8: /* Setup distance unit dropdown */
 
			ShowDropDownMenu(w, _units_dropdown, _opt_ptr->units, 8, 0, 0);
 
			return;
 
		case 10: case 11: { /* Setup road-side dropdown */
 
			int i = 0;
 

	
 
			/* You can only change the drive side if you are in the menu or ingame with
 
			 * no vehicles present. In a networking game only the server can change it */
 
			if ((_game_mode != GM_MENU && RoadVehiclesAreBuilt()) || (_networking && !_network_server))
 
				i = (-1) ^ (1 << _opt_ptr->road_side); // disable the other value
 

	
 
			ShowDropDownMenu(w, _driveside_dropdown, _opt_ptr->road_side, 11, i, 0);
 
		} return;
 
		case 13: case 14: { /* Setup townname dropdown */
 
			int i = _opt_ptr->town_name;
 
			ShowDropDownMenu(w, BuildDynamicDropdown(STR_TOWNNAME_ORIGINAL_ENGLISH, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1), i, 14, (_game_mode == GM_MENU) ? 0 : (-1) ^ (1 << i), 0);
 
			return;
 
		}
 
		case 16: case 17: /* Setup autosave dropdown */
 
			ShowDropDownMenu(w, _autosave_dropdown, _opt_ptr->autosave, 17, 0, 0);
 
			return;
 
		case 19: case 20: /* Setup customized vehicle-names dropdown */
 
			ShowDropDownMenu(w, _designnames_dropdown, (_vehicle_design_names & 1) ? 1 : 0, 20, (_vehicle_design_names & 2) ? 0 : 2, 0);
 
			return;
 
		case 21: /* Save customized vehicle-names to disk */
 
			return;
 
		case 23: case 24: /* Setup interface language dropdown */
 
			ShowDropDownMenu(w, _dynlang.dropdown, _dynlang.curr, 24, 0, 0);
 
			return;
 
		case 26: case 27: /* Setup resolution dropdown */
 
			ShowDropDownMenu(w, BuildDynamicDropdown(SPECSTR_RESOLUTION_START, _num_resolutions), GetCurRes(), 27, 0, 0);
 
			return;
 
		case 28: /* Click fullscreen on/off */
 
			SetWindowWidgetLoweredState(w, 28, !_fullscreen);
 
			ToggleFullScreen(!_fullscreen); // toggle full-screen on/off
 
			SetWindowDirty(w);
 
			return;
 
		case 30: case 31: /* Setup screenshot format dropdown */
 
			ShowDropDownMenu(w, BuildDynamicDropdown(SPECSTR_SCREENSHOT_START, _num_screenshot_formats), _cur_screenshot_format, 31, 0, 0);
 
			return;
 
		}
 
		break;
 

	
 
	case WE_DROPDOWN_SELECT:
 
		switch (e->we.dropdown.button) {
 
		case 20: /* Vehicle design names */
 
			if (e->we.dropdown.index == 0) {
 
				DeleteCustomEngineNames();
 
				MarkWholeScreenDirty();
 
			} else if (!(_vehicle_design_names & 1)) {
 
				LoadCustomEngineNames();
 
				MarkWholeScreenDirty();
 
			}
 
			break;
 
		case 5: /* Currency */
 
			if (e->we.dropdown.index == CUSTOM_CURRENCY_ID) ShowCustCurrency();
 
			_opt_ptr->currency = e->we.dropdown.index;
 
			MarkWholeScreenDirty();
 
			break;
 
		case 8: /* Measuring units */
 
			_opt_ptr->units = e->we.dropdown.index;
 
			MarkWholeScreenDirty();
 
			break;
 
		case 11: /* Road side */
 
			if (_opt_ptr->road_side != e->we.dropdown.index) { // only change if setting changed
 
				DoCommandP(0, e->we.dropdown.index, 0, NULL, CMD_SET_ROAD_DRIVE_SIDE | CMD_MSG(STR_00B4_CAN_T_DO_THIS));
 
				MarkWholeScreenDirty();
 
			}
 
			break;
 
		case 14: /* Town names */
 
			if (_game_mode == GM_MENU) {
 
				_opt_ptr->town_name = e->we.dropdown.index;
 
				InvalidateWindow(WC_GAME_OPTIONS, 0);
 
			}
 
			break;
 
		case 17: /* Autosave options */
 
			_opt.autosave = _opt_newgame.autosave = e->we.dropdown.index;
 
			SetWindowDirty(w);
 
			break;
 
		case 24: /* Change interface language */
 
			ReadLanguagePack(e->we.dropdown.index);
 
			MarkWholeScreenDirty();
 
			break;
 
		case 27: /* Change resolution */
 
			if (e->we.dropdown.index < _num_resolutions && ChangeResInGame(_resolutions[e->we.dropdown.index][0],_resolutions[e->we.dropdown.index][1]))
 
				SetWindowDirty(w);
 
			break;
 
		case 31: /* Change screenshot format */
 
			SetScreenshotFormat(e->we.dropdown.index);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_DESTROY:
 
		DeleteWindowById(WC_CUSTOM_CURRENCY, 0);
 
		break;
 
	}
 

	
 
}
 

	
 
/** Change the side of the road vehicles drive on (server only).
 
 * @param tile unused
 
 * @param p1 the side of the road; 0 = left side and 1 = right side
 
 * @param p2 unused
 
 */
 
int32 CmdSetRoadDriveSide(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	/* Check boundaries and you can only change this if NO vehicles have been built yet,
 
	 * except in the intro-menu where of course it's always possible to do so. */
 
	if (p1 > 1 || (_game_mode != GM_MENU && RoadVehiclesAreBuilt())) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		_opt_ptr->road_side = p1;
 
		InvalidateWindow(WC_GAME_OPTIONS,0);
 
	}
 
	return 0;
 
}
 

	
 
static const Widget _game_options_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                          STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   369,     0,    13, STR_00B1_GAME_OPTIONS,             STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   369,    14,   238, 0x0,                               STR_NULL},
 
{      WWT_FRAME,   RESIZE_NONE,    14,    10,   179,    20,    55, STR_02E0_CURRENCY_UNITS,           STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    14,    20,   169,    34,    45, STR_02E1,                          STR_02E2_CURRENCY_UNITS_SELECTION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   158,   168,    35,    44, STR_0225,                          STR_02E2_CURRENCY_UNITS_SELECTION},
 
{      WWT_FRAME,   RESIZE_NONE,    14,   190,   359,    20,    55, STR_MEASURING_UNITS,               STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    14,   200,   349,    34,    45, STR_02E4,                          STR_MEASURING_UNITS_SELECTION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   338,   348,    35,    44, STR_0225,                          STR_MEASURING_UNITS_SELECTION},
 
{      WWT_FRAME,   RESIZE_NONE,    14,    10,   179,    62,    97, STR_02E6_ROAD_VEHICLES,            STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    14,    20,   169,    76,    87, STR_02E7,                          STR_02E8_SELECT_SIDE_OF_ROAD_FOR},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   158,   168,    77,    86, STR_0225,                          STR_02E8_SELECT_SIDE_OF_ROAD_FOR},
 
{      WWT_FRAME,   RESIZE_NONE,    14,   190,   359,    62,    97, STR_02EB_TOWN_NAMES,               STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    14,   200,   349,    76,    87, STR_02EC,                          STR_02ED_SELECT_STYLE_OF_TOWN_NAMES},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   338,   348,    77,    86, STR_0225,                          STR_02ED_SELECT_STYLE_OF_TOWN_NAMES},
 
{      WWT_FRAME,   RESIZE_NONE,    14,    10,   179,   104,   139, STR_02F4_AUTOSAVE,                 STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    14,    20,   169,   118,   129, STR_02F5,                          STR_02F6_SELECT_INTERVAL_BETWEEN},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   158,   168,   119,   128, STR_0225,                          STR_02F6_SELECT_INTERVAL_BETWEEN},
 

	
 
{      WWT_FRAME,   RESIZE_NONE,    14,    10,   359,   194,   228, STR_02BC_VEHICLE_DESIGN_NAMES,     STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    14,    20,   119,   207,   218, STR_02BD,                          STR_02C1_VEHICLE_DESIGN_NAMES_SELECTION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   108,   118,   208,   217, STR_0225,                          STR_02C1_VEHICLE_DESIGN_NAMES_SELECTION},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   130,   349,   207,   218, STR_02C0_SAVE_CUSTOM_NAMES,        STR_02C2_SAVE_CUSTOMIZED_VEHICLE},
 

	
 
{      WWT_FRAME,   RESIZE_NONE,    14,   190,   359,   104,   139, STR_OPTIONS_LANG,                  STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    14,   200,   349,   118,   129, STR_OPTIONS_LANG_CBO,              STR_OPTIONS_LANG_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   338,   348,   119,   128, STR_0225,                          STR_OPTIONS_LANG_TIP},
 

	
 
{      WWT_FRAME,   RESIZE_NONE,    14,    10,   179,   146,   190, STR_OPTIONS_RES,                   STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    14,    20,   169,   160,   171, STR_OPTIONS_RES_CBO,               STR_OPTIONS_RES_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   158,   168,   161,   170, STR_0225,                          STR_OPTIONS_RES_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   149,   169,   176,   184, STR_EMPTY,                         STR_OPTIONS_FULLSCREEN_TIP},
 

	
 
{      WWT_FRAME,   RESIZE_NONE,    14,   190,   359,   146,   190, STR_OPTIONS_SCREENSHOT_FORMAT,     STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    14,   200,   349,   160,   171, STR_OPTIONS_SCREENSHOT_FORMAT_CBO, STR_OPTIONS_SCREENSHOT_FORMAT_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   338,   348,   161,   170, STR_0225,                          STR_OPTIONS_SCREENSHOT_FORMAT_TIP},
 

	
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _game_options_desc = {
 
	WDP_CENTER, WDP_CENTER, 370, 239,
 
	WC_GAME_OPTIONS,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_game_options_widgets,
 
	GameOptionsWndProc
 
};
 

	
 

	
 
void ShowGameOptions(void)
 
{
 
	DeleteWindowById(WC_GAME_OPTIONS, 0);
 
	AllocateWindowDesc(&_game_options_desc);
 
}
 

	
 
typedef struct {
 
	int16 min;
 
	int16 max;
 
	int16 step;
 
	StringID str;
 
} GameSettingData;
 

	
 
static const GameSettingData _game_setting_info[] = {
 
	{  0,   7,  1, STR_NULL},
 
	{  0,   3,  1, STR_6830_IMMEDIATE},
 
	{  0,   2,  1, STR_6816_LOW},
 
	{  0,   3,  1, STR_26816_NONE},
 
	{100, 500, 50, STR_NULL},
 
	{  2,   4,  1, STR_NULL},
 
	{  0,   2,  1, STR_6820_LOW},
 
	{  0,   4,  1, STR_681B_VERY_SLOW},
 
	{  0,   2,  1, STR_6820_LOW},
 
	{  0,   2,  1, STR_6823_NONE},
 
	{  0,   3,  1, STR_6826_X1_5},
 
	{  0,   2,  1, STR_6820_LOW},
 
	{  0,   3,  1, STR_682A_VERY_FLAT},
 
	{  0,   3,  1, STR_VERY_LOW},
 
	{  0,   1,  1, STR_682E_STEADY},
 
	{  0,   1,  1, STR_6834_AT_END_OF_LINE_AND_AT_STATIONS},
 
	{  0,   1,  1, STR_6836_OFF},
 
	{  0,   2,  1, STR_6839_PERMISSIVE},
 
};
 

	
 
static inline bool GetBitAndShift(uint32 *b)
 
{
 
	uint32 x = *b;
 
	*b >>= 1;
 
	return HASBIT(x, 0);
 
}
 

	
 
/*
 
 * A: competitors
 
 * B: start time in months / 3
 
 * C: town count (2 = high, 0 = low)
 
 * D: industry count (3 = high, 0 = none)
 
 * E: inital loan / 1000 (in GBP)
 
 * F: interest rate
 
 * G: running costs (0 = low, 2 = high)
 
 * H: construction speed of competitors (0 = very slow, 4 = very fast)
 
 * I: intelligence (0-2)
 
 * J: breakdowns (0 = off, 2 = normal)
 
 * K: subsidy multiplier (0 = 1.5, 3 = 4.0)
 
 * L: construction cost (0-2)
 
 * M: terrain type (0 = very flat, 3 = mountainous)
 
 * N: amount of water (0 = very low, 3 = high)
 
 * O: economy (0 = steady, 1 = fluctuating)
 
 * P: Train reversing (0 = end of line + stations, 1 = end of line)
 
 * Q: disasters
 
 * R: area restructuring (0 = permissive, 2 = hostile)
 
 */
 
static const int16 _default_game_diff[3][GAME_DIFFICULTY_NUM] = { /*
 
	 A, B, C, D,   E, F, G, H, I, J, K, L, M, N, O, P, Q, R*/
 
	{2, 2, 1, 3, 300, 2, 0, 2, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0}, //easy
 
	{4, 1, 1, 2, 150, 3, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1}, //medium
 
	{7, 0, 2, 2, 100, 4, 1, 3, 2, 2, 0, 2, 3, 2, 1, 1, 1, 2}, //hard
 
};
 

	
 
void SetDifficultyLevel(int mode, GameOptions *gm_opt)
 
{
 
	int i;
 
	assert(mode <= 3);
 

	
 
	gm_opt->diff_level = mode;
 
	if (mode != 3) { // not custom
 
		for (i = 0; i != GAME_DIFFICULTY_NUM; i++)
 
			((int*)&gm_opt->diff)[i] = _default_game_diff[mode][i];
 
	}
 
}
 

	
 
extern void StartupEconomy(void);
 

	
 
enum {
 
	GAMEDIFF_WND_TOP_OFFSET = 45,
 
	GAMEDIFF_WND_ROWSIZE    = 9
 
};
 

	
 
// Temporary holding place of values in the difficulty window until 'Save' is clicked
 
static GameOptions _opt_mod_temp;
 
// 0x383E = (1 << 13) | (1 << 12) | (1 << 11) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1)
 
#define DIFF_INGAME_DISABLED_BUTTONS 0x383E
 

	
 
static void GameDifficultyWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: // Setup disabled buttons when creating window
 
		/* disable all other difficulty buttons during gameplay except for 'custom' */
 
		SetWindowWidgetDisabledState(w,  3, _game_mode == GM_NORMAL);
 
		SetWindowWidgetDisabledState(w,  4, _game_mode == GM_NORMAL);
 
		SetWindowWidgetDisabledState(w,  5, _game_mode == GM_NORMAL);
 
		SetWindowWidgetDisabledState(w,  6, _game_mode == GM_NORMAL);
 
		SetWindowWidgetDisabledState(w,  7, _game_mode == GM_EDITOR || _networking); // highscore chart in multiplayer
 
		SetWindowWidgetDisabledState(w, 10, _networking && !_network_server); // Save-button in multiplayer (and if client)
 

	
 
		break;
 
	case WE_PAINT: {
 
		uint32 click_a, click_b, disabled;
 
		int i;
 
		int y, value;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		click_a = _difficulty_click_a;
 
		click_b = _difficulty_click_b;
 

	
 
		/* XXX - Disabled buttons in normal gameplay. Bitshifted for each button to see if
 
		 * that bit is set. If it is set, the button is disabled */
 
		disabled = (_game_mode == GM_NORMAL) ? DIFF_INGAME_DISABLED_BUTTONS : 0;
 

	
 
		y = GAMEDIFF_WND_TOP_OFFSET;
 
		for (i = 0; i != GAME_DIFFICULTY_NUM; i++) {
 
			DrawFrameRect( 5, y,  5 + 8, y + 8, 3, GetBitAndShift(&click_a) ? FR_LOWERED : 0);
 
			DrawFrameRect(15, y, 15 + 8, y + 8, 3, GetBitAndShift(&click_b) ? FR_LOWERED : 0);
 
			if (GetBitAndShift(&disabled) || (_networking && !_network_server)) {
 
				int color = PALETTE_MODIFIER_GREYOUT | _colour_gradient[COLOUR_YELLOW][2];
 
				GfxFillRect( 6, y + 1,  6 + 8, y + 8, color);
 
				GfxFillRect(16, y + 1, 16 + 8, y + 8, color);
 
			}
 

	
 
			DrawStringCentered(10, y, STR_6819, 0);
 
			DrawStringCentered(20, y, STR_681A, 0);
 

	
 

	
 
			value = _game_setting_info[i].str + ((int*)&_opt_mod_temp.diff)[i];
 
			if (i == 4) value *= 1000; // XXX - handle currency option
 
			SetDParam(0, value);
 
			DrawString(30, y, STR_6805_MAXIMUM_NO_COMPETITORS + i, 0);
 

	
 
			y += GAMEDIFF_WND_ROWSIZE + 2; // space items apart a bit
 
		}
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 8: { /* Difficulty settings widget, decode click */
 
			const GameSettingData *info;
 
			int x, y;
 
			uint btn, dis;
 
			int val;
 

	
 
			// Don't allow clients to make any changes
 
			if  (_networking && !_network_server)
 
				return;
 

	
 
			x = e->we.click.pt.x - 5;
 
			if (!IS_INT_INSIDE(x, 0, 21)) // Button area
 
				return;
 

	
 
			y = e->we.click.pt.y - GAMEDIFF_WND_TOP_OFFSET;
 
			if (y < 0)
 
				return;
 

	
 
			// Get button from Y coord.
 
			btn = y / (GAMEDIFF_WND_ROWSIZE + 2);
 
			if (btn >= GAME_DIFFICULTY_NUM || y % (GAMEDIFF_WND_ROWSIZE + 2) >= 9)
 
				return;
 

	
 
			// Clicked disabled button?
 
			dis = (_game_mode == GM_NORMAL) ? DIFF_INGAME_DISABLED_BUTTONS : 0;
 

	
 
			if (HASBIT(dis, btn))
 
				return;
 

	
 
			_difficulty_timeout = 5;
 

	
 
			val = ((int*)&_opt_mod_temp.diff)[btn];
 

	
 
			info = &_game_setting_info[btn]; // get information about the difficulty setting
 
			if (x >= 10) {
 
				// Increase button clicked
 
				val = min(val + info->step, info->max);
 
				SETBIT(_difficulty_click_b, btn);
 
			} else {
 
				// Decrease button clicked
 
				val = max(val - info->step, info->min);
 
				SETBIT(_difficulty_click_a, btn);
 
			}
 

	
 
			// save value in temporary variable
 
			((int*)&_opt_mod_temp.diff)[btn] = val;
 
			SetDifficultyLevel(3, &_opt_mod_temp); // set difficulty level to custom
 
			SetWindowDirty(w);
 
		}	break;
 
		case 3: case 4: case 5: case 6: /* Easy / Medium / Hard / Custom */
 
			// temporarily change difficulty level
 
			RaiseWindowWidget(w, _opt_mod_temp.diff_level + 3);
 
			SetDifficultyLevel(e->we.click.widget - 3, &_opt_mod_temp);
 
			LowerWindowWidget(w, _opt_mod_temp.diff_level + 3);
 
			SetWindowDirty(w);
 
			break;
 
		case 7: /* Highscore Table */
 
			ShowHighscoreTable(_opt_mod_temp.diff_level, -1);
 
			break;
 
		case 10: { /* Save button - save changes */
 
			int btn, val;
 
			for (btn = 0; btn != GAME_DIFFICULTY_NUM; btn++) {
 
				val = ((int*)&_opt_mod_temp.diff)[btn];
 
				// if setting has changed, change it
 
				if (val != ((int*)&_opt_ptr->diff)[btn])
 
					DoCommandP(0, btn, val, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
 
			}
 
			DoCommandP(0, -1, _opt_mod_temp.diff_level, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
 
			DeleteWindow(w);
 
			// If we are in the editor, we should reload the economy.
 
			//  This way when you load a game, the max loan and interest rate
 
			//  are loaded correctly.
 
			if (_game_mode == GM_EDITOR)
 
				StartupEconomy();
 
			break;
 
		}
 
		case 11: /* Cancel button - close window, abandon changes */
 
			DeleteWindow(w);
 
			break;
 
	} break;
 

	
 
	case WE_MOUSELOOP: /* Handle the visual 'clicking' of the buttons */
 
		if (_difficulty_timeout != 0 && !--_difficulty_timeout) {
 
			_difficulty_click_a = 0;
 
			_difficulty_click_b = 0;
 
			SetWindowDirty(w);
 
		}
 
		break;
 
	}
 
}
 

	
 
#undef DIFF_INGAME_DISABLED_BUTTONS
 

	
 
static const Widget _game_difficulty_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    10,     0,    10,     0,    13, STR_00C5,                     STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    10,    11,   369,     0,    13, STR_6800_DIFFICULTY_LEVEL,    STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    10,     0,   369,    14,    29, 0x0,                          STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    10,    96,    16,    27, STR_6801_EASY,                STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,    97,   183,    16,    27, STR_6802_MEDIUM,              STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,   184,   270,    16,    27, STR_6803_HARD,                STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,   271,   357,    16,    27, STR_6804_CUSTOM,              STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    10,     0,   369,    30,    41, STR_6838_SHOW_HI_SCORE_CHART, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    10,     0,   369,    42,   262, 0x0,                          STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    10,     0,   369,   263,   278, 0x0,                          STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,   105,   185,   265,   276, STR_OPTIONS_SAVE_CHANGES,     STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,   186,   266,   265,   276, STR_012E_CANCEL,              STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _game_difficulty_desc = {
 
	WDP_CENTER, WDP_CENTER, 370, 279,
 
	WC_GAME_OPTIONS,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_game_difficulty_widgets,
 
	GameDifficultyWndProc
 
};
 

	
 
void ShowGameDifficulty(void)
 
{
 
	DeleteWindowById(WC_GAME_OPTIONS, 0);
 
	/* Copy current settings (ingame or in intro) to temporary holding place
 
	 * change that when setting stuff, copy back on clicking 'OK' */
 
	_opt_mod_temp = *_opt_ptr;
 
	AllocateWindowDesc(&_game_difficulty_desc);
 
}
 

	
 
static const char *_patches_ui[] = {
 
	"vehicle_speed",
 
	"status_long_date",
 
	"show_finances",
 
	"autoscroll",
 
	"reverse_scroll",
 
	"errmsg_duration",
 
	"toolbar_pos",
 
	"measure_tooltip",
 
	"window_snap_radius",
 
	"invisible_trees",
 
	"population_in_label",
 
	"link_terraform_toolbar",
 
	"liveries",
 
	"prefer_teamchat",
 
};
 

	
 
static const char *_patches_construction[] = {
 
	"build_on_slopes",
 
	"extra_dynamite",
 
	"longbridges",
 
	"signal_side",
 
	"always_small_airport",
 
	"drag_signals_density",
 
	"oil_refinery_limit",
 
};
 

	
 
static const char *_patches_stations[] = {
 
	"join_stations",
 
	"full_load_any",
 
	"improved_load",
 
	"selectgoods",
 
	"new_nonstop",
 
	"nonuniform_stations",
 
	"station_spread",
 
	"serviceathelipad",
 
	"modified_catchment",
 
	"gradual_loading",
 
};
 

	
 
static const char *_patches_economy[] = {
 
	"inflation",
 
	"build_rawmaterial_ind",
 
	"multiple_industry_per_town",
 
	"same_industry_close",
 
	"bribe",
 
	"colored_news_year",
 
	"ending_year",
 
	"smooth_economy",
 
	"allow_shares",
 
};
 

	
 
static const char *_patches_ai[] = {
 
	"ainew_active",
 
	"ai_in_multiplayer",
 
	"ai_disable_veh_train",
 
	"ai_disable_veh_roadveh",
 
	"ai_disable_veh_aircraft",
 
	"ai_disable_veh_ship",
 
};
 

	
 
static const char *_patches_vehicles[] = {
 
	"realistic_acceleration",
 
	"forbid_90_deg",
 
	"mammoth_trains",
 
	"gotodepot",
 
	"roadveh_queue",
 
	"new_pathfinding_all",
 
	"yapf.ship_use_yapf",
 
	"yapf.road_use_yapf",
 
	"yapf.rail_use_yapf",
 
	"train_income_warn",
 
	"order_review_system",
 
	"never_expire_vehicles",
 
	"lost_train_warn",
 
	"autorenew",
 
	"autorenew_months",
 
	"autorenew_money",
 
	"max_trains",
 
	"max_roadveh",
 
	"max_aircraft",
 
	"max_ships",
 
	"servint_ispercent",
 
	"servint_trains",
 
	"servint_roadveh",
 
	"servint_ships",
 
	"servint_aircraft",
 
	"no_servicing_if_no_breakdowns",
 
	"wagon_speed_limits",
 
	"disable_elrails",
 
	"freight_trains",
 
};
 

	
 
typedef struct PatchEntry {
 
	const SettingDesc *setting;
 
	uint index;
 
} PatchEntry;
 

	
 
typedef struct PatchPage {
 
	const char **names;
 
	PatchEntry *entries;
 
	byte num;
 
} PatchPage;
 

	
 
/* PatchPage holds the categories, the number of elements in each category
 
 * and (in NULL) a dynamic array of settings based on the string-representations
 
 * of the settings. This way there is no worry about indeces, and such */
 
static PatchPage _patches_page[] = {
 
	{_patches_ui,           NULL, lengthof(_patches_ui)},
 
	{_patches_construction, NULL, lengthof(_patches_construction)},
 
	{_patches_vehicles,     NULL, lengthof(_patches_vehicles)},
 
	{_patches_stations,     NULL, lengthof(_patches_stations)},
 
	{_patches_economy,      NULL, lengthof(_patches_economy)},
 
	{_patches_ai,           NULL, lengthof(_patches_ai)},
 
};
 

	
 
/** The main patches window. Shows a number of categories on top and
 
 * a selection of patches in that category.
 
 * Uses WP(w, def_d) macro - data_1, data_2, data_3 */
 
static void PatchesSelectionWndProc(Window *w, WindowEvent *e)
 
{
 
	static Patches *patches_ptr;
 

	
 
	switch (e->event) {
 
	case WE_CREATE: {
 
		static bool first_time = true;
 

	
 
		patches_ptr = (_game_mode == GM_MENU) ? &_patches_newgame : &_patches;
 

	
 
		/* Build up the dynamic settings-array only once per OpenTTD session */
 
		if (first_time) {
 
			PatchPage *page;
 
			for (page = &_patches_page[0]; page != endof(_patches_page); page++) {
 
				uint i;
 

	
 
				page->entries = malloc(page->num * sizeof(*page->entries));
 
				for (i = 0; i != page->num; i++) {
 
					uint index;
 
					const SettingDesc *sd = GetPatchFromName(page->names[i], &index);
 
					assert(sd != NULL);
 

	
 
					page->entries[i].setting = sd;
 
					page->entries[i].index = index;
 
				}
 
			}
 
			first_time = false;
 
		}
 
		LowerWindowWidget(w, 4);
 
	} break;
 

	
 
	case WE_PAINT: {
 
		int x, y;
 
		const PatchPage *page = &_patches_page[WP(w,def_d).data_1];
 
		uint i;
 

	
 
		/* Set up selected category */
 
		DrawWindowWidgets(w);
 

	
 
		x = 5;
 
		y = 47;
 
		for (i = 0; i != page->num; i++) {
 
			const SettingDesc *sd = page->entries[i].setting;
 
			const SettingDescBase *sdb = &sd->desc;
 
			const void *var = GetVariableAddress(patches_ptr, &sd->save);
 
			bool editable = true;
 
			bool disabled = false;
 

	
 
			// We do not allow changes of some items when we are a client in a networkgame
 
			if (!(sd->save.conv & SLF_NETWORK_NO) && _networking && !_network_server) editable = false;
 
			if ((sdb->flags & SGF_NETWORK_ONLY) && !_networking) editable = false;
 

	
 
			if (sdb->cmd == SDT_BOOLX) {
 
				static const int _bool_ctabs[2][2] = {{9, 4}, {7, 6}};
 
				/* Draw checkbox for boolean-value either on/off */
 
				bool on = (*(bool*)var);
 

	
 
				DrawFrameRect(x, y, x + 19, y + 8, _bool_ctabs[!!on][!!editable], on ? FR_LOWERED : 0);
 
				SetDParam(0, on ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF);
 
			} else {
 
				int32 value;
 

	
 
				value = (int32)ReadValue(var, sd->save.conv);
 

	
 
				/* Draw [<][>] boxes for settings of an integer-type */
 
				DrawArrowButtons(x, y, 3, WP(w,def_d).data_2 - (i * 2), (editable && value != sdb->min), (editable && value != sdb->max));
 

	
 
				disabled = (value == 0) && (sdb->flags & SGF_0ISDISABLED);
 
				if (disabled) {
 
					SetDParam(0, STR_CONFIG_PATCHES_DISABLED);
 
				} else {
 
					if (sdb->flags & SGF_CURRENCY) {
 
						SetDParam(0, STR_CONFIG_PATCHES_CURRENCY);
 
					} else if (sdb->flags & SGF_MULTISTRING) {
 
						SetDParam(0, sdb->str + value + 1);
 
					} else {
 
						SetDParam(0, (sdb->flags & SGF_NOCOMMA) ? STR_CONFIG_PATCHES_INT32 : STR_7024);
 
					}
 
					SetDParam(1, value);
 
				}
 
			}
 
			DrawString(30, y, (sdb->str) + disabled, 0);
 
			y += 11;
 
		}
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 3: {
 
			const PatchPage *page = &_patches_page[WP(w,def_d).data_1];
 
			const SettingDesc *sd;
 
			void *var;
 
			int32 value;
 
			int x, y;
 
			byte btn;
 

	
 
			y = e->we.click.pt.y - 46 - 1;
 
			if (y < 0) return;
 

	
 
			x = e->we.click.pt.x - 5;
 
			if (x < 0) return;
 

	
 
			btn = y / 11;
 
			if (y % 11 > 9) return;
 
			if (btn >= page->num) return;
 

	
 
			sd = page->entries[btn].setting;
 

	
 
			/* return if action is only active in network, or only settable by server */
 
			if ((sd->desc.flags & SGF_NETWORK_ONLY) && !_networking) return;
 
			if (!(sd->save.conv & SLF_NETWORK_NO) && _networking && !_network_server) return;
 

	
 
			var = GetVariableAddress(patches_ptr, &sd->save);
 
			value = (int32)ReadValue(var, sd->save.conv);
 

	
 
			/* clicked on the icon on the left side. Either scroller or bool on/off */
 
			if (x < 21) {
 
				const SettingDescBase *sdb = &sd->desc;
 
				int32 oldvalue = value;
 

	
 
				switch (sdb->cmd) {
 
				case SDT_BOOLX: value ^= 1; break;
 
				case SDT_NUMX: {
 
					/* Add a dynamic step-size to the scroller. In a maximum of
 
					 * 50-steps you should be able to get from min to max,
 
					 * unless specified otherwise in the 'interval' variable
 
					 * of the current patch. */
 
					uint32 step = (sdb->interval == 0) ? ((sdb->max - sdb->min) / 50) : sdb->interval;
 
					if (step == 0) step = 1;
 

	
 
					// don't allow too fast scrolling
 
					if ((w->flags4 & WF_TIMEOUT_MASK) > 2 << WF_TIMEOUT_SHL) {
 
						_left_button_clicked = false;
 
						return;
 
					}
 

	
 
					/* Increase or decrease the value and clamp it to extremes */
 
					if (x >= 10) {
 
						value += step;
 
						if (value > sdb->max) value = sdb->max;
 
					} else {
 
						value -= step;
 
						if (value < sdb->min) value = (sdb->flags & SGF_0ISDISABLED) ? 0 : sdb->min;
 
					}
 

	
 
					/* Set up scroller timeout */
 
					if (value != oldvalue) {
 
						WP(w,def_d).data_2 = btn * 2 + 1 + ((x >= 10) ? 1 : 0);
 
						w->flags4 |= 5 << WF_TIMEOUT_SHL;
 
						_left_button_clicked = false;
 
					}
 
				} break;
 
				default: NOT_REACHED();
 
				}
 

	
 
				if (value != oldvalue) {
 
					SetPatchValue(page->entries[btn].index, patches_ptr, value);
 
					SetWindowDirty(w);
 
				}
 
			} else {
 
				/* only open editbox for types that its sensible for */
 
				if (sd->desc.cmd != SDT_BOOLX && !(sd->desc.flags & SGF_MULTISTRING)) {
 
					/* Show the correct currency-translated value */
 
					if (sd->desc.flags & SGF_CURRENCY) value *= _currency->rate;
 

	
 
					WP(w,def_d).data_3 = btn;
 
					SetDParam(0, value);
 
					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_PATCHES_QUERY_CAPT, 10, 100, w, CS_NUMERAL);
 
				}
 
			}
 

	
 
			break;
 
		}
 
		case 4: case 5: case 6: case 7: case 8: case 9:
 
			RaiseWindowWidget(w, WP(w, def_d).data_1 + 4);
 
			WP(w, def_d).data_1 = e->we.click.widget - 4;
 
			LowerWindowWidget(w, WP(w, def_d).data_1 + 4);
 
			DeleteWindowById(WC_QUERY_STRING, 0);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_TIMEOUT:
 
		WP(w,def_d).data_2 = 0;
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
		if (e->we.edittext.str != NULL) {
 
			const PatchEntry *pe = &_patches_page[WP(w,def_d).data_1].entries[WP(w,def_d).data_3];
 
			const SettingDesc *sd = pe->setting;
 
			int32 value = atoi(e->we.edittext.str);
 

	
 
			/* Save the correct currency-translated value */
 
			if (sd->desc.flags & SGF_CURRENCY) value /= _currency->rate;
 

	
 
			SetPatchValue(pe->index, patches_ptr, value);
 
			SetWindowDirty(w);
 
		}
 
		break;
 
	}
 

	
 
	case WE_DESTROY:
 
		DeleteWindowById(WC_QUERY_STRING, 0);
 
		break;
 
	}
 
}
 

	
 
static const Widget _patches_selection_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    10,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    10,    11,   369,     0,    13, STR_CONFIG_PATCHES_CAPTION,      STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    10,     0,   369,    14,    41, 0x0,                             STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    10,     0,   369,    42,   370, 0x0,                             STR_NULL},
 

	
 
{    WWT_TEXTBTN,   RESIZE_NONE,     3,    10,    96,    16,    27, STR_CONFIG_PATCHES_GUI,          STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,     3,    97,   183,    16,    27, STR_CONFIG_PATCHES_CONSTRUCTION, STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,     3,   184,   270,    16,    27, STR_CONFIG_PATCHES_VEHICLES,     STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,     3,   271,   357,    16,    27, STR_CONFIG_PATCHES_STATIONS,     STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,     3,    10,    96,    28,    39, STR_CONFIG_PATCHES_ECONOMY,      STR_NULL},
 
{    WWT_TEXTBTN,   RESIZE_NONE,     3,    97,   183,    28,    39, STR_CONFIG_PATCHES_AI,           STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _patches_selection_desc = {
 
	WDP_CENTER, WDP_CENTER, 370, 371,
 
	WC_GAME_OPTIONS,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_patches_selection_widgets,
 
	PatchesSelectionWndProc,
 
};
 

	
 
void ShowPatchesSelection(void)
 
{
 
	DeleteWindowById(WC_GAME_OPTIONS, 0);
 
	AllocateWindowDesc(&_patches_selection_desc);
 
}
 

	
 

	
 
/**
 
 * Draw [<][>] boxes.
 
 * @param x the x position to draw
 
 * @param y the y position to draw
 
 * @param ctab the color of the buttons
 
 * @param state 0 = none clicked, 1 = first clicked, 2 = second clicked
 
 * @param clickable_left is the left button clickable?
 
 * @param clickable_right is the right button clickable?
 
 */
 
void DrawArrowButtons(int x, int y, int ctab, byte state, bool clickable_left, bool clickable_right)
 
{
 
	int color = PALETTE_MODIFIER_GREYOUT | _colour_gradient[COLOUR_YELLOW][2];
 

	
 
	DrawFrameRect(x,      y + 1, x +  9, y + 9, ctab, (state == 1) ? FR_LOWERED : 0);
 
	DrawFrameRect(x + 10, y + 1, x + 19, y + 9, ctab, (state == 2) ? FR_LOWERED : 0);
 
	DrawStringCentered(x +  5, y + 1, STR_6819, 0); // [<]
 
	DrawStringCentered(x + 15, y + 1, STR_681A, 0); // [>]
 

	
 
	/* Grey out the buttons that aren't clickable */
 
	if (!clickable_left)
 
		GfxFillRect(x +  1, y + 1, x +  1 + 8, y + 8, color);
 
	if (!clickable_right)
 
		GfxFillRect(x + 11, y + 1, x + 11 + 8, y + 8, color);
 
}
 

	
 
static char _str_separator[2];
 

	
 
static void CustCurrencyWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			int x;
 
			int y = 20;
 
			int clk = WP(w,def_d).data_1;
 
			DrawWindowWidgets(w);
 

	
 
			// exchange rate
 
			DrawArrowButtons(10, y, 3, GB(clk, 0, 2), true, true);
 
			SetDParam(0, 1);
 
			SetDParam(1, 1);
 
			DrawString(35, y + 1, STR_CURRENCY_EXCHANGE_RATE, 0);
 
			y += 12;
 

	
 
			// separator
 
			DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(clk, 2, 2) ? FR_LOWERED : 0);
 
			x = DrawString(35, y + 1, STR_CURRENCY_SEPARATOR, 0);
 
			DoDrawString(_str_separator, x + 4, y + 1, 6);
 
			y += 12;
 

	
 
			// prefix
 
			DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(clk, 4, 2) ? FR_LOWERED : 0);
 
			x = DrawString(35, y + 1, STR_CURRENCY_PREFIX, 0);
 
			DoDrawString(_custom_currency.prefix, x + 4, y + 1, 6);
 
			y += 12;
 

	
 
			// suffix
 
			DrawFrameRect(10, y + 1, 29, y + 9, 0, GB(clk, 6, 2) ? FR_LOWERED : 0);
 
			x = DrawString(35, y + 1, STR_CURRENCY_SUFFIX, 0);
 
			DoDrawString(_custom_currency.suffix, x + 4, y + 1, 6);
 
			y += 12;
 

	
 
			// switch to euro
 
			DrawArrowButtons(10, y, 3, GB(clk, 8, 2), true, true);
 
			SetDParam(0, _custom_currency.to_euro);
 
			DrawString(35, y + 1, (_custom_currency.to_euro != CF_NOEURO) ? STR_CURRENCY_SWITCH_TO_EURO : STR_CURRENCY_SWITCH_TO_EURO_NEVER, 0);
 
			y += 12;
 

	
 
			// Preview
 
			y += 12;
 
			SetDParam(0, 10000);
 
			DrawString(35, y + 1, STR_CURRENCY_PREVIEW, 0);
 
			break;
 
		}
 

	
 
		case WE_CLICK: {
 
			int line = (e->we.click.pt.y - 20) / 12;
 
			int len = 0;
 
			int x = e->we.click.pt.x;
 
			StringID str = 0;
 
			CharSetFilter afilter = CS_ALPHANUMERAL;
 

	
 
			switch (line) {
 
				case 0: // rate
 
					if (IS_INT_INSIDE(x, 10, 30)) { // clicked buttons
 
						if (x < 20) {
 
							if (_custom_currency.rate > 1) _custom_currency.rate--;
 
							WP(w,def_d).data_1 = 1 << (line * 2 + 0);
 
						} else {
 
							if (_custom_currency.rate < 5000) _custom_currency.rate++;
 
							WP(w,def_d).data_1 = 1 << (line * 2 + 1);
 
						}
 
					} else { // enter text
 
						SetDParam(0, _custom_currency.rate);
 
						str = STR_CONFIG_PATCHES_INT32;
 
						len = 4;
 
						afilter = CS_NUMERAL;
 
					}
 
					break;
 

	
 
				case 1: // separator
 
					if (IS_INT_INSIDE(x, 10, 30)) { // clicked button
 
						WP(w,def_d).data_1 = 1 << (line * 2 + 1);
 
					}
 
					str = BindCString(_str_separator);
 
					len = 1;
 
					break;
 

	
 
				case 2: // prefix
 
					if (IS_INT_INSIDE(x, 10, 30)) { // clicked button
 
						WP(w,def_d).data_1 = 1 << (line * 2 + 1);
 
					}
 
					str = BindCString(_custom_currency.prefix);
 
					len = 12;
 
					break;
 

	
 
				case 3: // suffix
 
					if (IS_INT_INSIDE(x, 10, 30)) { // clicked button
 
						WP(w,def_d).data_1 = 1 << (line * 2 + 1);
 
					}
 
					str = BindCString(_custom_currency.suffix);
 
					len = 12;
 
					break;
 

	
 
				case 4: // to euro
 
					if (IS_INT_INSIDE(x, 10, 30)) { // clicked buttons
 
						if (x < 20) {
 
							_custom_currency.to_euro = (_custom_currency.to_euro <= 2000) ?
 
								CF_NOEURO : _custom_currency.to_euro - 1;
 
							WP(w,def_d).data_1 = 1 << (line * 2 + 0);
 
						} else {
 
							_custom_currency.to_euro =
 
								clamp(_custom_currency.to_euro + 1, 2000, MAX_YEAR);
 
							WP(w,def_d).data_1 = 1 << (line * 2 + 1);
 
						}
 
					} else { // enter text
 
						SetDParam(0, _custom_currency.to_euro);
 
						str = STR_CONFIG_PATCHES_INT32;
 
						len = 4;
 
						afilter = CS_NUMERAL;
 
					}
 
					break;
 
			}
 

	
 
			if (len != 0) {
 
				WP(w, def_d).data_2 = line;
 
				ShowQueryString(str, STR_CURRENCY_CHANGE_PARAMETER, len + 1, 250, w, afilter);
 
			}
 

	
 
			w->flags4 |= 5 << WF_TIMEOUT_SHL;
 
			SetWindowDirty(w);
 
			break;
 
		}
 

	
 
		case WE_ON_EDIT_TEXT: {
 
				const char *b = e->we.edittext.str;
 

	
 
				switch (WP(w,def_d).data_2) {
 
					case 0: /* Exchange rate */
 
						_custom_currency.rate = clamp(atoi(b), 1, 5000);
 
						break;
 

	
 
					case 1: /* Thousands seperator */
 
						_custom_currency.separator = (b[0] == '\0') ? ' ' : b[0];
 
						ttd_strlcpy(_str_separator, b, lengthof(_str_separator));
 
						break;
 

	
 
					case 2: /* Currency prefix */
 
						ttd_strlcpy(_custom_currency.prefix, b, lengthof(_custom_currency.prefix));
 
						break;
 

	
 
					case 3: /* Currency suffix */
 
						ttd_strlcpy(_custom_currency.suffix, b, lengthof(_custom_currency.suffix));
 
						break;
 

	
 
					case 4: { /* Year to switch to euro */
 
						int val = atoi(b);
 

	
 
						_custom_currency.to_euro =
 
							(val < 2000 ? CF_NOEURO : min(val, MAX_YEAR));
 
						break;
 
					}
 
				}
 
			MarkWholeScreenDirty();
 
			break;
 
		}
 

	
 
		case WE_TIMEOUT:
 
			WP(w,def_d).data_1 = 0;
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case WE_DESTROY:
 
			DeleteWindowById(WC_QUERY_STRING, 0);
 
			MarkWholeScreenDirty();
 
			break;
 
	}
 
}
 

	
 
static const Widget _cust_currency_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   229,     0,    13, STR_CURRENCY_WINDOW, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   229,    14,   119, 0x0,                 STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _cust_currency_desc = {
 
	WDP_CENTER, WDP_CENTER, 230, 120,
 
	WC_CUSTOM_CURRENCY, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_cust_currency_widgets,
 
	CustCurrencyWndProc,
 
};
 

	
 
static void ShowCustCurrency(void)
 
{
 
	_str_separator[0] = _custom_currency.separator;
 
	_str_separator[1] = '\0';
 

	
 
	DeleteWindowById(WC_CUSTOM_CURRENCY, 0);
 
	AllocateWindowDesc(&_cust_currency_desc);
 
}
src/ship_cmd.c
Show inline comments
 
deleted file
src/ship_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "ship.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "vehicle.h"
 
#include "command.h"
 
#include "pathfind.h"
 
#include "station_map.h"
 
#include "station.h"
 
#include "news.h"
 
#include "engine.h"
 
#include "player.h"
 
#include "sound.h"
 
#include "npf.h"
 
#include "depot.h"
 
#include "vehicle_gui.h"
 
#include "newgrf_engine.h"
 
#include "water_map.h"
 
#include "yapf/yapf.h"
 
#include "debug.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_text.h"
 
#include "newgrf_sound.h"
 
#include "date.h"
 

	
 
static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D};
 
static const byte _ship_sometracks[4] = {0x19, 0x16, 0x25, 0x2A};
 

	
 
static byte GetTileShipTrackStatus(TileIndex tile)
 
{
 
	uint32 r = GetTileTrackStatus(tile, TRANSPORT_WATER);
 
	return r | r >> 8;
 
}
 

	
 
void DrawShipEngine(int x, int y, EngineID engine, uint32 image_ormod)
 
{
 
	int spritenum = ShipVehInfo(engine)->image_index;
 

	
 
	if (is_custom_sprite(spritenum)) {
 
		int sprite = GetCustomVehicleIcon(engine, DIR_W);
 

	
 
		if (sprite != 0) {
 
			DrawSprite(sprite | image_ormod, x, y);
 
			return;
 
		}
 
		spritenum = orig_ship_vehicle_info[engine - SHIP_ENGINES_INDEX].image_index;
 
	}
 
	DrawSprite((6 + _ship_sprites[spritenum]) | image_ormod, x, y);
 
}
 

	
 
int GetShipImage(const Vehicle* v, Direction direction)
 
{
 
	int spritenum = v->spritenum;
 

	
 
	if (is_custom_sprite(spritenum)) {
 
		int sprite = GetCustomVehicleSprite(v, direction);
 

	
 
		if (sprite != 0) return sprite;
 
		spritenum = orig_ship_vehicle_info[v->engine_type - SHIP_ENGINES_INDEX].image_index;
 
	}
 
	return _ship_sprites[spritenum] + direction;
 
}
 

	
 
static const Depot* FindClosestShipDepot(const Vehicle* v)
 
{
 
	const Depot* depot;
 
	const Depot* best_depot = NULL;
 
	uint dist;
 
	uint best_dist = (uint)-1;
 
	TileIndex tile;
 
	TileIndex tile2 = v->tile;
 

	
 
	if (_patches.new_pathfinding_all) {
 
		NPFFoundTargetData ftd;
 
		byte trackdir = GetVehicleTrackdir(v);
 
		ftd = NPFRouteToDepotTrialError(v->tile, trackdir, TRANSPORT_WATER, v->owner, INVALID_RAILTYPE);
 
		if (ftd.best_bird_dist == 0) {
 
			best_depot = GetDepotByTile(ftd.node.tile); /* Found target */
 
		} else {
 
			best_depot = NULL; /* Did not find target */
 
		}
 
	} else {
 
		FOR_ALL_DEPOTS(depot) {
 
			tile = depot->xy;
 
			if (IsTileDepotType(tile, TRANSPORT_WATER) && IsTileOwner(tile, v->owner)) {
 
				dist = DistanceManhattan(tile, tile2);
 
				if (dist < best_dist) {
 
					best_dist = dist;
 
					best_depot = depot;
 
				}
 
			}
 
		}
 
	}
 
	return best_depot;
 
}
 

	
 
static void CheckIfShipNeedsService(Vehicle *v)
 
{
 
	const Depot* depot;
 

	
 
	if (_patches.servint_ships == 0) return;
 
	if (!VehicleNeedsService(v))     return;
 
	if (v->vehstatus & VS_STOPPED)   return;
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT &&
 
			v->current_order.flags & OF_HALT_IN_DEPOT)
 
		return;
 

	
 
	if (_patches.gotodepot && VehicleHasDepotOrders(v)) return;
 

	
 
	if (IsShipInDepot(v)) {
 
		VehicleServiceInDepot(v);
 
		return;
 
	}
 

	
 
	depot = FindClosestShipDepot(v);
 

	
 
	if (depot == NULL || DistanceManhattan(v->tile, depot->xy) > 12) {
 
		if (v->current_order.type == OT_GOTO_DEPOT) {
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
		return;
 
	}
 

	
 
	v->current_order.type = OT_GOTO_DEPOT;
 
	v->current_order.flags = OF_NON_STOP;
 
	v->current_order.dest = depot->index;
 
	v->dest_tile = depot->xy;
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
}
 

	
 
void OnNewDay_Ship(Vehicle *v)
 
{
 
	int32 cost;
 

	
 
	if ((++v->day_counter & 7) == 0)
 
		DecreaseVehicleValue(v);
 

	
 
	CheckVehicleBreakdown(v);
 
	AgeVehicle(v);
 
	CheckIfShipNeedsService(v);
 

	
 
	CheckOrders(v);
 

	
 
	if (v->vehstatus & VS_STOPPED) return;
 

	
 
	cost = ShipVehInfo(v->engine_type)->running_cost * _price.ship_running / 364;
 
	v->profit_this_year -= cost >> 8;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_SHIP_RUN);
 
	SubtractMoneyFromPlayerFract(v->owner, cost);
 

	
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	//we need this for the profit
 
	InvalidateWindowClasses(WC_SHIPS_LIST);
 
}
 

	
 
static void HandleBrokenShip(Vehicle *v)
 
{
 
	if (v->breakdown_ctr != 1) {
 
		v->breakdown_ctr = 1;
 
		v->cur_speed = 0;
 

	
 
		if (v->breakdowns_since_last_service != 255)
 
			v->breakdowns_since_last_service++;
 

	
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
 
			SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
 
				SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
 
		}
 

	
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
 
			if (u != NULL) u->u.special.unk0 = v->breakdown_delay * 2;
 
		}
 
	}
 

	
 
	if (!(v->tick_counter & 1)) {
 
		if (!--v->breakdown_delay) {
 
			v->breakdown_ctr = 0;
 
			InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		}
 
	}
 
}
 

	
 
static void MarkShipDirty(Vehicle *v)
 
{
 
	v->cur_image = GetShipImage(v, v->direction);
 
	MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
 
}
 

	
 
static void PlayShipSound(Vehicle *v)
 
{
 
	if (!PlayVehicleSound(v, VSE_START)) {
 
		SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
 
	}
 
}
 

	
 
static void ProcessShipOrder(Vehicle *v)
 
{
 
	const Order *order;
 

	
 
	switch (v->current_order.type) {
 
		case OT_GOTO_DEPOT:
 
			if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return;
 
			if (v->current_order.flags & OF_SERVICE_IF_NEEDED &&
 
					!VehicleNeedsService(v)) {
 
				v->cur_order_index++;
 
			}
 
			break;
 

	
 
		case OT_LOADING:
 
		case OT_LEAVESTATION:
 
			return;
 

	
 
		default: break;
 
	}
 

	
 
	if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
 

	
 
	order = GetVehicleOrder(v, v->cur_order_index);
 

	
 
	if (order == NULL) {
 
		v->current_order.type  = OT_NOTHING;
 
		v->current_order.flags = 0;
 
		v->dest_tile = 0;
 
		return;
 
	}
 

	
 
	if (order->type  == v->current_order.type &&
 
			order->flags == v->current_order.flags &&
 
			order->dest  == v->current_order.dest)
 
		return;
 

	
 
	v->current_order = *order;
 

	
 
	if (order->type == OT_GOTO_STATION) {
 
		const Station *st;
 

	
 
		if (order->dest == v->last_station_visited)
 
			v->last_station_visited = INVALID_STATION;
 

	
 
		st = GetStation(order->dest);
 
		if (st->dock_tile != 0) {
 
			v->dest_tile = TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile)));
 
		}
 
	} else if (order->type == OT_GOTO_DEPOT) {
 
		v->dest_tile = GetDepot(order->dest)->xy;
 
	} else {
 
		v->dest_tile = 0;
 
	}
 

	
 
	InvalidateVehicleOrder(v);
 

	
 
	InvalidateWindowClasses(WC_SHIPS_LIST);
 
}
 

	
 
static void HandleShipLoading(Vehicle *v)
 
{
 
	if (v->current_order.type == OT_NOTHING) return;
 

	
 
	if (v->current_order.type != OT_DUMMY) {
 
		if (v->current_order.type != OT_LOADING) return;
 
		if (--v->load_unload_time_rem) return;
 

	
 
		if (CanFillVehicle(v) && (v->current_order.flags & OF_FULL_LOAD ||
 
				(_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED)))) {
 
			SET_EXPENSES_TYPE(EXPENSES_SHIP_INC);
 
			if (LoadUnloadVehicle(v, false)) {
 
				InvalidateWindow(WC_SHIPS_LIST, v->owner);
 
				MarkShipDirty(v);
 
			}
 
			return;
 
		}
 
		PlayShipSound(v);
 

	
 
		{
 
			Order b = v->current_order;
 
			v->current_order.type = OT_LEAVESTATION;
 
			v->current_order.flags = 0;
 
			if (!(b.flags & OF_NON_STOP)) return;
 
		}
 
	}
 

	
 
	v->cur_order_index++;
 
	InvalidateVehicleOrder(v);
 
}
 

	
 
static void UpdateShipDeltaXY(Vehicle *v, int dir)
 
{
 
#define MKIT(d,c,b,a) ((a&0xFF)<<24) | ((b&0xFF)<<16) | ((c&0xFF)<<8) | ((d&0xFF)<<0)
 
	static const uint32 _delta_xy_table[8] = {
 
		MKIT( -3,  -3,  6,  6),
 
		MKIT(-16,  -3, 32,  6),
 
		MKIT( -3,  -3,  6,  6),
 
		MKIT( -3, -16,  6, 32),
 
		MKIT( -3,  -3,  6,  6),
 
		MKIT(-16,  -3, 32,  6),
 
		MKIT( -3,  -3,  6,  6),
 
		MKIT( -3, -16,  6, 32),
 
	};
 
#undef MKIT
 
	uint32 x = _delta_xy_table[dir];
 
	v->x_offs        = GB(x,  0, 8);
 
	v->y_offs        = GB(x,  8, 8);
 
	v->sprite_width  = GB(x, 16, 8);
 
	v->sprite_height = GB(x, 24, 8);
 
}
 

	
 
void RecalcShipStuff(Vehicle *v)
 
{
 
	UpdateShipDeltaXY(v, v->direction);
 
	v->cur_image = GetShipImage(v, v->direction);
 
	MarkShipDirty(v);
 
	InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
}
 

	
 
static const TileIndexDiffC _ship_leave_depot_offs[] = {
 
	{-1,  0},
 
	{ 0, -1}
 
};
 

	
 
static void CheckShipLeaveDepot(Vehicle *v)
 
{
 
	TileIndex tile;
 
	Axis axis;
 
	uint m;
 

	
 
	if (!IsShipInDepot(v)) return;
 

	
 
	tile = v->tile;
 
	axis = GetShipDepotAxis(tile);
 

	
 
	// Check first side
 
	if (_ship_sometracks[axis] & GetTileShipTrackStatus(TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
 
		m = (axis == AXIS_X) ? 0x101 : 0x207;
 
	// Check second side
 
	} else if (_ship_sometracks[axis + 2] & GetTileShipTrackStatus(TILE_ADD(tile, -2 * ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
 
		m = (axis == AXIS_X) ? 0x105 : 0x203;
 
	} else {
 
		return;
 
	}
 
	v->direction    = GB(m, 0, 8);
 
	v->u.ship.state = GB(m, 8, 8);
 
	v->vehstatus &= ~VS_HIDDEN;
 

	
 
	v->cur_speed = 0;
 
	RecalcShipStuff(v);
 

	
 
	PlayShipSound(v);
 
	VehicleServiceInDepot(v);
 
	InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	InvalidateWindowClasses(WC_SHIPS_LIST);
 
}
 

	
 
static bool ShipAccelerate(Vehicle *v)
 
{
 
	uint spd;
 
	byte t;
 

	
 
	spd = min(v->cur_speed + 1, v->max_speed);
 

	
 
	//updates statusbar only if speed have changed to save CPU time
 
	if (spd != v->cur_speed) {
 
		v->cur_speed = spd;
 
		if (_patches.vehicle_speed)
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 

	
 
	// Decrease somewhat when turning
 
	if (!(v->direction & 1)) spd = spd * 3 / 4;
 

	
 
	if (spd == 0) return false;
 
	if ((byte)++spd == 0) return true;
 

	
 
	v->progress = (t = v->progress) - (byte)spd;
 

	
 
	return (t < v->progress);
 
}
 

	
 
static int32 EstimateShipCost(EngineID engine_type)
 
{
 
	return ShipVehInfo(engine_type)->base_cost * (_price.ship_base>>3)>>5;
 
}
 

	
 
static void ShipArrivesAt(const Vehicle* v, Station* st)
 
{
 
	/* Check if station was ever visited before */
 
	if (!(st->had_vehicle_of_type & HVOT_SHIP)) {
 
		uint32 flags;
 

	
 
		st->had_vehicle_of_type |= HVOT_SHIP;
 

	
 
		SetDParam(0, st->index);
 
		flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0);
 
		AddNewsItem(
 
			STR_9833_CITIZENS_CELEBRATE_FIRST,
 
			flags,
 
			v->index,
 
			0);
 
	}
 
}
 

	
 
typedef struct {
 
	TileIndex skiptile;
 
	TileIndex dest_coords;
 
	uint best_bird_dist;
 
	uint best_length;
 
} PathFindShip;
 

	
 
static bool ShipTrackFollower(TileIndex tile, PathFindShip *pfs, int track, uint length, byte *state)
 
{
 
	// Found dest?
 
	if (tile == pfs->dest_coords) {
 
		pfs->best_bird_dist = 0;
 

	
 
		pfs->best_length = minu(pfs->best_length, length);
 
		return true;
 
	}
 

	
 
	// Skip this tile in the calculation
 
	if (tile != pfs->skiptile) {
 
		pfs->best_bird_dist = minu(pfs->best_bird_dist, DistanceMaxPlusManhattan(pfs->dest_coords, tile));
 
	}
 

	
 
	return false;
 
}
 

	
 
static const byte _ship_search_directions[6][4] = {
 
	{ 0, 9, 2, 9 },
 
	{ 9, 1, 9, 3 },
 
	{ 9, 0, 3, 9 },
 
	{ 1, 9, 9, 2 },
 
	{ 3, 2, 9, 9 },
 
	{ 9, 9, 1, 0 },
 
};
 

	
 
static const byte _pick_shiptrack_table[6] = {1, 3, 2, 2, 0, 0};
 

	
 
static uint FindShipTrack(Vehicle *v, TileIndex tile, int dir, uint bits, TileIndex skiptile, int *track)
 
{
 
	PathFindShip pfs;
 
	int i, best_track;
 
	uint best_bird_dist = 0;
 
	uint best_length    = 0;
 
	uint r;
 
	byte ship_dir = v->direction & 3;
 

	
 
	pfs.dest_coords = v->dest_tile;
 
	pfs.skiptile = skiptile;
 

	
 
	best_track = -1;
 

	
 
	do {
 
		i = FIND_FIRST_BIT(bits);
 
		bits = KILL_FIRST_BIT(bits);
 

	
 
		pfs.best_bird_dist = (uint)-1;
 
		pfs.best_length = (uint)-1;
 

	
 
		FollowTrack(tile, 0x3800 | TRANSPORT_WATER, _ship_search_directions[i][dir], (TPFEnumProc*)ShipTrackFollower, NULL, &pfs);
 

	
 
		if (best_track >= 0) {
 
			if (pfs.best_bird_dist != 0) {
 
				/* neither reached the destination, pick the one with the smallest bird dist */
 
				if (pfs.best_bird_dist > best_bird_dist) goto bad;
 
				if (pfs.best_bird_dist < best_bird_dist) goto good;
 
			} else {
 
				if (pfs.best_length > best_length) goto bad;
 
				if (pfs.best_length < best_length) goto good;
 
			}
 

	
 
			/* if we reach this position, there's two paths of equal value so far.
 
			 * pick one randomly. */
 
			r = GB(Random(), 0, 8);
 
			if (_pick_shiptrack_table[i] == ship_dir) r += 80;
 
			if (_pick_shiptrack_table[best_track] == ship_dir) r -= 80;
 
			if (r <= 127) goto bad;
 
		}
 
good:;
 
		best_track = i;
 
		best_bird_dist = pfs.best_bird_dist;
 
		best_length = pfs.best_length;
 
bad:;
 

	
 
	} while (bits != 0);
 

	
 
	*track = best_track;
 
	return best_bird_dist;
 
}
 

	
 
static inline NPFFoundTargetData PerfNPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypeMask railtypes)
 
{
 

	
 
	void* perf = NpfBeginInterval();
 
	NPFFoundTargetData ret = NPFRouteToStationOrTile(tile, trackdir, target, type, owner, railtypes);
 
	int t = NpfEndInterval(perf);
 
	DEBUG(yapf, 4, "[NPFW] %d us - %d rounds - %d open - %d closed -- ", t, 0, _aystar_stats_open_size, _aystar_stats_closed_size);
 
	return ret;
 
}
 

	
 
/* returns the track to choose on the next tile, or -1 when it's better to
 
 * reverse. The tile given is the tile we are about to enter, enterdir is the
 
 * direction in which we are entering the tile */
 
static int ChooseShipTrack(Vehicle *v, TileIndex tile, int enterdir, uint tracks)
 
{
 
	assert(enterdir>=0 && enterdir<=3);
 

	
 
	if (_patches.yapf.ship_use_yapf) {
 
		Trackdir trackdir = YapfChooseShipTrack(v, tile, enterdir, tracks);
 
		return (trackdir != INVALID_TRACKDIR) ? (int)TrackdirToTrack(trackdir) : -1;
 
	} else if (_patches.new_pathfinding_all) {
 
		NPFFindStationOrTileData fstd;
 
		NPFFoundTargetData ftd;
 
		TileIndex src_tile = TILE_ADD(tile, TileOffsByDiagDir(ReverseDiagDir(enterdir)));
 
		byte trackdir = GetVehicleTrackdir(v);
 
		assert (trackdir != 0xFF); /* Check that we are not in a depot */
 

	
 
		NPFFillWithOrderData(&fstd, v);
 

	
 
		ftd = PerfNPFRouteToStationOrTile(src_tile, trackdir, &fstd, TRANSPORT_WATER, v->owner, INVALID_RAILTYPE);
 

	
 
		if (ftd.best_trackdir != 0xff) {
 
			/* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
 
			the direction we need to take to get there, if ftd.best_bird_dist is not 0,
 
			we did not find our target, but ftd.best_trackdir contains the direction leading
 
			to the tile closest to our target. */
 
			return ftd.best_trackdir & 7; /* TODO: Wrapper function? */
 
		} else {
 
			return -1; /* Already at target, reverse? */
 
		}
 
	} else {
 
		uint b;
 
		uint tot_dist, dist;
 
		int track;
 
		TileIndex tile2;
 

	
 
		tile2 = TILE_ADD(tile, -TileOffsByDiagDir(enterdir));
 
		tot_dist = (uint)-1;
 

	
 
		/* Let's find out how far it would be if we would reverse first */
 
		b = GetTileShipTrackStatus(tile2) & _ship_sometracks[ReverseDiagDir(enterdir)] & v->u.ship.state;
 
		if (b != 0) {
 
			dist = FindShipTrack(v, tile2, ReverseDiagDir(enterdir), b, tile, &track);
 
			if (dist != (uint)-1)
 
				tot_dist = dist + 1;
 
		}
 
		/* And if we would not reverse? */
 
		dist = FindShipTrack(v, tile, enterdir, tracks, 0, &track);
 
		if (dist > tot_dist)
 
			/* We could better reverse */
 
			return -1;
 
		return track;
 
	}
 
}
 

	
 
static const Direction _new_vehicle_direction_table[] = {
 
	DIR_N , DIR_NW, DIR_W , 0,
 
	DIR_NE, DIR_N , DIR_SW, 0,
 
	DIR_E , DIR_SE, DIR_S
 
};
 

	
 
static int ShipGetNewDirectionFromTiles(TileIndex new_tile, TileIndex old_tile)
 
{
 
	uint offs = (TileY(new_tile) - TileY(old_tile) + 1) * 4 +
 
							TileX(new_tile) - TileX(old_tile) + 1;
 
	assert(offs < 11 && offs != 3 && offs != 7);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
static int ShipGetNewDirection(Vehicle *v, int x, int y)
 
{
 
	uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1);
 
	assert(offs < 11 && offs != 3 && offs != 7);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
static int GetAvailShipTracks(TileIndex tile, int dir)
 
{
 
	uint32 r = GetTileTrackStatus(tile, TRANSPORT_WATER);
 
	return (byte) ((r | r >> 8)) & _ship_sometracks[dir];
 
}
 

	
 
static const byte _ship_subcoord[4][6][3] = {
 
	{
 
		{15, 8, 1},
 
		{ 0, 0, 0},
 
		{ 0, 0, 0},
 
		{15, 8, 2},
 
		{15, 7, 0},
 
		{ 0, 0, 0},
 
	},
 
	{
 
		{ 0, 0, 0},
 
		{ 8, 0, 3},
 
		{ 7, 0, 2},
 
		{ 0, 0, 0},
 
		{ 8, 0, 4},
 
		{ 0, 0, 0},
 
	},
 
	{
 
		{ 0, 8, 5},
 
		{ 0, 0, 0},
 
		{ 0, 7, 6},
 
		{ 0, 0, 0},
 
		{ 0, 0, 0},
 
		{ 0, 8, 4},
 
	},
 
	{
 
		{ 0, 0, 0},
 
		{ 8,15, 7},
 
		{ 0, 0, 0},
 
		{ 8,15, 6},
 
		{ 0, 0, 0},
 
		{ 7,15, 0},
 
	}
 
};
 

	
 
static void ShipController(Vehicle *v)
 
{
 
	GetNewVehiclePosResult gp;
 
	uint32 r;
 
	const byte *b;
 
	Direction dir;
 
	int track;
 
	int tracks;
 

	
 
	v->tick_counter++;
 

	
 
	if (v->breakdown_ctr != 0) {
 
		if (v->breakdown_ctr <= 2) {
 
			HandleBrokenShip(v);
 
			return;
 
		}
 
		v->breakdown_ctr--;
 
	}
 

	
 
	if (v->vehstatus & VS_STOPPED) return;
 

	
 
	ProcessShipOrder(v);
 
	HandleShipLoading(v);
 

	
 
	if (v->current_order.type == OT_LOADING) return;
 

	
 
	CheckShipLeaveDepot(v);
 

	
 
	if (!ShipAccelerate(v)) return;
 

	
 
	BeginVehicleMove(v);
 

	
 
	if (GetNewVehiclePos(v, &gp)) {
 
		// staying in tile
 
		if (IsShipInDepot(v)) {
 
			gp.x = v->x_pos;
 
			gp.y = v->y_pos;
 
		} else {
 
			/* isnot inside depot */
 
			r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
 
			if (r & 0x8) goto reverse_direction;
 

	
 
			/* A leave station order only needs one tick to get processed, so we can
 
			 * always skip ahead. */
 
			if (v->current_order.type == OT_LEAVESTATION) {
 
				v->current_order.type = OT_NOTHING;
 
				v->current_order.flags = 0;
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
			} else if (v->dest_tile != 0) {
 
				/* We have a target, let's see if we reached it... */
 
				if (v->current_order.type == OT_GOTO_STATION &&
 
						IsBuoyTile(v->dest_tile) &&
 
						DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
 
					/* We got within 3 tiles of our target buoy, so let's skip to our
 
					 * next order */
 
					v->cur_order_index++;
 
					v->current_order.type = OT_DUMMY;
 
					InvalidateVehicleOrder(v);
 
				} else {
 
					/* Non-buoy orders really need to reach the tile */
 
					if (v->dest_tile == gp.new_tile) {
 
						if (v->current_order.type == OT_GOTO_DEPOT) {
 
							if ((gp.x&0xF)==8 && (gp.y&0xF)==8) {
 
								VehicleEnterDepot(v);
 
								return;
 
							}
 
						} else if (v->current_order.type == OT_GOTO_STATION) {
 
							Station *st;
 

	
 
							v->last_station_visited = v->current_order.dest;
 

	
 
							/* Process station in the orderlist. */
 
							st = GetStation(v->current_order.dest);
 
							if (st->facilities & FACIL_DOCK) { /* ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations */
 
								v->current_order.type = OT_LOADING;
 
								v->current_order.flags &= OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER;
 
								v->current_order.flags |= OF_NON_STOP;
 
								ShipArrivesAt(v, st);
 

	
 
								SET_EXPENSES_TYPE(EXPENSES_SHIP_INC);
 
								if (LoadUnloadVehicle(v, true)) {
 
									InvalidateWindow(WC_SHIPS_LIST, v->owner);
 
									MarkShipDirty(v);
 
								}
 
								InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
							} else { /* leave stations without docks right aways */
 
								v->current_order.type = OT_LEAVESTATION;
 
								v->current_order.flags = 0;
 
								v->cur_order_index++;
 
								InvalidateVehicleOrder(v);
 
							}
 
						}
 
					}
 
				}
 
			}
 
		}
 
	} else {
 
		DiagDirection diagdir;
 
		// new tile
 
		if (TileX(gp.new_tile) >= MapMaxX() || TileY(gp.new_tile) >= MapMaxY())
 
			goto reverse_direction;
 

	
 
		dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile);
 
		assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW);
 
		diagdir = DirToDiagDir(dir);
 
		tracks = GetAvailShipTracks(gp.new_tile, diagdir);
 
		if (tracks == 0)
 
			goto reverse_direction;
 

	
 
		// Choose a direction, and continue if we find one
 
		track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
 
		if (track < 0)
 
			goto reverse_direction;
 

	
 
		b = _ship_subcoord[diagdir][track];
 

	
 
		gp.x = (gp.x&~0xF) | b[0];
 
		gp.y = (gp.y&~0xF) | b[1];
 

	
 
		/* Call the landscape function and tell it that the vehicle entered the tile */
 
		r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
 
		if (r&0x8) goto reverse_direction;
 

	
 
		if (!(r&0x4)) {
 
			v->tile = gp.new_tile;
 
			v->u.ship.state = 1 << track;
 
		}
 

	
 
		v->direction = b[2];
 
	}
 

	
 
	/* update image of ship, as well as delta XY */
 
	dir = ShipGetNewDirection(v, gp.x, gp.y);
 
	v->x_pos = gp.x;
 
	v->y_pos = gp.y;
 
	v->z_pos = GetSlopeZ(gp.x, gp.y);
 

	
 
getout:
 
	UpdateShipDeltaXY(v, dir);
 
	v->cur_image = GetShipImage(v, dir);
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
	return;
 

	
 
reverse_direction:
 
	dir = ReverseDir(v->direction);
 
	v->direction = dir;
 
	goto getout;
 
}
 

	
 
static void AgeShipCargo(Vehicle *v)
 
{
 
	if (_age_cargo_skip_counter != 0) return;
 
	if (v->cargo_days != 255) v->cargo_days++;
 
}
 

	
 
void Ship_Tick(Vehicle *v)
 
{
 
	AgeShipCargo(v);
 
	ShipController(v);
 
}
 

	
 

	
 
void ShipsYearlyLoop(void)
 
{
 
	Vehicle *v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Ship) {
 
			v->profit_last_year = v->profit_this_year;
 
			v->profit_this_year = 0;
 
			InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		}
 
	}
 
}
 

	
 
/** Build a ship.
 
 * @param tile tile of depot where ship is built
 
 * @param p1 ship type being built (engine)
 
 * @param p2 bit 0 when set, the unitnumber will be 0, otherwise it will be a free number
 
 */
 
int32 CmdBuildShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 value;
 
	Vehicle *v;
 
	UnitID unit_num;
 
	Engine *e;
 

	
 
	if (!IsEngineBuildable(p1, VEH_Ship, _current_player)) return_cmd_error(STR_ENGINE_NOT_BUILDABLE);
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	value = EstimateShipCost(p1);
 
	if (flags & DC_QUERY_COST) return value;
 

	
 
	/* The ai_new queries the vehicle cost before building the route,
 
	 * so we must check against cheaters no sooner than now. --pasky */
 
	if (!IsTileDepotType(tile, TRANSPORT_WATER)) return CMD_ERROR;
 
	if (!IsTileOwner(tile, _current_player)) return CMD_ERROR;
 

	
 
	v = AllocateVehicle();
 
	unit_num = HASBIT(p2, 0) ? 0 : GetFreeUnitNumber(VEH_Ship);
 

	
 
	if (v == NULL || IsOrderPoolFull() || unit_num > _patches.max_ships)
 
		return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
	if (flags & DC_EXEC) {
 
		int x;
 
		int y;
 

	
 
		const ShipVehicleInfo *svi = ShipVehInfo(p1);
 

	
 
		v->unitnumber = unit_num;
 

	
 
		v->owner = _current_player;
 
		v->tile = tile;
 
		x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
 
		y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
 
		v->x_pos = x;
 
		v->y_pos = y;
 
		v->z_pos = GetSlopeZ(x,y);
 

	
 
		v->z_height = 6;
 
		v->sprite_width = 6;
 
		v->sprite_height = 6;
 
		v->x_offs = -3;
 
		v->y_offs = -3;
 
		v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
 

	
 
		v->spritenum = svi->image_index;
 
		v->cargo_type = svi->cargo_type;
 
		v->cargo_subtype = 0;
 
		v->cargo_cap = svi->capacity;
 
		v->value = value;
 

	
 
		v->last_station_visited = INVALID_STATION;
 
		v->max_speed = svi->max_speed;
 
		v->engine_type = p1;
 

	
 
		e = GetEngine(p1);
 
		v->reliability = e->reliability;
 
		v->reliability_spd_dec = e->reliability_spd_dec;
 
		v->max_age = e->lifelength * 366;
 
		_new_vehicle_id = v->index;
 

	
 
		v->string_id = STR_SV_SHIP_NAME;
 
		v->u.ship.state = 0x80;
 

	
 
		v->service_interval = _patches.servint_ships;
 
		v->date_of_last_service = _date;
 
		v->build_year = _cur_year;
 
		v->cur_image = 0x0E5E;
 
		v->type = VEH_Ship;
 
		v->random_bits = VehicleRandomBits();
 

	
 
		VehiclePositionChanged(v);
 
		GetPlayer(_current_player)->num_engines[p1]++;
 

	
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
		RebuildVehicleLists();
 
		InvalidateWindow(WC_COMPANY, v->owner);
 
		if (IsLocalPlayer())
 
			InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Ship); // updates the replace Ship window
 
	}
 

	
 
	return value;
 
}
 

	
 
/** Sell a ship.
 
 * @param tile unused
 
 * @param p1 vehicle ID to be sold
 
 * @param p2 unused
 
 */
 
int32 CmdSellShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Ship || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	if (!IsShipInDepotStopped(v)) {
 
		return_cmd_error(STR_980B_SHIP_MUST_BE_STOPPED_IN);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		RebuildVehicleLists();
 
		InvalidateWindow(WC_COMPANY, v->owner);
 
		DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 
		DeleteDepotHighlightOfVehicle(v);
 
		DeleteVehicle(v);
 
		if (IsLocalPlayer())
 
			InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Ship); // updates the replace Ship window
 
	}
 

	
 
	return -(int32)v->value;
 
}
 

	
 
/** Start/Stop a ship.
 
 * @param tile unused
 
 * @param p1 ship ID to start/stop
 
 * @param p2 unused
 
 */
 
int32 CmdStartStopShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	uint16 callback;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Ship || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	/* Check if this ship can be started/stopped. The callback will fail or
 
	 * return 0xFF if it can. */
 
	callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
 
	if (callback != CALLBACK_FAILED && callback != 0xFF) {
 
		StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
 
		return_cmd_error(error);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		if (IsShipInDepotStopped(v)) {
 
			DeleteVehicleNews(p1, STR_981C_SHIP_IS_WAITING_IN_DEPOT);
 
		}
 

	
 
		v->vehstatus ^= VS_STOPPED;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		InvalidateWindowClasses(WC_SHIPS_LIST);
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Send a ship to the depot.
 
 * @param tile unused
 
 * @param p1 vehicle ID to send to the depot
 
 * @param p2 various bitmasked elements
 
 * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h)
 
 * - p2 bit 8-10 - VLW flag (for mass goto depot)
 
 */
 
int32 CmdSendShipToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	const Depot *dep;
 

	
 
	if (p2 & DEPOT_MASS_SEND) {
 
		/* Mass goto depot requested */
 
		if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
 
		return SendAllVehiclesToDepot(VEH_Ship, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1);
 
	}
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Ship || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
 

	
 
	if (IsShipInDepot(v)) return CMD_ERROR;
 

	
 
	/* If the current orders are already goto-depot */
 
	if (v->current_order.type == OT_GOTO_DEPOT) {
 
		if (!!(p2 & DEPOT_SERVICE) == HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT)) {
 
			/* We called with a different DEPOT_SERVICE setting.
 
			 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
 
			 * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
 
			if (flags & DC_EXEC) {
 
				TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
			}
 
			return 0;
 
		}
 

	
 
		if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
 
		if (flags & DC_EXEC) {
 
			/* If the orders to 'goto depot' are in the orders list (forced servicing),
 
			 * then skip to the next order; effectively cancelling this forced service */
 
			if (HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS))
 
				v->cur_order_index++;
 

	
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
		return 0;
 
	}
 

	
 
	dep = FindClosestShipDepot(v);
 
	if (dep == NULL) return_cmd_error(STR_981A_UNABLE_TO_FIND_LOCAL_DEPOT);
 

	
 
	if (flags & DC_EXEC) {
 
		v->dest_tile = dep->xy;
 
		v->current_order.type = OT_GOTO_DEPOT;
 
		v->current_order.flags = OF_NON_STOP;
 
		if (!(p2 & DEPOT_SERVICE)) SETBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
		v->current_order.refit_cargo = CT_INVALID;
 
		v->current_order.dest = dep->index;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
/** Refits a ship to the specified cargo type.
 
 * @param tile unused
 
 * @param p1 vehicle ID of the ship to refit
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit 0-7) - the new cargo type to refit to (p2 & 0xFF)
 
 * - p2 = (bit 8-15) - the new cargo subtype to refit to
 
 */
 
int32 CmdRefitShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	int32 cost;
 
	CargoID new_cid = GB(p2, 0, 8); //gets the cargo number
 
	byte new_subtype = GB(p2, 8, 8);
 
	uint16 capacity = CALLBACK_FAILED;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Ship || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (!IsShipInDepotStopped(v)) {
 
		return_cmd_error(STR_980B_SHIP_MUST_BE_STOPPED_IN);
 
	}
 

	
 
	/* Check cargo */
 
	if (!ShipVehInfo(v->engine_type)->refittable) return CMD_ERROR;
 
	if (new_cid > NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_SHIP_RUN);
 

	
 
	/* Check the refit capacity callback */
 
	if (HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_REFIT_CAPACITY)) {
 
		/* Back up the existing cargo type */
 
		CargoID temp_cid = v->cargo_type;
 
		byte temp_subtype = v->cargo_subtype;
 
		v->cargo_type = new_cid;
 
		v->cargo_subtype = new_subtype;
 

	
 
		capacity = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
 

	
 
		/* Restore the cargo type */
 
		v->cargo_type = temp_cid;
 
		v->cargo_subtype = temp_subtype;
 
	}
 

	
 
	if (capacity == CALLBACK_FAILED) {
 
		capacity = ShipVehInfo(v->engine_type)->capacity;
 
	}
 
	_returned_refit_capacity = capacity;
 

	
 
	cost = 0;
 
	if (IsHumanPlayer(v->owner) && new_cid != v->cargo_type) {
 
		cost = GetRefitCost(v->engine_type);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		v->cargo_cap = capacity;
 
		v->cargo_count = (v->cargo_type == new_cid) ? min(v->cargo_cap, v->cargo_count) : 0;
 
		v->cargo_type = new_cid;
 
		v->cargo_subtype = new_subtype;
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		RebuildVehicleLists();
 
	}
 

	
 
	return cost;
 

	
 
}
src/ship_gui.c
Show inline comments
 
deleted file
src/ship_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "ship.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "map.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "vehicle.h"
 
#include "viewport.h"
 
#include "station.h"
 
#include "command.h"
 
#include "player.h"
 
#include "engine.h"
 
#include "depot.h"
 
#include "vehicle_gui.h"
 
#include "newgrf_engine.h"
 
#include "date.h"
 

	
 
/**
 
 * Draw the purchase info details of a ship at a given location.
 
 * @param x,y location where to draw the info
 
 * @param engine_number the engine of which to draw the info of
 
 */
 
void DrawShipPurchaseInfo(int x, int y, uint w, EngineID engine_number)
 
{
 
	YearMonthDay ymd;
 
	const ShipVehicleInfo *svi = ShipVehInfo(engine_number);
 
	const Engine *e;
 

	
 
	/* Purchase cost - Max speed */
 
	SetDParam(0, svi->base_cost * (_price.ship_base>>3)>>5);
 
	SetDParam(1, svi->max_speed / 2);
 
	DrawString(x,y, STR_PURCHASE_INFO_COST_SPEED, 0);
 
	y += 10;
 

	
 
	/* Cargo type + capacity */
 
	SetDParam(0, svi->cargo_type);
 
	SetDParam(1, svi->capacity);
 
	SetDParam(2, svi->refittable ? STR_9842_REFITTABLE : STR_EMPTY);
 
	DrawString(x,y, STR_PURCHASE_INFO_CAPACITY, 0);
 
	y += 10;
 

	
 
	/* Running cost */
 
	SetDParam(0, svi->running_cost * _price.ship_running >> 8);
 
	DrawString(x,y, STR_PURCHASE_INFO_RUNNINGCOST, 0);
 
	y += 10;
 

	
 
	/* Design date - Life length */
 
	e = GetEngine(engine_number);
 
	ConvertDateToYMD(e->intro_date, &ymd);
 
	SetDParam(0, ymd.year);
 
	SetDParam(1, e->lifelength);
 
	DrawString(x,y, STR_PURCHASE_INFO_DESIGNED_LIFE, 0);
 
	y += 10;
 

	
 
	/* Reliability */
 
	SetDParam(0, e->reliability * 100 >> 16);
 
	DrawString(x,y, STR_PURCHASE_INFO_RELIABILITY, 0);
 
	y += 10;
 

	
 
	/* Additional text from NewGRF */
 
	y += ShowAdditionalText(x, y, w, engine_number);
 
	if (svi->refittable) y += ShowRefitOptionsList(x, y, w, engine_number);
 
}
 

	
 
void DrawShipImage(const Vehicle *v, int x, int y, VehicleID selection)
 
{
 
	DrawSprite(GetShipImage(v, DIR_W) | GetVehiclePalette(v), x + 32, y + 10);
 

	
 
	if (v->index == selection) {
 
		DrawFrameRect(x - 5, y - 1, x + 67, y + 21, 15, FR_BORDERONLY);
 
	}
 
}
 

	
 
static void ShipDetailsWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const Vehicle *v = GetVehicle(w->window_number);
 
		StringID str;
 

	
 
		SetWindowWidgetDisabledState(w, 2, v->owner != _local_player);
 
		// disable service-scroller when interval is set to disabled
 
		SetWindowWidgetDisabledState(w, 5, !_patches.servint_ships);
 
		SetWindowWidgetDisabledState(w, 6, !_patches.servint_ships);
 

	
 
		SetDParam(0, v->string_id);
 
		SetDParam(1, v->unitnumber);
 
		DrawWindowWidgets(w);
 

	
 
		/* Draw running cost */
 
		{
 
			int year = v->age / 366;
 

	
 
			SetDParam(1, year);
 

	
 
			SetDParam(0, (v->age + 365 < v->max_age) ? STR_AGE : STR_AGE_RED);
 
			SetDParam(2, v->max_age / 366);
 
			SetDParam(3, ShipVehInfo(v->engine_type)->running_cost * _price.ship_running >> 8);
 
			DrawString(2, 15, STR_9812_AGE_RUNNING_COST_YR, 0);
 
		}
 

	
 
		/* Draw max speed */
 
		{
 
			SetDParam(0, v->max_speed / 2);
 
			DrawString(2, 25, STR_9813_MAX_SPEED, 0);
 
		}
 

	
 
		/* Draw profit */
 
		{
 
			SetDParam(0, v->profit_this_year);
 
			SetDParam(1, v->profit_last_year);
 
			DrawString(2, 35, STR_9814_PROFIT_THIS_YEAR_LAST_YEAR, 0);
 
		}
 

	
 
		/* Draw breakdown & reliability */
 
		{
 
			SetDParam(0, v->reliability * 100 >> 16);
 
			SetDParam(1, v->breakdowns_since_last_service);
 
			DrawString(2, 45, STR_9815_RELIABILITY_BREAKDOWNS, 0);
 
		}
 

	
 
		/* Draw service interval text */
 
		{
 
			SetDParam(0, v->service_interval);
 
			SetDParam(1, v->date_of_last_service);
 
			DrawString(13, 90, _patches.servint_ispercent?STR_SERVICING_INTERVAL_PERCENT:STR_883C_SERVICING_INTERVAL_DAYS, 0);
 
		}
 

	
 
		DrawShipImage(v, 3, 57, INVALID_VEHICLE);
 

	
 
		SetDParam(1, v->build_year);
 
		SetDParam(0, GetCustomEngineName(v->engine_type));
 
		SetDParam(2, v->value);
 
		DrawString(74, 57, STR_9816_BUILT_VALUE, 0);
 

	
 
		SetDParam(0, v->cargo_type);
 
		SetDParam(1, v->cargo_cap);
 
		DrawString(74, 67, STR_9817_CAPACITY, 0);
 

	
 
		str = STR_8812_EMPTY;
 
		if (v->cargo_count != 0) {
 
			SetDParam(0, v->cargo_type);
 
			SetDParam(1, v->cargo_count);
 
			SetDParam(2, v->cargo_source);
 
			str = STR_8813_FROM;
 
		}
 
		DrawString(74, 78, str, 0);
 
	} break;
 

	
 
	case WE_CLICK: {
 
		int mod;
 
		const Vehicle *v;
 
		switch (e->we.click.widget) {
 
		case 2: /* rename */
 
			v = GetVehicle(w->window_number);
 
			SetDParam(0, v->unitnumber);
 
			ShowQueryString(v->string_id, STR_9831_NAME_SHIP, 31, 150, w, CS_ALPHANUMERAL);
 
			break;
 
		case 5: /* increase int */
 
			mod = _ctrl_pressed? 5 : 10;
 
			goto do_change_service_int;
 
		case 6: /* decrease int */
 
			mod = _ctrl_pressed?- 5 : -10;
 
do_change_service_int:
 
			v = GetVehicle(w->window_number);
 

	
 
			mod = GetServiceIntervalClamped(mod + v->service_interval);
 
			if (mod == v->service_interval) return;
 

	
 
			DoCommandP(v->tile, v->index, mod, NULL, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING));
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_ON_EDIT_TEXT:
 
		if (e->we.edittext.str[0] != '\0') {
 
			_cmd_text = e->we.edittext.str;
 
			DoCommandP(0, w->window_number, 0, NULL,
 
				CMD_NAME_VEHICLE | CMD_MSG(STR_9832_CAN_T_NAME_SHIP));
 
		}
 
		break;
 
	}
 
}
 

	
 

	
 
static const Widget _ship_details_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   364,     0,    13, STR_9811_DETAILS, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   365,   404,     0,    13, STR_01AA_NAME,    STR_982F_NAME_SHIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   404,    14,    55, 0x0,              STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   404,    56,    88, 0x0,              STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    10,    89,    94, STR_0188,         STR_884D_INCREASE_SERVICING_INTERVAL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    10,    95,   100, STR_0189,         STR_884E_DECREASE_SERVICING_INTERVAL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    11,   404,    89,   100, 0x0,              STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _ship_details_desc = {
 
	WDP_AUTO, WDP_AUTO, 405, 101,
 
	WC_VEHICLE_DETAILS,WC_VEHICLE_VIEW,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_ship_details_widgets,
 
	ShipDetailsWndProc
 
};
 

	
 
static void ShowShipDetailsWindow(const Vehicle *v)
 
{
 
	Window *w;
 
	VehicleID veh = v->index;
 

	
 
	DeleteWindowById(WC_VEHICLE_ORDERS, veh);
 
	DeleteWindowById(WC_VEHICLE_DETAILS, veh);
 
	w = AllocateWindowDescFront(&_ship_details_desc, veh);
 
	w->caption_color = v->owner;
 
}
 

	
 
void CcBuildShip(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	const Vehicle *v;
 
	if (!success) return;
 

	
 
	v = GetVehicle(_new_vehicle_id);
 
	if (v->tile == _backup_orders_tile) {
 
		_backup_orders_tile = 0;
 
		RestoreVehicleOrders(v, _backup_orders_data);
 
	}
 
	ShowShipViewWindow(v);
 
}
 

	
 
void CcCloneShip(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) ShowShipViewWindow(GetVehicle(_new_vehicle_id));
 
}
 

	
 
static void NewShipWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			EngineID selected_id;
 
			EngineID eid;
 
			int count;
 
			int pos;
 
			int sel;
 
			int y;
 

	
 
			SetWindowWidgetDisabledState(w, 5, w->window_number == 0);
 

	
 
			count = 0;
 
			for (eid = SHIP_ENGINES_INDEX; eid < SHIP_ENGINES_INDEX + NUM_SHIP_ENGINES; eid++) {
 
				if (HASBIT(GetEngine(eid)->player_avail, _local_player)) count++;
 
			}
 
			SetVScrollCount(w, count);
 

	
 
			DrawWindowWidgets(w);
 

	
 
			y = 15;
 
			sel = WP(w,buildvehicle_d).sel_index;
 
			pos = w->vscroll.pos;
 
			selected_id = INVALID_ENGINE;
 
			for (eid = SHIP_ENGINES_INDEX; eid < SHIP_ENGINES_INDEX + NUM_SHIP_ENGINES; eid++) {
 
				if (!HASBIT(GetEngine(eid)->player_avail, _local_player)) continue;
 
				if (sel == 0) selected_id = eid;
 
				if (IS_INT_INSIDE(--pos, -w->vscroll.cap, 0)) {
 
					DrawString(77, y + 7, GetCustomEngineName(eid), sel == 0 ? 0xC : 0x10);
 
					DrawShipEngine(37, y + 10, eid, GetEnginePalette(eid, _local_player));
 
					y += 24;
 
				}
 
				sel--;
 
			}
 

	
 
			WP(w,buildvehicle_d).sel_engine = selected_id;
 

	
 
			if (selected_id != INVALID_ENGINE) {
 
				const Widget *wi = &w->widget[4];
 
				DrawShipPurchaseInfo(2, wi->top + 1, wi->right - wi->left - 2, selected_id);
 
			}
 
			break;
 
		}
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 2: { /* listbox */
 
			uint i = (e->we.click.pt.y - 14) / 24;
 
			if (i < w->vscroll.cap) {
 
				WP(w,buildvehicle_d).sel_index = i + w->vscroll.pos;
 
				SetWindowDirty(w);
 
			}
 
		} break;
 
		case 5: { /* build */
 
			EngineID sel_eng = WP(w,buildvehicle_d).sel_engine;
 
			if (sel_eng != INVALID_ENGINE)
 
				DoCommandP(w->window_number, sel_eng, 0, CcBuildShip, CMD_BUILD_SHIP | CMD_MSG(STR_980D_CAN_T_BUILD_SHIP));
 
		} break;
 

	
 
		case 6: { /* rename */
 
			EngineID sel_eng = WP(w,buildvehicle_d).sel_engine;
 
			if (sel_eng != INVALID_ENGINE) {
 
				WP(w, buildvehicle_d).rename_engine = sel_eng;
 
				ShowQueryString(GetCustomEngineName(sel_eng), STR_9838_RENAME_SHIP_TYPE, 31, 160, w, CS_ALPHANUMERAL);
 
			}
 
		}	break;
 
		}
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT:
 
		if (e->we.edittext.str[0] != '\0') {
 
			_cmd_text = e->we.edittext.str;
 
			DoCommandP(0, WP(w, buildvehicle_d).rename_engine, 0, NULL,
 
				CMD_RENAME_ENGINE | CMD_MSG(STR_9839_CAN_T_RENAME_SHIP_TYPE));
 
		}
 
		break;
 

	
 
	case WE_RESIZE:
 
		w->vscroll.cap += e->we.sizing.diff.y / 24;
 
		w->widget[2].data = (w->vscroll.cap << 8) + 1;
 
		break;
 

	
 
	}
 
}
 

	
 
static const Widget _new_ship_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,            STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   254,     0,    13, STR_9808_NEW_SHIPS,  STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   242,    14,   109, 0x401,               STR_9825_SHIP_SELECTION_LIST_CLICK},
 
{  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   243,   254,    14,   109, 0x0,                 STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,     RESIZE_TB,    14,     0,   254,   110,   201, 0x0,                 STR_NULL},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   121,   202,   213, STR_9809_BUILD_SHIP, STR_9826_BUILD_THE_HIGHLIGHTED_SHIP},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   122,   242,   202,   213, STR_9836_RENAME,     STR_9837_RENAME_SHIP_TYPE},
 
{  WWT_RESIZEBOX,     RESIZE_TB,    14,   243,   254,   202,   213, 0x0,                 STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _new_ship_desc = {
 
	WDP_AUTO, WDP_AUTO, 255, 214,
 
	WC_BUILD_VEHICLE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_new_ship_widgets,
 
	NewShipWndProc
 
};
 

	
 

	
 
void ShowBuildShipWindow(TileIndex tile)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_BUILD_VEHICLE, tile);
 

	
 
	w = AllocateWindowDescFront(&_new_ship_desc, tile);
 
	w->vscroll.cap = 4;
 
	w->widget[2].data = (w->vscroll.cap << 8) + 1;
 

	
 
	w->resize.step_height = 24;
 

	
 
	if (tile != 0) {
 
		w->caption_color = GetTileOwner(tile);
 
	} else {
 
		w->caption_color = _local_player;
 
	}
 

	
 
}
 

	
 

	
 
static void ShipViewWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			Vehicle *v = GetVehicle(w->window_number);
 
			StringID str;
 
			bool refitable_and_stopped_in_depot = ShipVehInfo(v->engine_type)->refittable && IsShipInDepotStopped(v);
 
			bool is_localplayer = v->owner == _local_player;
 

	
 
			SetWindowWidgetDisabledState(w,  7, !is_localplayer);
 
			SetWindowWidgetDisabledState(w,  8,
 
			                             !is_localplayer ||      // Disable if owner is not local player
 
			                             !refitable_and_stopped_in_depot); // Disable if the ship is not refitable or stopped in a depot
 
			SetWindowWidgetDisabledState(w, 11, !is_localplayer);
 

	
 
			/* draw widgets & caption */
 
			SetDParam(0, v->string_id);
 
			SetDParam(1, v->unitnumber);
 
			DrawWindowWidgets(w);
 

	
 
			if (v->breakdown_ctr == 1) {
 
				str = STR_885C_BROKEN_DOWN;
 
			} else if (v->vehstatus & VS_STOPPED) {
 
				str = STR_8861_STOPPED;
 
			} else {
 
				switch (v->current_order.type) {
 
					case OT_GOTO_STATION: {
 
						SetDParam(0, v->current_order.dest);
 
						SetDParam(1, v->cur_speed / 2);
 
						str = STR_HEADING_FOR_STATION + _patches.vehicle_speed;
 
					} break;
 

	
 
					case OT_GOTO_DEPOT: {
 
						Depot *depot = GetDepot(v->current_order.dest);
 
						SetDParam(0, depot->town_index);
 
						SetDParam(1, v->cur_speed / 2);
 
						if (HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT) && !HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) {
 
							str = STR_HEADING_FOR_SHIP_DEPOT + _patches.vehicle_speed;
 
						} else {
 
							str = STR_HEADING_FOR_SHIP_DEPOT_SERVICE + _patches.vehicle_speed;
 
						}
 
					} break;
 

	
 
					case OT_LOADING:
 
					case OT_LEAVESTATION:
 
						str = STR_882F_LOADING_UNLOADING;
 
						break;
 

	
 
					default:
 
						if (v->num_orders == 0) {
 
							str = STR_NO_ORDERS + _patches.vehicle_speed;
 
							SetDParam(0, v->cur_speed / 2);
 
						} else {
 
							str = STR_EMPTY;
 
						}
 
						break;
 
				}
 
			}
 

	
 
		/* draw the flag plus orders */
 
		DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, 2, w->widget[5].top + 1);
 
		DrawStringCenteredTruncated(w->widget[5].left + 8, w->widget[5].right, w->widget[5].top + 1, str, 0);
 
		DrawWindowViewport(w);
 
	} break;
 

	
 
		case WE_CLICK: {
 
			const Vehicle *v = GetVehicle(w->window_number);
 

	
 
			switch (e->we.click.widget) {
 
				case 5: /* start stop */
 
					DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_SHIP | CMD_MSG(STR_9818_CAN_T_STOP_START_SHIP));
 
					break;
 
				case 6: /* center main view */
 
					ScrollMainWindowTo(v->x_pos, v->y_pos);
 
					break;
 
				case 7: /* goto hangar */
 
					DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0, NULL, CMD_SEND_SHIP_TO_DEPOT | CMD_MSG(STR_9819_CAN_T_SEND_SHIP_TO_DEPOT));
 
					break;
 
				case 8: /* refit */
 
					ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID);
 
					break;
 
				case 9: /* show orders */
 
					ShowOrdersWindow(v);
 
					break;
 
				case 10: /* show details */
 
					ShowShipDetailsWindow(v);
 
					break;
 
				case 11: {
 
					/* clone vehicle */
 
					DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneShip, CMD_CLONE_VEHICLE | CMD_MSG(STR_980D_CAN_T_BUILD_SHIP));
 
				} break;
 
			}
 
		} break;
 

	
 
		case WE_RESIZE:
 
			w->viewport->width          += e->we.sizing.diff.x;
 
			w->viewport->height         += e->we.sizing.diff.y;
 
			w->viewport->virtual_width  += e->we.sizing.diff.x;
 
			w->viewport->virtual_height += e->we.sizing.diff.y;
 
			break;
 

	
 
		case WE_DESTROY:
 
			DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number);
 
			DeleteWindowById(WC_VEHICLE_REFIT, w->window_number);
 
			DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number);
 
			break;
 

	
 
		case WE_MOUSELOOP: {
 
			const Vehicle *v = GetVehicle(w->window_number);
 
			bool ship_stopped = IsShipInDepotStopped(v);
 

	
 
			/* Widget 7 (send to depot) must be hidden if the ship is already stopped in depot.
 
			 * Widget 11 (clone) should then be shown, since cloning is allowed only while in depot and stopped.
 
			 * This sytem allows to have two buttons, on top of each otherother*/
 
			if (ship_stopped != IsWindowWidgetHidden(w, 7) || ship_stopped == IsWindowWidgetHidden(w, 11)) {
 
				SetWindowWidgetHiddenState(w,  7, ship_stopped);  // send to depot
 
				SetWindowWidgetHiddenState(w, 11, !ship_stopped); // clone
 
				SetWindowDirty(w);
 
			}
 
		}
 
	}
 
}
 

	
 
static const Widget _ship_view_widgets[] = {
 
{   WWT_CLOSEBOX, RESIZE_NONE,  14,   0,  10,   0,  13, STR_00C5,                STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION, RESIZE_RIGHT, 14,  11, 237,   0,  13, STR_980F,                STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX, RESIZE_LR,    14, 238, 249,   0,  13, 0x0,                     STR_STICKY_BUTTON},
 
{      WWT_PANEL, RESIZE_RB,    14,   0, 231,  14, 103, 0x0,                     STR_NULL},
 
{      WWT_INSET, RESIZE_RB,    14,   2, 229,  16, 101, 0x0,                     STR_NULL},
 
{    WWT_PUSHBTN, RESIZE_RTB,   14,   0, 237, 104, 115, 0x0,                     STR_9827_CURRENT_SHIP_ACTION_CLICK},
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  14,  31, SPR_CENTRE_VIEW_VEHICLE, STR_9829_CENTER_MAIN_VIEW_ON_SHIP},
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  32,  49, SPR_SEND_SHIP_TODEPOT,   STR_982A_SEND_SHIP_TO_DEPOT},
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  50,  67, SPR_REFIT_VEHICLE,       STR_983A_REFIT_CARGO_SHIP_TO_CARRY},
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  68,  85, SPR_SHOW_ORDERS,         STR_9828_SHOW_SHIP_S_ORDERS},
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  86, 103, SPR_SHOW_VEHICLE_DETAILS,STR_982B_SHOW_SHIP_DETAILS},
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  32,  49, SPR_CLONE_SHIP,          STR_CLONE_SHIP_INFO},
 
{      WWT_PANEL, RESIZE_LRB,   14, 232, 249, 104, 103, 0x0,                     STR_NULL },
 
{  WWT_RESIZEBOX, RESIZE_LRTB,  14, 238, 249, 104, 115, 0x0,                     STR_NULL },
 
{ WIDGETS_END }
 
};
 

	
 
static const WindowDesc _ship_view_desc = {
 
	WDP_AUTO, WDP_AUTO, 250, 116,
 
	WC_VEHICLE_VIEW,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_ship_view_widgets,
 
	ShipViewWndProc
 
};
 

	
 
void ShowShipViewWindow(const Vehicle *v)
 
{
 
	Window *w = AllocateWindowDescFront(&_ship_view_desc, v->index);
 

	
 
	if (w != NULL) {
 
		w->caption_color = v->owner;
 
		AssignWindowViewport(w, 3, 17, 0xE2, 0x54, w->window_number | (1 << 31), 0);
 
	}
 
}
src/signs.c
Show inline comments
 
deleted file
src/signs.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "player.h"
 
#include "signs.h"
 
#include "saveload.h"
 
#include "command.h"
 
#include "variables.h"
 

	
 
static Sign *_new_sign;
 

	
 
/**
 
 * Called if a new block is added to the sign-pool
 
 */
 
static void SignPoolNewBlock(uint start_item)
 
{
 
	Sign *si;
 

	
 
	/* 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 (si = GetSign(start_item); si != NULL; si = (si->index + 1U < GetSignPoolSize()) ? GetSign(si->index + 1U) : NULL) si->index = start_item++;
 
}
 

	
 
/* Initialize the sign-pool */
 
DEFINE_OLD_POOL(Sign, Sign, SignPoolNewBlock, NULL)
 

	
 
/**
 
 *
 
 * Update the coordinate of one sign
 
 *
 
 */
 
static void UpdateSignVirtCoords(Sign *si)
 
{
 
	Point pt = RemapCoords(si->x, si->y, si->z);
 
	SetDParam(0, si->str);
 
	UpdateViewportSignPos(&si->sign, pt.x, pt.y - 6, STR_2806);
 
}
 

	
 
/**
 
 *
 
 * Update the coordinates of all signs
 
 *
 
 */
 
void UpdateAllSignVirtCoords(void)
 
{
 
	Sign *si;
 

	
 
	FOR_ALL_SIGNS(si) UpdateSignVirtCoords(si);
 

	
 
}
 

	
 
/**
 
 *
 
 * Marks the region of a sign as dirty
 
 *
 
 * @param si Pointer to the Sign
 
 */
 
static void MarkSignDirty(Sign *si)
 
{
 
	MarkAllViewportsDirty(
 
		si->sign.left - 6,
 
		si->sign.top  - 3,
 
		si->sign.left + si->sign.width_1 * 4 + 12,
 
		si->sign.top  + 45);
 
}
 

	
 
/**
 
 *
 
 * Allocates a new sign
 
 *
 
 * @return The pointer to the new sign, or NULL if there is no more free space
 
 */
 
static Sign *AllocateSign(void)
 
{
 
	Sign *si;
 

	
 
	/* 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 (si = GetSign(0); si != NULL; si = (si->index + 1U < GetSignPoolSize()) ? GetSign(si->index + 1U) : NULL) {
 
		if (!IsValidSign(si)) {
 
			uint index = si->index;
 

	
 
			memset(si, 0, sizeof(Sign));
 
			si->index = index;
 

	
 
			return si;
 
		}
 
	}
 

	
 
	/* Check if we can add a block to the pool */
 
	if (AddBlockToPool(&_Sign_pool))
 
		return AllocateSign();
 

	
 
	return NULL;
 
}
 

	
 
void DestroySign(Sign *si)
 
{
 
	DeleteName(si->str);
 
}
 

	
 
/**
 
 * Place a sign at the given coordinates. Ownership of sign has
 
 * no effect whatsoever except for the colour the sign gets for easy recognition,
 
 * but everybody is able to rename/remove it.
 
 * @param tile tile to place sign at
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdPlaceSign(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Sign *si;
 

	
 
	/* Try to locate a new sign */
 
	si = AllocateSign();
 
	if (si == NULL) return_cmd_error(STR_2808_TOO_MANY_SIGNS);
 

	
 
	/* When we execute, really make the sign */
 
	if (flags & DC_EXEC) {
 
		int x = TileX(tile) * TILE_SIZE;
 
		int y = TileY(tile) * TILE_SIZE;
 

	
 
		si->str = STR_280A_SIGN;
 
		si->x = x;
 
		si->y = y;
 
		si->owner = _current_player; // owner of the sign; just eyecandy
 
		si->z = GetSlopeZ(x,y);
 
		UpdateSignVirtCoords(si);
 
		MarkSignDirty(si);
 
		InvalidateWindow(WC_SIGN_LIST, 0);
 
		_sign_sort_dirty = true;
 
		_new_sign = si;
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Rename a sign. If the new name of the sign is empty, we assume
 
 * the user wanted to delete it. So delete it. Ownership of signs
 
 * has no meaning/effect whatsoever except for eyecandy
 
 * @param tile unused
 
 * @param p1 index of the sign to be renamed/removed
 
 * @param p2 unused
 
 */
 
int32 CmdRenameSign(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (!IsValidSignID(p1)) return CMD_ERROR;
 

	
 
	/* If _cmd_text 0 means the new text for the sign is non-empty.
 
	 * So rename the sign. If it is empty, it has no name, so delete it */
 
	if (_cmd_text[0] != '\0') {
 
		/* Create the name */
 
		StringID str = AllocateName(_cmd_text, 0);
 
		if (str == 0) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
 
			Sign *si = GetSign(p1);
 

	
 
			/* Delete the old name */
 
			DeleteName(si->str);
 
			/* Assign the new one */
 
			si->str = str;
 
			si->owner = _current_player;
 

	
 
			/* Update; mark sign dirty twice, because it can either becom longer, or shorter */
 
			MarkSignDirty(si);
 
			UpdateSignVirtCoords(si);
 
			MarkSignDirty(si);
 
			InvalidateWindow(WC_SIGN_LIST, 0);
 
			_sign_sort_dirty = true;
 
		} else {
 
			/* Free the name, because we did not assign it yet */
 
			DeleteName(str);
 
		}
 
	} else { /* Delete sign */
 
		if (flags & DC_EXEC) {
 
			Sign *si = GetSign(p1);
 

	
 
			MarkSignDirty(si);
 
			DeleteSign(si);
 

	
 
			InvalidateWindow(WC_SIGN_LIST, 0);
 
			_sign_sort_dirty = true;
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
/**
 
 *
 
 * Callback function that is called after a sign is placed
 
 *
 
 */
 
void CcPlaceSign(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		ShowRenameSignWindow(_new_sign);
 
		ResetObjectToPlace();
 
	}
 
}
 

	
 
/**
 
 *
 
 * PlaceProc function, called when someone pressed the button if the
 
 *  sign-tool is selected
 
 *
 
 */
 
void PlaceProc_Sign(TileIndex tile)
 
{
 
	DoCommandP(tile, 0, 0, CcPlaceSign, CMD_PLACE_SIGN | CMD_MSG(STR_2809_CAN_T_PLACE_SIGN_HERE));
 
}
 

	
 
/**
 
 *
 
 * Initialize the signs
 
 *
 
 */
 
void InitializeSigns(void)
 
{
 
	CleanPool(&_Sign_pool);
 
	AddBlockToPool(&_Sign_pool);
 
}
 

	
 
static const SaveLoad _sign_desc[] = {
 
      SLE_VAR(Sign, str,   SLE_UINT16),
 
  SLE_CONDVAR(Sign, x,     SLE_FILE_I16 | SLE_VAR_I32, 0, 4),
 
  SLE_CONDVAR(Sign, y,     SLE_FILE_I16 | SLE_VAR_I32, 0, 4),
 
  SLE_CONDVAR(Sign, x,     SLE_INT32,                  5, SL_MAX_VERSION),
 
  SLE_CONDVAR(Sign, y,     SLE_INT32,                  5, SL_MAX_VERSION),
 
  SLE_CONDVAR(Sign, owner, SLE_UINT8,                  6, SL_MAX_VERSION),
 
      SLE_VAR(Sign, z,     SLE_UINT8),
 
	SLE_END()
 
};
 

	
 
/**
 
 *
 
 * Save all signs
 
 *
 
 */
 
static void Save_SIGN(void)
 
{
 
	Sign *si;
 

	
 
	FOR_ALL_SIGNS(si) {
 
		SlSetArrayIndex(si->index);
 
		SlObject(si, _sign_desc);
 
	}
 
}
 

	
 
/**
 
 *
 
 * Load all signs
 
 *
 
 */
 
static void Load_SIGN(void)
 
{
 
	int index;
 
	while ((index = SlIterateArray()) != -1) {
 
		Sign *si;
 

	
 
		if (!AddBlockIfNeeded(&_Sign_pool, index))
 
			error("Signs: failed loading savegame: too many signs");
 

	
 
		si = GetSign(index);
 
		SlObject(si, _sign_desc);
 
	}
 

	
 
	_sign_sort_dirty = true;
 
}
 

	
 
const ChunkHandler _sign_chunk_handlers[] = {
 
	{ 'SIGN', Save_SIGN, Load_SIGN, CH_ARRAY | CH_LAST},
 
};
src/smallmap_gui.c
Show inline comments
 
deleted file
src/smallmap_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "bridge_map.h"
 
#include "clear_map.h"
 
#include "industry_map.h"
 
#include "station_map.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "gui.h"
 
#include "tree_map.h"
 
#include "tunnel_map.h"
 
#include "window.h"
 
#include "gfx.h"
 
#include "viewport.h"
 
#include "player.h"
 
#include "vehicle.h"
 
#include "town.h"
 
#include "sound.h"
 
#include "variables.h"
 

	
 
static const Widget _smallmap_widgets[] = {
 
{  WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},
 
{   WWT_CAPTION,  RESIZE_RIGHT,    13,    11,   433,     0,    13, STR_00B0_MAP,            STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_STICKYBOX,     RESIZE_LR,    13,   434,   445,     0,    13, 0x0,                     STR_STICKY_BUTTON},
 
{     WWT_PANEL,     RESIZE_RB,    13,     0,   445,    14,   257, 0x0,                     STR_NULL},
 
{     WWT_INSET,     RESIZE_RB,    13,     2,   443,    16,   255, 0x0,                     STR_NULL},
 
{    WWT_IMGBTN,   RESIZE_LRTB,    13,   380,   401,   258,   279, SPR_IMG_SHOW_COUNTOURS,  STR_0191_SHOW_LAND_CONTOURS_ON_MAP},
 
{    WWT_IMGBTN,   RESIZE_LRTB,    13,   402,   423,   258,   279, SPR_IMG_SHOW_VEHICLES,   STR_0192_SHOW_VEHICLES_ON_MAP},
 
{    WWT_IMGBTN,   RESIZE_LRTB,    13,   424,   445,   258,   279, SPR_IMG_INDUSTRY,        STR_0193_SHOW_INDUSTRIES_ON_MAP},
 
{    WWT_IMGBTN,   RESIZE_LRTB,    13,   380,   401,   280,   301, SPR_IMG_SHOW_ROUTES,     STR_0194_SHOW_TRANSPORT_ROUTES_ON},
 
{    WWT_IMGBTN,   RESIZE_LRTB,    13,   402,   423,   280,   301, SPR_IMG_PLANTTREES,      STR_0195_SHOW_VEGETATION_ON_MAP},
 
{    WWT_IMGBTN,   RESIZE_LRTB,    13,   424,   445,   280,   301, SPR_IMG_COMPANY_GENERAL, STR_0196_SHOW_LAND_OWNERS_ON_MAP},
 
{    WWT_IMGBTN,   RESIZE_LRTB,    13,   358,   379,   258,   279, SPR_IMG_SMALLMAP,        STR_SMALLMAP_CENTER},
 
{    WWT_IMGBTN,   RESIZE_LRTB,    13,   358,   379,   280,   301, SPR_IMG_TOWN,            STR_0197_TOGGLE_TOWN_NAMES_ON_OFF},
 
{     WWT_PANEL,    RESIZE_RTB,    13,     0,   357,   258,   301, 0x0,                     STR_NULL},
 
{     WWT_PANEL,    RESIZE_RTB,    13,     0,   433,   302,   313, 0x0,                     STR_NULL},
 
{ WWT_RESIZEBOX,   RESIZE_LRTB,    13,   434,   445,   302,   313, 0x0,                     STR_RESIZE_BUTTON},
 
{  WIDGETS_END},
 
};
 

	
 
static int _smallmap_type;
 
static bool _smallmap_show_towns = true;
 

	
 
#define MK(a,b) a, b
 
#define MKEND() 0xFFFF
 
#define MS(a,b) (a | 0x100), b
 

	
 
/* Legend text giving the colours to look for on the minimap */
 
static const uint16 _legend_land_contours[] = {
 
	MK(0x5A, STR_00F0_100M),
 
	MK(0x5C, STR_00F1_200M),
 
	MK(0x5E, STR_00F2_300M),
 
	MK(0x1F, STR_00F3_400M),
 
	MK(0x27, STR_00F4_500M),
 

	
 
	MS(0xD7, STR_00EB_ROADS),
 
	MK(0x0A, STR_00EC_RAILROADS),
 
	MK(0x98, STR_00ED_STATIONS_AIRPORTS_DOCKS),
 
	MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
 
	MK(0x0F, STR_00EF_VEHICLES),
 
	MKEND()
 
};
 

	
 
static const uint16 _legend_vehicles[] = {
 
	MK(0xB8, STR_00F5_TRAINS),
 
	MK(0xBF, STR_00F6_ROAD_VEHICLES),
 
	MK(0x98, STR_00F7_SHIPS),
 
	MK(0x0F, STR_00F8_AIRCRAFT),
 
	MS(0xD7, STR_00F9_TRANSPORT_ROUTES),
 
	MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
 
	MKEND()
 
};
 

	
 
static const uint16 _legend_industries_normal[] = {
 
	MK(0xD7, STR_00FA_COAL_MINE),
 
	MK(0xB8, STR_00FB_POWER_STATION),
 
	MK(0x56, STR_00FC_FOREST),
 
	MK(0xC2, STR_00FD_SAWMILL),
 
	MK(0xBF, STR_00FE_OIL_REFINERY),
 
	MK(0x0F, STR_0105_BANK),
 

	
 
	MS(0x30, STR_00FF_FARM),
 
	MK(0xAE, STR_0100_FACTORY),
 
	MK(0x98, STR_0102_OIL_WELLS),
 
	MK(0x37, STR_0103_IRON_ORE_MINE),
 
	MK(0x0A, STR_0104_STEEL_MILL),
 
	MKEND()
 
};
 

	
 
static const uint16 _legend_industries_hilly[] = {
 
	MK(0xD7, STR_00FA_COAL_MINE),
 
	MK(0xB8, STR_00FB_POWER_STATION),
 
	MK(0x56, STR_00FC_FOREST),
 
	MK(0x0A, STR_0106_PAPER_MILL),
 
	MK(0xBF, STR_00FE_OIL_REFINERY),
 
	MK(0x37, STR_0108_FOOD_PROCESSING_PLANT),
 
	MS(0x30, STR_00FF_FARM),
 

	
 
	MK(0xAE, STR_0101_PRINTING_WORKS),
 
	MK(0x98, STR_0102_OIL_WELLS),
 
	MK(0xC2, STR_0107_GOLD_MINE),
 
	MK(0x0F, STR_0105_BANK),
 
	MKEND()
 
};
 

	
 
static const uint16 _legend_industries_desert[] = {
 
	MK(0xBF, STR_00FE_OIL_REFINERY),
 
	MK(0x98, STR_0102_OIL_WELLS),
 
	MK(0x0F, STR_0105_BANK),
 
	MK(0xB8, STR_0109_DIAMOND_MINE),
 
	MK(0x37, STR_0108_FOOD_PROCESSING_PLANT),
 
	MK(0x0A, STR_010A_COPPER_ORE_MINE),
 
	MK(0x30, STR_00FF_FARM),
 
	MS(0x56, STR_010B_FRUIT_PLANTATION),
 

	
 
	MK(0x27, STR_010C_RUBBER_PLANTATION),
 
	MK(0x25, STR_010D_WATER_SUPPLY),
 
	MK(0xD0, STR_010E_WATER_TOWER),
 
	MK(0xAE, STR_0100_FACTORY),
 
	MK(0xC2, STR_010F_LUMBER_MILL),
 
	MKEND()
 
};
 

	
 
static const uint16 _legend_industries_candy[] = {
 
	MK(0x30, STR_0110_COTTON_CANDY_FOREST),
 
	MK(0xAE, STR_0111_CANDY_FACTORY),
 
	MK(0x27, STR_0112_BATTERY_FARM),
 
	MK(0x37, STR_0113_COLA_WELLS),
 
	MK(0xD0, STR_0114_TOY_SHOP),
 
	MK(0x0A, STR_0115_TOY_FACTORY),
 
	MS(0x25, STR_0116_PLASTIC_FOUNTAINS),
 

	
 
	MK(0xB8, STR_0117_FIZZY_DRINK_FACTORY),
 
	MK(0x98, STR_0118_BUBBLE_GENERATOR),
 
	MK(0xC2, STR_0119_TOFFEE_QUARRY),
 
	MK(0x0F, STR_011A_SUGAR_MINE),
 
	MKEND()
 
};
 

	
 
static const uint16 _legend_routes[] = {
 
	MK(0xD7, STR_00EB_ROADS),
 
	MK(0x0A, STR_00EC_RAILROADS),
 
	MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
 
	MS(0x56, STR_011B_RAILROAD_STATION),
 

	
 
	MK(0xC2, STR_011C_TRUCK_LOADING_BAY),
 
	MK(0xBF, STR_011D_BUS_STATION),
 
	MK(0xB8, STR_011E_AIRPORT_HELIPORT),
 
	MK(0x98, STR_011F_DOCK),
 
	MKEND()
 
};
 

	
 
static const uint16 _legend_vegetation[] = {
 
	MK(0x52, STR_0120_ROUGH_LAND),
 
	MK(0x54, STR_0121_GRASS_LAND),
 
	MK(0x37, STR_0122_BARE_LAND),
 
	MK(0x25, STR_0123_FIELDS),
 
	MK(0x57, STR_0124_TREES),
 
	MK(0xD0, STR_00FC_FOREST),
 
	MS(0x0A, STR_0125_ROCKS),
 

	
 
	MK(0xC2, STR_012A_DESERT),
 
	MK(0x98, STR_012B_SNOW),
 
	MK(0xD7, STR_00F9_TRANSPORT_ROUTES),
 
	MK(0xB5, STR_00EE_BUILDINGS_INDUSTRIES),
 
	MKEND()
 
};
 

	
 
static const uint16 _legend_land_owners[] = {
 
	MK(0xCA, STR_0126_WATER),
 
	MK(0x54, STR_0127_NO_OWNER),
 
	MK(0xB4, STR_0128_TOWNS),
 
	MK(0x20, STR_0129_INDUSTRIES),
 
	MKEND()
 
};
 
#undef MK
 
#undef MS
 
#undef MKEND
 

	
 

	
 
enum { IND_OFFS = 6 };
 
static const uint16 * const _legend_table[] = {
 
	_legend_land_contours,
 
	_legend_vehicles,
 
	NULL,
 
	_legend_routes,
 
	_legend_vegetation,
 
	_legend_land_owners,
 

	
 
	_legend_industries_normal,
 
	_legend_industries_hilly,
 
	_legend_industries_desert,
 
	_legend_industries_candy,
 
};
 

	
 
#if defined(OTTD_ALIGNMENT)
 
	static inline void WRITE_PIXELS(Pixel* d, uint32 val)
 
	{
 
#	if defined(TTD_BIG_ENDIAN)
 
		d[0] = GB(val, 24, 8);
 
		d[1] = GB(val, 16, 8);
 
		d[2] = GB(val,  8, 8);
 
		d[3] = GB(val,  0, 8);
 
#	elif defined(TTD_LITTLE_ENDIAN)
 
		d[0] = GB(val,  0, 8);
 
		d[1] = GB(val,  8, 8);
 
		d[2] = GB(val, 16, 8);
 
		d[3] = GB(val, 24, 8);
 
#	endif
 
	}
 

	
 
/* need to use OR, otherwise we will overwrite the wrong pixels at the edges :( */
 
	static inline void WRITE_PIXELS_OR(Pixel *d, uint32 val)
 
	{
 
#	if defined(TTD_BIG_ENDIAN)
 
		d[0] |= GB(val, 24, 8);
 
		d[1] |= GB(val, 16, 8);
 
		d[2] |= GB(val,  8, 8);
 
		d[3] |= GB(val,  0, 8);
 
#	elif defined(TTD_LITTLE_ENDIAN)
 
		d[0] |= GB(val,  0, 8);
 
		d[1] |= GB(val,  8, 8);
 
		d[2] |= GB(val, 16, 8);
 
		d[3] |= GB(val, 24, 8);
 
#	endif
 
	}
 
#else
 
#	define WRITE_PIXELS(dst, val)   *(uint32*)(dst) = (val);
 
#	define WRITE_PIXELS_OR(dst,val) *(uint32*)(dst) |= (val);
 
#endif
 

	
 
#define MKCOLOR(x) TO_LE32X(x)
 

	
 
/* Height encodings; 16 levels XXX - needs updating for more/finer heights! */
 
static const uint32 _map_height_bits[16] = {
 
	MKCOLOR(0x5A5A5A5A),
 
	MKCOLOR(0x5A5B5A5B),
 
	MKCOLOR(0x5B5B5B5B),
 
	MKCOLOR(0x5B5C5B5C),
 
	MKCOLOR(0x5C5C5C5C),
 
	MKCOLOR(0x5C5D5C5D),
 
	MKCOLOR(0x5D5D5D5D),
 
	MKCOLOR(0x5D5E5D5E),
 
	MKCOLOR(0x5E5E5E5E),
 
	MKCOLOR(0x5E5F5E5F),
 
	MKCOLOR(0x5F5F5F5F),
 
	MKCOLOR(0x5F1F5F1F),
 
	MKCOLOR(0x1F1F1F1F),
 
	MKCOLOR(0x1F271F27),
 
	MKCOLOR(0x27272727),
 
	MKCOLOR(0x27272727),
 
};
 

	
 
typedef struct AndOr {
 
	uint32 mor;
 
	uint32 mand;
 
} AndOr;
 

	
 
static inline uint32 ApplyMask(uint32 colour, const AndOr *mask)
 
{
 
	return (colour & mask->mand) | mask->mor;
 
}
 

	
 

	
 
static const AndOr _smallmap_contours_andor[] = {
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0x000A0A00), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0x98989898), MKCOLOR(0x00000000)},
 
	{MKCOLOR(0xCACACACA), MKCOLOR(0x00000000)},
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0xB5B5B5B5), MKCOLOR(0x00000000)},
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x000A0A00), MKCOLOR(0xFF0000FF)},
 
};
 

	
 
static const AndOr _smallmap_vehicles_andor[] = {
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0xCACACACA), MKCOLOR(0x00000000)},
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0xB5B5B5B5), MKCOLOR(0x00000000)},
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
 
};
 

	
 
static const AndOr _smallmap_vegetation_andor[] = {
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00575700), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0xCACACACA), MKCOLOR(0x00000000)},
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0xB5B5B5B5), MKCOLOR(0x00000000)},
 
	{MKCOLOR(0x00000000), MKCOLOR(0xFFFFFFFF)},
 
	{MKCOLOR(0x00B5B500), MKCOLOR(0xFF0000FF)},
 
	{MKCOLOR(0x00D7D700), MKCOLOR(0xFF0000FF)},
 
};
 

	
 
typedef uint32 GetSmallMapPixels(TileIndex tile); // typedef callthrough function
 

	
 
/**
 
 * Draws one column of the small map in a certain mode onto the screen buffer. This
 
 * function looks exactly the same for all types
 
 *
 
 * @param dst Pointer to a part of the screen buffer to write to.
 
 * @param xc The X coordinate of the first tile in the column.
 
 * @param yc The Y coordinate of the first tile in the column
 
 * @param pitch Number of pixels to advance in the screen buffer each time a pixel is written.
 
 * @param reps Number of lines to draw
 
 * @param mask ?
 
 * @param proc Pointer to the colour function
 
 * @see GetSmallMapPixels(TileIndex)
 
 */
 
static void DrawSmallMapStuff(Pixel *dst, uint xc, uint yc, int pitch, int reps, uint32 mask, GetSmallMapPixels *proc)
 
{
 
	Pixel *dst_ptr_end = _screen.dst_ptr + _screen.width * _screen.height - _screen.width;
 

	
 
	do {
 
		// check if the tile (xc,yc) is within the map range
 
		if (xc < MapMaxX() && yc < MapMaxY()) {
 
			// check if the dst pointer points to a pixel inside the screen buffer
 
			if (dst > _screen.dst_ptr && dst < dst_ptr_end)
 
				WRITE_PIXELS_OR(dst, proc(TileXY(xc, yc)) & mask);
 
		}
 
	// switch to next tile in the column
 
	} while (xc++, yc++, dst += pitch, --reps != 0);
 
}
 

	
 

	
 
static inline TileType GetEffectiveTileType(TileIndex tile)
 
{
 
	TileType t = GetTileType(tile);
 

	
 
	if (t == MP_TUNNELBRIDGE) {
 
		TransportType tt;
 

	
 
		if (IsTunnel(tile)) {
 
			tt = GetTunnelTransportType(tile);
 
		} else {
 
			tt = GetBridgeTransportType(tile);
 
		}
 
		switch (tt) {
 
			case TRANSPORT_RAIL: t = MP_RAILWAY; break;
 
			case TRANSPORT_ROAD: t = MP_STREET;  break;
 
			default:             t = MP_WATER;   break;
 
		}
 
	}
 
	return t;
 
}
 

	
 
/**
 
 * Return the color a tile would be displayed with in the small map in mode "Contour".
 
 * @param tile The tile of which we would like to get the color.
 
 * @return The color of tile in the small map in mode "Contour"
 
 */
 
static inline uint32 GetSmallMapContoursPixels(TileIndex tile)
 
{
 
	TileType t = GetEffectiveTileType(tile);
 

	
 
	return
 
		ApplyMask(_map_height_bits[TileHeight(tile)], &_smallmap_contours_andor[t]);
 
}
 

	
 
/**
 
 * Return the color a tile would be displayed with in the small map in mode "Vehicles".
 
 *
 
 * @param t The tile of which we would like to get the color.
 
 * @return The color of tile in the small map in mode "Vehicles"
 
 */
 
static inline uint32 GetSmallMapVehiclesPixels(TileIndex tile)
 
{
 
	TileType t = GetEffectiveTileType(tile);
 

	
 
	return ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
 
}
 

	
 
/* Industry colours... a total of 175 gfx - XXX - increase if more industries */
 
static const byte _industry_smallmap_colors[175] = {
 
	215, 215, 215, 215, 215, 215, 215, 184,
 
	184, 184, 184, 194, 194, 194, 194, 194,
 
	 86,  86, 191, 191, 191, 191, 191, 191,
 
	152, 152, 152, 152, 152, 152, 152, 152,
 
	152,  48,  48,  48,  48,  48,  48, 174,
 
	174, 174, 174, 174, 174, 174, 174,  10,
 
	 10,  10,  10,  10,  10,  10,  10,  10,
 
	 10,  10,  15,  15,  55,  55,  55,  55,
 
	 10,  10,  10,  10,  10,  10,  10,  10,
 
	194, 194, 194, 194, 194, 194, 194, 194,
 
	194, 194, 194, 194, 194, 194, 194, 194,
 
	194,  15,  15, 184, 184, 184, 184, 184,
 
	184, 184, 184, 184,  55,  55,  55,  55,
 
	 55,  55,  55,  55,  55,  55,  55,  55,
 
	 55,  55,  55,  55,  86,  39,  37,  37,
 
	208, 174, 174, 174, 174, 194, 194, 194,
 
	194,  48,  48, 174, 174, 174, 174,  39,
 
	 39,  55, 208, 208, 208, 208,  10,  10,
 
	 10,  10,  10,  10,  37,  37,  37,  37,
 
	 37,  37,  37,  37, 184, 184, 184, 184,
 
	152, 152, 152, 152, 194, 194, 194,  15,
 
	 15,  15,  15,  15,  15,  15,  15,
 
};
 

	
 
/**
 
 * Return the color a tile would be displayed with in the small map in mode "Industries".
 
 *
 
 * @param tile The tile of which we would like to get the color.
 
 * @return The color of tile in the small map in mode "Industries"
 
 */
 
static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile)
 
{
 
	TileType t = GetEffectiveTileType(tile);
 

	
 
	if (t == MP_INDUSTRY) {
 
		return _industry_smallmap_colors[GetIndustryGfx(tile)] * 0x01010101;
 
	}
 

	
 
	return ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
 
}
 

	
 
/**
 
 * Return the color a tile would be displayed with in the small map in mode "Routes".
 
 *
 
 * @param t The tile of which we would like to get the color.
 
 * @return The color of tile  in the small map in mode "Routes"
 
 */
 
static inline uint32 GetSmallMapRoutesPixels(TileIndex tile)
 
{
 
	TileType t = GetEffectiveTileType(tile);
 
	uint32 bits;
 

	
 
	if (t == MP_STATION) {
 
		switch (GetStationType(tile)) {
 
			case STATION_RAIL:    bits = MKCOLOR(0x56565656); break;
 
			case STATION_AIRPORT: bits = MKCOLOR(0xB8B8B8B8); break;
 
			case STATION_TRUCK:   bits = MKCOLOR(0xC2C2C2C2); break;
 
			case STATION_BUS:     bits = MKCOLOR(0xBFBFBFBF); break;
 
			case STATION_DOCK:    bits = MKCOLOR(0x98989898); break;
 
			default:              bits = MKCOLOR(0xFFFFFFFF); break;
 
		}
 
	} else {
 
		// ground color
 
		bits = ApplyMask(MKCOLOR(0x54545454), &_smallmap_contours_andor[t]);
 
	}
 
	return bits;
 
}
 

	
 

	
 
static const uint32 _vegetation_clear_bits[] = {
 
	MKCOLOR(0x54545454), ///< full grass
 
	MKCOLOR(0x52525252), ///< rough land
 
	MKCOLOR(0x0A0A0A0A), ///< rocks
 
	MKCOLOR(0x25252525), ///< fields
 
	MKCOLOR(0x98989898), ///< snow
 
	MKCOLOR(0xC2C2C2C2), ///< desert
 
	MKCOLOR(0x54545454), ///< unused
 
	MKCOLOR(0x54545454), ///< unused
 
};
 

	
 
static inline uint32 GetSmallMapVegetationPixels(TileIndex tile)
 
{
 
	TileType t = GetEffectiveTileType(tile);
 
	uint32 bits;
 

	
 
	switch (t) {
 
		case MP_CLEAR:
 
			if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) < 3) {
 
				bits = MKCOLOR(0x37373737);
 
			} else {
 
				bits = _vegetation_clear_bits[GetClearGround(tile)];
 
			}
 
			break;
 

	
 
		case MP_INDUSTRY:
 
			bits = GetIndustryType(tile) == IT_FOREST ? MKCOLOR(0xD0D0D0D0) : MKCOLOR(0xB5B5B5B5);
 
			break;
 

	
 
		case MP_TREES:
 
			if (GetTreeGround(tile) == TREE_GROUND_SNOW_DESERT) {
 
				bits = (_opt.landscape == LT_HILLY) ? MKCOLOR(0x98575798) : MKCOLOR(0xC25757C2);
 
			} else {
 
				bits = MKCOLOR(0x54575754);
 
			}
 
			break;
 

	
 
		default:
 
			bits = ApplyMask(MKCOLOR(0x54545454), &_smallmap_vehicles_andor[t]);
 
			break;
 
	}
 

	
 
	return bits;
 
}
 

	
 

	
 
static uint32 _owner_colors[OWNER_END + 1];
 

	
 
/**
 
 * Return the color a tile would be displayed with in the small map in mode "Owner".
 
 *
 
 * @param t The tile of which we would like to get the color.
 
 * @return The color of tile in the small map in mode "Owner"
 
 */
 
static inline uint32 GetSmallMapOwnerPixels(TileIndex tile)
 
{
 
	Owner o;
 

	
 
	switch (GetTileType(tile)) {
 
		case MP_INDUSTRY: o = OWNER_END;          break;
 
		case MP_HOUSE:    o = OWNER_TOWN;         break;
 
		default:          o = GetTileOwner(tile); break;
 
	}
 

	
 
	return _owner_colors[o];
 
}
 

	
 

	
 
static const uint32 _smallmap_mask_left[3] = {
 
	MKCOLOR(0xFF000000),
 
	MKCOLOR(0xFFFF0000),
 
	MKCOLOR(0xFFFFFF00),
 
};
 

	
 
static const uint32 _smallmap_mask_right[] = {
 
	MKCOLOR(0x000000FF),
 
	MKCOLOR(0x0000FFFF),
 
	MKCOLOR(0x00FFFFFF),
 
};
 

	
 
/* each tile has 4 x pixels and 1 y pixel */
 

	
 
static GetSmallMapPixels *_smallmap_draw_procs[] = {
 
	GetSmallMapContoursPixels,
 
	GetSmallMapVehiclesPixels,
 
	GetSmallMapIndustriesPixels,
 
	GetSmallMapRoutesPixels,
 
	GetSmallMapVegetationPixels,
 
	GetSmallMapOwnerPixels,
 
};
 

	
 
static const byte _vehicle_type_colors[6] = {
 
	184, 191, 152, 15, 215, 184
 
};
 

	
 

	
 
static void DrawVertMapIndicator(int x, int y, int x2, int y2)
 
{
 
	GfxFillRect(x, y,      x2, y + 3, 69);
 
	GfxFillRect(x, y2 - 3, x2, y2,    69);
 
}
 

	
 
static void DrawHorizMapIndicator(int x, int y, int x2, int y2)
 
{
 
	GfxFillRect(x,      y, x + 3, y2, 69);
 
	GfxFillRect(x2 - 3, y, x2,    y2, 69);
 
}
 

	
 
/**
 
 * Draws the small map.
 
 *
 
 * Basically, the small map is draw column of pixels by column of pixels. The pixels
 
 * are drawn directly into the screen buffer. The final map is drawn in multiple passes.
 
 * The passes are:
 
 * <ol><li>The colors of tiles in the different modes.</li>
 
 * <li>Town names (optional)</li>
 
 *
 
 * @param dpi pointer to pixel to write onto
 
 * @param w pointer to Window struct
 
 * @param type type of map requested (vegetation, owners, routes, etc)
 
 * @param show_towns true if the town names should be displayed, false if not.
 
 */
 
static void DrawSmallMap(DrawPixelInfo *dpi, Window *w, int type, bool show_towns)
 
{
 
	DrawPixelInfo *old_dpi;
 
	int dx,dy, x, y, x2, y2;
 
	Pixel *ptr;
 
	int tile_x;
 
	int tile_y;
 
	ViewPort *vp;
 

	
 
	old_dpi = _cur_dpi;
 
	_cur_dpi = dpi;
 

	
 
	/* clear it */
 
	GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
 

	
 
	/* setup owner table */
 
	if (type == 5) {
 
		const Player *p;
 

	
 
		/* fill with some special colors */
 
		_owner_colors[OWNER_TOWN] = MKCOLOR(0xB4B4B4B4);
 
		_owner_colors[OWNER_NONE] = MKCOLOR(0x54545454);
 
		_owner_colors[OWNER_WATER] = MKCOLOR(0xCACACACA);
 
		_owner_colors[OWNER_END]   = MKCOLOR(0x20202020); /* industry */
 

	
 
		/* now fill with the player colors */
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active) {
 
				_owner_colors[p->index] =
 
					_colour_gradient[p->player_color][5] * 0x01010101;
 
			}
 
		}
 
	}
 

	
 
	tile_x = WP(w,smallmap_d).scroll_x / TILE_SIZE;
 
	tile_y = WP(w,smallmap_d).scroll_y / TILE_SIZE;
 

	
 
	dx = dpi->left + WP(w,smallmap_d).subscroll;
 
	tile_x -= dx / 4;
 
	tile_y += dx / 4;
 
	dx &= 3;
 

	
 
	dy = dpi->top;
 
	tile_x += dy / 2;
 
	tile_y += dy / 2;
 

	
 
	if (dy & 1) {
 
		tile_x++;
 
		dx += 2;
 
		if (dx > 3) {
 
			dx -= 4;
 
			tile_x--;
 
			tile_y++;
 
		}
 
	}
 

	
 
	ptr = dpi->dst_ptr - dx - 4;
 
	x = - dx - 4;
 
	y = 0;
 

	
 
	for (;;) {
 
		uint32 mask = 0xFFFFFFFF;
 
		int reps;
 
		int t;
 

	
 
		/* distance from left edge */
 
		if (x < 0) {
 
			if (x < -3) goto skip_column;
 
			/* mask to use at the left edge */
 
			mask = _smallmap_mask_left[x + 3];
 
		}
 

	
 
		/* distance from right edge */
 
		t = dpi->width - x;
 
		if (t < 4) {
 
			if (t <= 0) break; /* exit loop */
 
			/* mask to use at the right edge */
 
			mask &= _smallmap_mask_right[t - 1];
 
		}
 

	
 
		/* number of lines */
 
		reps = (dpi->height - y + 1) / 2;
 
		if (reps > 0) {
 
//			assert(ptr >= dpi->dst_ptr);
 
			DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, _smallmap_draw_procs[type]);
 
		}
 

	
 
skip_column:
 
		if (y == 0) {
 
			tile_y++;
 
			y++;
 
			ptr += dpi->pitch;
 
		} else {
 
			tile_x--;
 
			y--;
 
			ptr -= dpi->pitch;
 
		}
 
		ptr += 2;
 
		x += 2;
 
	}
 

	
 
	/* draw vehicles? */
 
	if (type == 0 || type == 1) {
 
		Vehicle *v;
 
		bool skip;
 
		byte color;
 

	
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->type != VEH_Special &&
 
					(v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0) {
 
				// Remap into flat coordinates.
 
				Point pt = RemapCoords(
 
					v->x_pos / TILE_SIZE - WP(w,smallmap_d).scroll_x / TILE_SIZE, // divide each one separately because (a-b)/c != a/c-b/c in integer world
 
					v->y_pos / TILE_SIZE - WP(w,smallmap_d).scroll_y / TILE_SIZE, //    dtto
 
					0);
 
				x = pt.x;
 
				y = pt.y;
 

	
 
				// Check if y is out of bounds?
 
				y -= dpi->top;
 
				if (!IS_INT_INSIDE(y, 0, dpi->height)) continue;
 

	
 
				// Default is to draw both pixels.
 
				skip = false;
 

	
 
				// Offset X coordinate
 
				x -= WP(w,smallmap_d).subscroll + 3 + dpi->left;
 

	
 
				if (x < 0) {
 
					// if x+1 is 0, that means we're on the very left edge,
 
					//  and should thus only draw a single pixel
 
					if (++x != 0) continue;
 
					skip = true;
 
				} else if (x >= dpi->width - 1) {
 
					// Check if we're at the very right edge, and if so draw only a single pixel
 
					if (x != dpi->width - 1) continue;
 
					skip = true;
 
				}
 

	
 
				// Calculate pointer to pixel and the color
 
				ptr = dpi->dst_ptr + y * dpi->pitch + x;
 
				color = (type == 1) ? _vehicle_type_colors[v->type-0x10] : 0xF;
 

	
 
				// And draw either one or two pixels depending on clipping
 
				ptr[0] = color;
 
				if (!skip) ptr[1] = color;
 
			}
 
		}
 
	}
 

	
 
	if (show_towns) {
 
		const Town *t;
 

	
 
		FOR_ALL_TOWNS(t) {
 
			// Remap the town coordinate
 
			Point pt = RemapCoords(
 
				(int)(TileX(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_x) / TILE_SIZE,
 
				(int)(TileY(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_y) / TILE_SIZE,
 
				0);
 
			x = pt.x - WP(w,smallmap_d).subscroll + 3 - (t->sign.width_2 >> 1);
 
			y = pt.y;
 

	
 
			// Check if the town sign is within bounds
 
			if (x + t->sign.width_2 > dpi->left &&
 
					x < dpi->left + dpi->width &&
 
					y + 6 > dpi->top &&
 
					y < dpi->top + dpi->height) {
 
				// And draw it.
 
				SetDParam(0, t->index);
 
				DrawString(x, y, STR_2056, 12);
 
			}
 
		}
 
	}
 

	
 
	// Draw map indicators
 
	{
 
		Point pt;
 

	
 
		// Find main viewport.
 
		vp = FindWindowById(WC_MAIN_WINDOW,0)->viewport;
 

	
 
		pt = RemapCoords(WP(w, smallmap_d).scroll_x, WP(w, smallmap_d).scroll_y, 0);
 

	
 
		x = vp->virtual_left - pt.x;
 
		y = vp->virtual_top - pt.y;
 
		x2 = (x + vp->virtual_width) / TILE_SIZE;
 
		y2 = (y + vp->virtual_height) / TILE_SIZE;
 
		x /= TILE_SIZE;
 
		y /= TILE_SIZE;
 

	
 
		x -= WP(w,smallmap_d).subscroll;
 
		x2 -= WP(w,smallmap_d).subscroll;
 

	
 
		DrawVertMapIndicator(x, y, x, y2);
 
		DrawVertMapIndicator(x2, y, x2, y2);
 

	
 
		DrawHorizMapIndicator(x, y, x2, y);
 
		DrawHorizMapIndicator(x, y2, x2, y2);
 
	}
 
	_cur_dpi = old_dpi;
 
}
 

	
 
void SmallMapCenterOnCurrentPos(Window *w)
 
{
 
	int x, y;
 
	ViewPort *vp;
 
	vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
 

	
 
	x  = ((vp->virtual_width  - (w->widget[4].right  - w->widget[4].left) * TILE_SIZE) / 2 + vp->virtual_left) / 4;
 
	y  = ((vp->virtual_height - (w->widget[4].bottom - w->widget[4].top ) * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
 
	WP(w, smallmap_d).scroll_x = (y - x) & ~0xF;
 
	WP(w, smallmap_d).scroll_y = (x + y) & ~0xF;
 
	SetWindowDirty(w);
 
}
 

	
 
static void SmallMapWindowProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			const uint16 *tbl;
 
			int x, y, y_org;
 
			DrawPixelInfo new_dpi;
 

	
 
			/* draw the window */
 
			SetDParam(0, STR_00E5_CONTOURS + _smallmap_type);
 
			DrawWindowWidgets(w);
 

	
 
			/* draw the legend */
 
			tbl = _legend_table[(_smallmap_type != 2) ? _smallmap_type : (_opt.landscape + IND_OFFS)];
 
			x = 4;
 
			y_org = w->height - 44 - 11;
 
			y = y_org;
 
			for (;;) {
 
				GfxFillRect(x,     y + 1, x + 8, y + 5, 0);
 
				GfxFillRect(x + 1, y + 2, x + 7, y + 4, (byte)tbl[0]);
 
				DrawString(x + 11, y, tbl[1], 0);
 

	
 
				tbl += 2;
 
				y += 6;
 

	
 
				if (tbl[0] == 0xFFFF) {
 
					break;
 
				} else if (tbl[0] & 0x100) {
 
					x += 123;
 
					y = y_org;
 
				}
 
			}
 

	
 
			if (!FillDrawPixelInfo(&new_dpi, 3, 17, w->width - 28 + 22, w->height - 64 - 11))
 
				return;
 

	
 
			DrawSmallMap(&new_dpi, w, _smallmap_type, _smallmap_show_towns);
 
		} break;
 

	
 
		case WE_CLICK:
 
			switch (e->we.click.widget) {
 
				case 4: { // Map window
 
					Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
 
					Point pt;
 

	
 
					/*
 
					 * XXX: scrolling with the left mouse button is done by subsequently
 
					 * clicking with the left mouse button; clicking once centers the
 
					 * large map at the selected point. So by unclicking the left mouse
 
					 * button here, it gets reclicked during the next inputloop, which
 
					 * would make it look like the mouse is being dragged, while it is
 
					 * actually being (virtually) clicked every inputloop.
 
					 */
 
					_left_button_clicked = false;
 

	
 
					pt = RemapCoords(WP(w,smallmap_d).scroll_x, WP(w,smallmap_d).scroll_y, 0);
 
					WP(w2, vp_d).scrollpos_x = pt.x + ((_cursor.pos.x - w->left + 2) << 4) - (w2->viewport->virtual_width >> 1);
 
					WP(w2, vp_d).scrollpos_y = pt.y + ((_cursor.pos.y - w->top - 16) << 4) - (w2->viewport->virtual_height >> 1);
 

	
 
					SetWindowDirty(w);
 
				} break;
 

	
 
				case 5:  // Show land contours
 
				case 6:  // Show vehicles
 
				case 7:  // Show industries
 
				case 8:  // Show transport routes
 
				case 9:  // Show vegetation
 
				case 10: // Show land owners
 
					RaiseWindowWidget(w, _smallmap_type + 5);
 
					_smallmap_type = e->we.click.widget - 5;
 
					LowerWindowWidget(w, _smallmap_type + 5);
 

	
 
					SetWindowDirty(w);
 
					SndPlayFx(SND_15_BEEP);
 
					break;
 

	
 
				case 11: // Center the smallmap again
 
					SmallMapCenterOnCurrentPos(w);
 

	
 
					SetWindowDirty(w);
 
					SndPlayFx(SND_15_BEEP);
 
					break;
 

	
 
				case 12: // Toggle town names
 
					ToggleWidgetLoweredState(w, 12);
 
					_smallmap_show_towns = IsWindowWidgetLowered(w, 12);
 

	
 
					SetWindowDirty(w);
 
					SndPlayFx(SND_15_BEEP);
 
					break;
 
				}
 
			break;
 

	
 
		case WE_RCLICK:
 
			if (e->we.click.widget == 4) {
 
				if (_scrolling_viewport) return;
 
				_scrolling_viewport = true;
 
				_cursor.delta.x = 0;
 
				_cursor.delta.y = 0;
 
			}
 
			break;
 

	
 
		case WE_MOUSELOOP:
 
			/* update the window every now and then */
 
			if ((++w->vscroll.pos & 0x1F) == 0) SetWindowDirty(w);
 
			break;
 

	
 
		case WE_SCROLL: {
 
			int x;
 
			int y;
 
			int sub;
 
			int hx;
 
			int hy;
 
			int hvx;
 
			int hvy;
 

	
 
			_cursor.fix_at = true;
 

	
 
			x = WP(w, smallmap_d).scroll_x;
 
			y = WP(w, smallmap_d).scroll_y;
 

	
 
			sub = WP(w, smallmap_d).subscroll + e->we.scroll.delta.x;
 

	
 
			x -= (sub >> 2) << 4;
 
			y += (sub >> 2) << 4;
 
			sub &= 3;
 

	
 
			x += (e->we.scroll.delta.y >> 1) << 4;
 
			y += (e->we.scroll.delta.y >> 1) << 4;
 

	
 
			if (e->we.scroll.delta.y & 1) {
 
				x += TILE_SIZE;
 
				sub += 2;
 
				if (sub > 3) {
 
					sub -= 4;
 
					x -= TILE_SIZE;
 
					y += TILE_SIZE;
 
				}
 
			}
 

	
 
			hx = (w->widget[4].right  - w->widget[4].left) / 2;
 
			hy = (w->widget[4].bottom - w->widget[4].top ) / 2;
 
			hvx = hx * -4 + hy * 8;
 
			hvy = hx *  4 + hy * 8;
 
			if (x < -hvx) {
 
				x = -hvx;
 
				sub = 0;
 
			}
 
			if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
 
				x = MapMaxX() * TILE_SIZE - hvx;
 
				sub = 0;
 
			}
 
			if (y < -hvy) {
 
				y = -hvy;
 
				sub = 0;
 
			}
 
			if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
 
				y = MapMaxY() * TILE_SIZE - hvy;
 
				sub = 0;
 
			}
 

	
 
			WP(w, smallmap_d).scroll_x = x;
 
			WP(w, smallmap_d).scroll_y = y;
 
			WP(w, smallmap_d).subscroll = sub;
 

	
 
			SetWindowDirty(w);
 
		} break;
 
	}
 
}
 

	
 
static const WindowDesc _smallmap_desc = {
 
	WDP_AUTO, WDP_AUTO, 446, 314,
 
	WC_SMALLMAP,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_smallmap_widgets,
 
	SmallMapWindowProc
 
};
 

	
 
void ShowSmallMap(void)
 
{
 
	Window *w;
 

	
 
	w = AllocateWindowDescFront(&_smallmap_desc, 0);
 
	if (w == NULL) return;
 

	
 
	LowerWindowWidget(w, _smallmap_type + 5);
 
	SetWindowWidgetLoweredState(w, 12, _smallmap_show_towns);
 
	w->resize.width = 350;
 
	w->resize.height = 250;
 

	
 
	SmallMapCenterOnCurrentPos(w);
 
}
 

	
 
/* Extra ViewPort Window Stuff */
 
static const Widget _extra_view_port_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                         STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   287,     0,    13, STR_EXTRA_VIEW_PORT_TITLE,        STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,     RESIZE_LR,    14,   288,   299,     0,    13, 0x0,                              STR_STICKY_BUTTON},
 
{      WWT_PANEL,     RESIZE_RB,    14,     0,   299,    14,   233, 0x0,                              STR_NULL},
 
{      WWT_INSET,     RESIZE_RB,    14,     2,   297,    16,   231, 0x0,                              STR_NULL},
 
{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,     0,    21,   234,   255, SPR_IMG_ZOOMIN,                   STR_017F_ZOOM_THE_VIEW_IN},
 
{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,    22,    43,   234,   255, SPR_IMG_ZOOMOUT,                  STR_0180_ZOOM_THE_VIEW_OUT},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,    44,   171,   234,   255, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW, STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   172,   298,   234,   255, STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN, STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT},
 
{      WWT_PANEL,    RESIZE_RTB,    14,   299,   299,   234,   255, 0x0,                              STR_NULL},
 
{      WWT_PANEL,    RESIZE_RTB,    14,     0,   287,   256,   267, 0x0,                              STR_NULL},
 
{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   288,   299,   256,   267, 0x0,                              STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static void ExtraViewPortWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE: /* Disable zoom in button */
 
		DisableWindowWidget(w, 5);
 
		break;
 

	
 
	case WE_PAINT:
 
		// set the number in the title bar
 
		SetDParam(0, w->window_number + 1);
 

	
 
		DrawWindowWidgets(w);
 
		DrawWindowViewport(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
			case 5: DoZoomInOutWindow(ZOOM_IN,  w); break;
 
			case 6: DoZoomInOutWindow(ZOOM_OUT, w); break;
 

	
 
		case 7: { /* location button (move main view to same spot as this view) 'Paste Location' */
 
			Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
 
			int x = WP(w, vp_d).scrollpos_x; // Where is the main looking at
 
			int y = WP(w, vp_d).scrollpos_y;
 

	
 
			// set this view to same location. Based on the center, adjusting for zoom
 
			WP(w2, vp_d).scrollpos_x =  x - (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
 
			WP(w2, vp_d).scrollpos_y =  y - (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
 
		} break;
 

	
 
		case 8: { /* inverse location button (move this view to same spot as main view) 'Copy Location' */
 
			const Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
 
			int x = WP(w2, const vp_d).scrollpos_x;
 
			int y = WP(w2, const vp_d).scrollpos_y;
 

	
 
			WP(w, vp_d).scrollpos_x =  x + (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
 
			WP(w, vp_d).scrollpos_y =  y + (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
 
		} break;
 
		}
 
		break;
 

	
 
	case WE_RESIZE:
 
		w->viewport->width          += e->we.sizing.diff.x;
 
		w->viewport->height         += e->we.sizing.diff.y;
 
		w->viewport->virtual_width  += e->we.sizing.diff.x;
 
		w->viewport->virtual_height += e->we.sizing.diff.y;
 
		break;
 

	
 
		case WE_SCROLL: {
 
			ViewPort *vp = IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y);
 

	
 
			if (vp == NULL) {
 
				_cursor.fix_at = false;
 
				_scrolling_viewport = false;
 
			}
 

	
 
			WP(w, vp_d).scrollpos_x += e->we.scroll.delta.x << vp->zoom;
 
			WP(w, vp_d).scrollpos_y += e->we.scroll.delta.y << vp->zoom;
 
		} break;
 

	
 
		case WE_MOUSEWHEEL:
 
			ZoomInOrOutToCursorWindow(e->we.wheel.wheel < 0, w);
 
			break;
 

	
 

	
 
		case WE_MESSAGE:
 
			/* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */
 
			if (e->we.message.wparam != w->window_number) break;
 
			HandleZoomMessage(w, w->viewport, 5, 6);
 
			break;
 
	}
 
}
 

	
 
static const WindowDesc _extra_view_port_desc = {
 
	WDP_AUTO, WDP_AUTO, 300, 268,
 
	WC_EXTRA_VIEW_PORT,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_extra_view_port_widgets,
 
	ExtraViewPortWndProc
 
};
 

	
 
void ShowExtraViewPortWindow(void)
 
{
 
	Window *w, *v;
 
	int i = 0;
 

	
 
	// find next free window number for extra viewport
 
	while (FindWindowById(WC_EXTRA_VIEW_PORT, i) != NULL) i++;
 

	
 
	w = AllocateWindowDescFront(&_extra_view_port_desc, i);
 
	if (w != NULL) {
 
		int x, y;
 
		// the main window with the main view
 
		v = FindWindowById(WC_MAIN_WINDOW, 0);
 
		// New viewport start ats (zero,zero)
 
		AssignWindowViewport(w, 3, 17, 294, 214, 0 , 0);
 

	
 
		// center on same place as main window (zoom is maximum, no adjustment needed)
 
		x = WP(v, vp_d).scrollpos_x;
 
		y = WP(v, vp_d).scrollpos_y;
 
		WP(w, vp_d).scrollpos_x = x + (v->viewport->virtual_width  - (294)) / 2;
 
		WP(w, vp_d).scrollpos_y = y + (v->viewport->virtual_height - (214)) / 2;
 
	}
 
}
src/sound.c
Show inline comments
 
deleted file
src/sound.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "mixer.h"
 
#include "sound.h"
 
#include "vehicle.h"
 
#include "window.h"
 
#include "viewport.h"
 
#include "fileio.h"
 
#include "newgrf_sound.h"
 

	
 
static uint _file_count;
 
static FileEntry *_files;
 

	
 
#define SOUND_SLOT 63
 
// Number of levels of panning per side
 
#define PANNING_LEVELS 16
 

	
 

	
 
static void OpenBankFile(const char *filename)
 
{
 
	FileEntry *fe;
 
	uint count;
 
	uint i;
 

	
 
	FioOpenFile(SOUND_SLOT, filename);
 
	count = FioReadDword() / 8;
 
	fe = calloc(count, sizeof(*fe));
 

	
 
	if (fe == NULL) {
 
		_file_count = 0;
 
		_files = NULL;
 
		return;
 
	}
 

	
 
	_file_count = count;
 
	_files = fe;
 

	
 
	FioSeekTo(0, SEEK_SET);
 

	
 
	for (i = 0; i != count; i++) {
 
		fe[i].file_offset = FioReadDword();
 
		fe[i].file_size = FioReadDword();
 
	}
 

	
 
	for (i = 0; i != count; i++, fe++) {
 
		char name[255];
 

	
 
		FioSeekTo(fe->file_offset, SEEK_SET);
 

	
 
		// Check for special case, see else case
 
		FioReadBlock(name, FioReadByte()); // Read the name of the sound
 
		if (strcmp(name, "Corrupt sound") != 0) {
 
			FioSeekTo(12, SEEK_CUR); // Skip past RIFF header
 

	
 
			// Read riff tags
 
			for (;;) {
 
				uint32 tag = FioReadDword();
 
				uint32 size = FioReadDword();
 

	
 
				if (tag == ' tmf') {
 
					FioReadWord(); // wFormatTag
 
					fe->channels = FioReadWord(); // wChannels
 
					FioReadDword();   // samples per second
 
					fe->rate = 11025; // seems like all samples should be played at this rate.
 
					FioReadDword();   // avg bytes per second
 
					FioReadWord();    // alignment
 
					fe->bits_per_sample = FioReadByte(); // bits per sample
 
					FioSeekTo(size - (2 + 2 + 4 + 4 + 2 + 1), SEEK_CUR);
 
				} else if (tag == 'atad') {
 
					fe->file_size = size;
 
					fe->file_offset = FioGetPos() | (SOUND_SLOT << 24);
 
					break;
 
				} else {
 
					fe->file_size = 0;
 
					break;
 
				}
 
			}
 
		} else {
 
			/*
 
			 * Special case for the jackhammer sound
 
			 * (name in sample.cat is "Corrupt sound")
 
			 * It's no RIFF file, but raw PCM data
 
			 */
 
			fe->channels = 1;
 
			fe->rate = 11025;
 
			fe->bits_per_sample = 8;
 
			fe->file_offset = FioGetPos() | (SOUND_SLOT << 24);
 
		}
 
	}
 
}
 

	
 
uint GetNumOriginalSounds(void)
 
{
 
	return _file_count;
 
}
 

	
 
static bool SetBankSource(MixerChannel *mc, uint bank)
 
{
 
	const FileEntry *fe;
 
	int8 *mem;
 
	uint i;
 

	
 
	if (bank >= GetNumSounds()) return false;
 
	fe = GetSound(bank);
 

	
 
	if (fe->file_size == 0) return false;
 

	
 
	mem = malloc(fe->file_size);
 
	if (mem == NULL) return false;
 

	
 
	FioSeekToFile(fe->file_offset);
 
	FioReadBlock(mem, fe->file_size);
 

	
 
	for (i = 0; i != fe->file_size; i++)
 
		mem[i] += -128; // Convert unsigned sound data to signed
 

	
 
	assert(fe->bits_per_sample == 8 && fe->channels == 1 && fe->file_size != 0 && fe->rate != 0);
 

	
 
	MxSetChannelRawSrc(mc, mem, fe->file_size, fe->rate, MX_AUTOFREE);
 

	
 
	return true;
 
}
 

	
 
bool SoundInitialize(const char *filename)
 
{
 
	OpenBankFile(filename);
 
	return true;
 
}
 

	
 
// Low level sound player
 
static void StartSound(uint sound, int panning, uint volume)
 
{
 
	MixerChannel *mc;
 
	uint left_vol, right_vol;
 

	
 
	if (volume == 0) return;
 
	mc = MxAllocateChannel();
 
	if (mc == NULL) return;
 
	if (!SetBankSource(mc, sound)) return;
 

	
 
	panning = clamp(panning, -PANNING_LEVELS, PANNING_LEVELS);
 
	left_vol = (volume * PANNING_LEVELS) - (volume * panning);
 
	right_vol = (volume * PANNING_LEVELS) + (volume * panning);
 
	MxSetChannelVolume(mc, left_vol * 128 / PANNING_LEVELS, right_vol * 128 / PANNING_LEVELS);
 
	MxActivateChannel(mc);
 
}
 

	
 

	
 
static const byte _vol_factor_by_zoom[] = {255, 190, 134};
 

	
 
static const byte _sound_base_vol[] = {
 
	128,  90, 128, 128, 128, 128, 128, 128,
 
	128,  90,  90, 128, 128, 128, 128, 128,
 
	128, 128, 128,  80, 128, 128, 128, 128,
 
	128, 128, 128, 128, 128, 128, 128, 128,
 
	128, 128,  90,  90,  90, 128,  90, 128,
 
	128,  90, 128, 128, 128,  90, 128, 128,
 
	128, 128, 128, 128,  90, 128, 128, 128,
 
	128,  90, 128, 128, 128, 128, 128, 128,
 
	128, 128,  90,  90,  90, 128, 128, 128,
 
	 90,
 
};
 

	
 
static const byte _sound_idx[] = {
 
	 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,  0,
 
	 1, 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,
 
};
 

	
 
void SndCopyToPool(void)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < _file_count; i++) {
 
		FileEntry *orig = &_files[_sound_idx[i]];
 
		FileEntry *fe = AllocateFileEntry();
 

	
 
		*fe = *orig;
 
		fe->volume = _sound_base_vol[i];
 
		fe->priority = 0;
 
	}
 
}
 

	
 
static void SndPlayScreenCoordFx(SoundFx sound, int x, int y)
 
{
 
	Window* const *wz;
 

	
 
	if (msf.effect_vol == 0) return;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		const ViewPort *vp = (*wz)->viewport;
 

	
 
		if (vp != NULL &&
 
				IS_INSIDE_1D(x, vp->virtual_left, vp->virtual_width) &&
 
				IS_INSIDE_1D(y, vp->virtual_top, vp->virtual_height)) {
 
			int left = (x - vp->virtual_left);
 

	
 
			StartSound(
 
				sound,
 
				left / (vp->virtual_width / ((PANNING_LEVELS << 1) + 1)) - PANNING_LEVELS,
 
				(GetSound(sound)->volume * msf.effect_vol * _vol_factor_by_zoom[vp->zoom]) >> 15
 
			);
 
			return;
 
		}
 
	}
 

	
 
}
 

	
 
void SndPlayTileFx(SoundFx sound, TileIndex tile)
 
{
 
	/* emits sound from center of the tile */
 
	int x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
 
	int y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
 
	Point pt = RemapCoords(x, y, GetSlopeZ(x, y));
 
	SndPlayScreenCoordFx(sound, pt.x, pt.y);
 
}
 

	
 
void SndPlayVehicleFx(SoundFx sound, const Vehicle *v)
 
{
 
	SndPlayScreenCoordFx(sound,
 
		(v->left_coord + v->right_coord) / 2,
 
		(v->top_coord + v->bottom_coord) / 2
 
	);
 
}
 

	
 
void SndPlayFx(SoundFx sound)
 
{
 
	StartSound(
 
		sound,
 
		0,
 
		(GetSound(sound)->volume * msf.effect_vol) >> 7
 
	);
 
}
src/sound/cocoa_s.c
Show inline comments
 
deleted file
src/sound/cocoa_s.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/*****************************************************************************
 
 *                             Cocoa sound driver                            *
 
 * Known things left to do:                                                  *
 
 * - Might need to do endian checking for it to work on both ppc and x86     *
 
 *****************************************************************************/
 

	
 
#ifdef WITH_COCOA
 

	
 
#include <AudioUnit/AudioUnit.h>
 

	
 
/* Name conflict */
 
#define Rect        OTTDRect
 
#define Point       OTTDPoint
 
#define WindowClass OTTDWindowClass
 
/* Defined in stdbool.h */
 
#ifndef __cplusplus
 
# ifndef __BEOS__
 
#  undef bool
 
#  undef false
 
#  undef true
 
# endif
 
#endif
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "../debug.h"
 
#include "../driver.h"
 
#include "../mixer.h"
 
#include "../sdl.h"
 

	
 
#include "cocoa_s.h"
 

	
 
#undef WindowClass
 
#undef Point
 
#undef Rect
 

	
 

	
 
static AudioUnit _outputAudioUnit;
 

	
 
/* The CoreAudio callback */
 
static OSStatus audioCallback(void *inRefCon, AudioUnitRenderActionFlags inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, AudioBuffer *ioData)
 
{
 
	MxMixSamples(ioData->mData, ioData->mDataByteSize / 4);
 

	
 
	return noErr;
 
}
 

	
 

	
 
static const char *CocoaSoundStart(const char * const *parm)
 
{
 
	Component comp;
 
	ComponentDescription desc;
 
	struct AudioUnitInputCallback callback;
 
	AudioStreamBasicDescription requestedDesc;
 

	
 
	/* Setup a AudioStreamBasicDescription with the requested format */
 
	requestedDesc.mFormatID = kAudioFormatLinearPCM;
 
	requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
 
	requestedDesc.mChannelsPerFrame = 2;
 
	requestedDesc.mSampleRate = GetDriverParamInt(parm, "hz", 11025);
 

	
 
	requestedDesc.mBitsPerChannel = 16;
 
	requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
 

	
 
#ifdef TTD_BIG_ENDIAN
 
	requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
 
#endif
 

	
 
	requestedDesc.mFramesPerPacket = 1;
 
	requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
 
	requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
 

	
 

	
 
	/* Locate the default output audio unit */
 
	desc.componentType = kAudioUnitComponentType;
 
	desc.componentSubType = kAudioUnitSubType_Output;
 
	desc.componentManufacturer = kAudioUnitID_DefaultOutput;
 
	desc.componentFlags = 0;
 
	desc.componentFlagsMask = 0;
 

	
 
	comp = FindNextComponent (NULL, &desc);
 
	if (comp == NULL) {
 
		return "cocoa_s: Failed to start CoreAudio: FindNextComponent returned NULL";
 
	}
 

	
 
	/* Open & initialize the default output audio unit */
 
	if (OpenAComponent(comp, &_outputAudioUnit) != noErr) {
 
		return "cocoa_s: Failed to start CoreAudio: OpenAComponent";
 
	}
 

	
 
	if (AudioUnitInitialize(_outputAudioUnit) != noErr) {
 
		return "cocoa_s: Failed to start CoreAudio: AudioUnitInitialize";
 
	}
 

	
 
	/* Set the input format of the audio unit. */
 
	if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &requestedDesc, sizeof(requestedDesc)) != noErr) {
 
		return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)";
 
	}
 

	
 
	/* Set the audio callback */
 
	callback.inputProc = audioCallback;
 
	callback.inputProcRefCon = NULL;
 
	if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_SetInputCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)) != noErr) {
 
		return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)";
 
	}
 

	
 
	/* Finally, start processing of the audio unit */
 
	if (AudioOutputUnitStart(_outputAudioUnit) != noErr) {
 
		return "cocoa_s: Failed to start CoreAudio: AudioOutputUnitStart";
 
	}
 

	
 
	/* We're running! */
 
	return NULL;
 
}
 

	
 

	
 
static void CocoaSoundStop(void)
 
{
 
	struct AudioUnitInputCallback callback;
 

	
 
	/* stop processing the audio unit */
 
	if (AudioOutputUnitStop(_outputAudioUnit) != noErr) {
 
		DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: AudioOutputUnitStop failed");
 
		return;
 
	}
 

	
 
	/* Remove the input callback */
 
	callback.inputProc = 0;
 
	callback.inputProcRefCon = 0;
 
	if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_SetInputCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)) != noErr) {
 
		DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback) failed");
 
		return;
 
	}
 

	
 
	if (CloseComponent(_outputAudioUnit) != noErr) {
 
		DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: CloseComponent failed");
 
		return;
 
	}
 
}
 

	
 

	
 
const HalSoundDriver _cocoa_sound_driver = {
 
	CocoaSoundStart,
 
	CocoaSoundStop,
 
};
 

	
 
#endif /* WITH_COCOA */
src/sound/null_s.c
Show inline comments
 
deleted file
src/sound/null_s.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "null_s.h"
 

	
 
static const char *NullSoundStart(const char * const *parm) { return NULL; }
 
static void NullSoundStop(void) {}
 

	
 
const HalSoundDriver _null_sound_driver = {
 
	NullSoundStart,
 
	NullSoundStop,
 
};
src/sound/sdl_s.c
Show inline comments
 
deleted file
src/sound/sdl_s.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 

	
 
#ifdef WITH_SDL
 

	
 
#include "../openttd.h"
 
#include "../driver.h"
 
#include "../mixer.h"
 
#include "../sdl.h"
 
#include "sdl_s.h"
 
#include <SDL.h>
 

	
 
static void CDECL fill_sound_buffer(void *userdata, Uint8 *stream, int len)
 
{
 
	MxMixSamples(stream, len / 4);
 
}
 

	
 
static const char *SdlSoundStart(const char * const *parm)
 
{
 
	SDL_AudioSpec spec;
 

	
 
	const char *s = SdlOpen(SDL_INIT_AUDIO);
 
	if (s != NULL) return s;
 

	
 
	spec.freq = GetDriverParamInt(parm, "hz", 11025);
 
	spec.format = AUDIO_S16SYS;
 
	spec.channels = 2;
 
	spec.samples = 512;
 
	spec.callback = fill_sound_buffer;
 
	SDL_CALL SDL_OpenAudio(&spec, &spec);
 
	SDL_CALL SDL_PauseAudio(0);
 
	return NULL;
 
}
 

	
 
static void SdlSoundStop(void)
 
{
 
	SDL_CALL SDL_CloseAudio();
 
	SdlClose(SDL_INIT_AUDIO);
 
}
 

	
 
const HalSoundDriver _sdl_sound_driver = {
 
	SdlSoundStart,
 
	SdlSoundStop,
 
};
 

	
 
#endif
src/sound/win32_s.c
Show inline comments
 
deleted file
src/sound/win32_s.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "../driver.h"
 
#include "../functions.h"
 
#include "../mixer.h"
 
#include "win32_s.h"
 
#include <windows.h>
 
#include <mmsystem.h>
 

	
 
static HWAVEOUT _waveout;
 
static WAVEHDR _wave_hdr[2];
 
static int _bufsize;
 

	
 
static void PrepareHeader(WAVEHDR *hdr)
 
{
 
	hdr->dwBufferLength = _bufsize * 4;
 
	hdr->dwFlags = 0;
 
	hdr->lpData = malloc(_bufsize * 4);
 
	if (hdr->lpData == NULL ||
 
			waveOutPrepareHeader(_waveout, hdr, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
 
		error("waveOutPrepareHeader failed");
 
}
 

	
 
static void FillHeaders(void)
 
{
 
	WAVEHDR *hdr;
 

	
 
	for (hdr = _wave_hdr; hdr != endof(_wave_hdr); hdr++) {
 
		if (!(hdr->dwFlags & WHDR_INQUEUE)) {
 
			MxMixSamples(hdr->lpData, hdr->dwBufferLength / 4);
 
			if (waveOutWrite(_waveout, hdr, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
 
				error("waveOutWrite failed");
 
		}
 
	}
 
}
 

	
 
static void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
 
	DWORD dwParam1, DWORD dwParam2)
 
{
 
	switch (uMsg) {
 
		case WOM_DONE:
 
			if (_waveout) FillHeaders();
 
			break;
 

	
 
		default:
 
			break;
 
	}
 
}
 

	
 
static const char *Win32SoundStart(const char* const* parm)
 
{
 
	WAVEFORMATEX wfex;
 
	int hz;
 

	
 
	_bufsize = GetDriverParamInt(parm, "bufsize", 1024);
 
	hz = GetDriverParamInt(parm, "hz", 11025);
 
	wfex.wFormatTag = WAVE_FORMAT_PCM;
 
	wfex.nChannels = 2;
 
	wfex.nSamplesPerSec = hz;
 
	wfex.nAvgBytesPerSec = hz * 2 * 2;
 
	wfex.nBlockAlign = 4;
 
	wfex.wBitsPerSample = 16;
 
	if (waveOutOpen(&_waveout, WAVE_MAPPER, &wfex, (DWORD_PTR)&waveOutProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
 
		return "waveOutOpen failed";
 
	PrepareHeader(&_wave_hdr[0]);
 
	PrepareHeader(&_wave_hdr[1]);
 
	FillHeaders();
 
	return NULL;
 
}
 

	
 
static void Win32SoundStop(void)
 
{
 
	HWAVEOUT waveout = _waveout;
 

	
 
	_waveout = NULL;
 
	waveOutReset(waveout);
 
	waveOutUnprepareHeader(waveout, &_wave_hdr[0], sizeof(WAVEHDR));
 
	waveOutUnprepareHeader(waveout, &_wave_hdr[1], sizeof(WAVEHDR));
 
	waveOutClose(waveout);
 
}
 

	
 
const HalSoundDriver _win32_sound_driver = {
 
	Win32SoundStart,
 
	Win32SoundStop,
 
};
src/spritecache.c
Show inline comments
 
deleted file
src/spritecache.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "macros.h"
 
#include "spritecache.h"
 
#include "table/sprites.h"
 
#include "fileio.h"
 

	
 
#define SPRITE_CACHE_SIZE 1024*1024
 

	
 

	
 
typedef struct SpriteCache {
 
	void *ptr;
 
	uint32 file_pos;
 
	int16 lru;
 
} SpriteCache;
 

	
 

	
 
static uint _spritecache_items = 0;
 
static SpriteCache *_spritecache = NULL;
 

	
 

	
 
static inline SpriteCache *GetSpriteCache(uint index)
 
{
 
	return &_spritecache[index];
 
}
 

	
 

	
 
static SpriteCache *AllocateSpriteCache(uint index)
 
{
 
	if (index >= _spritecache_items) {
 
		/* Add another 1024 items to the 'pool' */
 
		uint items = ALIGN(index + 1, 1024);
 

	
 
		DEBUG(sprite, 4, "Increasing sprite cache to %d items (%d bytes)", items, items * sizeof(*_spritecache));
 

	
 
		_spritecache = realloc(_spritecache, items * sizeof(*_spritecache));
 

	
 
		if (_spritecache == NULL) {
 
			error("Unable to allocate sprite cache of %d items (%d bytes)", items, items * sizeof(*_spritecache));
 
		}
 

	
 
		/* Reset the new items and update the count */
 
		memset(_spritecache + _spritecache_items, 0, (items - _spritecache_items) * sizeof(*_spritecache));
 
		_spritecache_items = items;
 
	}
 

	
 
	return GetSpriteCache(index);
 
}
 

	
 

	
 
typedef struct MemBlock {
 
	uint32 size;
 
	byte data[VARARRAY_SIZE];
 
} MemBlock;
 

	
 
static uint _sprite_lru_counter;
 
static MemBlock *_spritecache_ptr;
 
static int _compact_cache_counter;
 

	
 
static void CompactSpriteCache(void);
 

	
 
static bool ReadSpriteHeaderSkipData(void)
 
{
 
	uint16 num = FioReadWord();
 
	byte type;
 

	
 
	if (num == 0) return false;
 

	
 
	type = FioReadByte();
 
	if (type == 0xFF) {
 
		FioSkipBytes(num);
 
		/* Some NewGRF files have "empty" pseudo-sprites which are 1
 
		 * byte long. Catch these so the sprites won't be displayed. */
 
		return num != 1;
 
	}
 

	
 
	FioSkipBytes(7);
 
	num -= 8;
 
	if (num == 0) return true;
 

	
 
	if (type & 2) {
 
		FioSkipBytes(num);
 
	} else {
 
		while (num > 0) {
 
			int8 i = FioReadByte();
 
			if (i >= 0) {
 
				num -= i;
 
				FioSkipBytes(i);
 
			} else {
 
				i = -(i >> 3);
 
				num -= i;
 
				FioReadByte();
 
			}
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
/* Check if the given Sprite ID exists */
 
bool SpriteExists(SpriteID id)
 
{
 
	/* Special case for Sprite ID zero -- its position is also 0... */
 
	if (id == 0) return true;
 

	
 
	return GetSpriteCache(id)->file_pos != 0;
 
}
 

	
 
static void* AllocSprite(size_t);
 

	
 
static void* ReadSprite(SpriteCache *sc, SpriteID id)
 
{
 
	uint num;
 
	byte type;
 

	
 
	DEBUG(sprite, 9, "Load sprite %d", id);
 

	
 
	if (!SpriteExists(id)) {
 
		error(
 
			"Tried to load non-existing sprite #%d.\n"
 
			"Probable cause: Wrong/missing NewGRFs",
 
			id
 
		);
 
	}
 

	
 
	FioSeekToFile(sc->file_pos);
 

	
 
	num  = FioReadWord();
 
	type = FioReadByte();
 
	if (type == 0xFF) {
 
		byte* dest = AllocSprite(num);
 

	
 
		sc->ptr = dest;
 
		FioReadBlock(dest, num);
 

	
 
		return dest;
 
	} else {
 
		uint height = FioReadByte();
 
		uint width  = FioReadWord();
 
		Sprite* sprite;
 
		byte* dest;
 

	
 
		num = (type & 0x02) ? width * height : num - 8;
 
		sprite = AllocSprite(sizeof(*sprite) + num);
 
		sc->ptr = sprite;
 
		sprite->info   = type;
 
		sprite->height = (id != 142) ? height : 10; // Compensate for a TTD bug
 
		sprite->width  = width;
 
		sprite->x_offs = FioReadWord();
 
		sprite->y_offs = FioReadWord();
 

	
 
		dest = sprite->data;
 
		while (num > 0) {
 
			int8 i = FioReadByte();
 

	
 
			if (i >= 0) {
 
				num -= i;
 
				for (; i > 0; --i) *dest++ = FioReadByte();
 
			} else {
 
				const byte* rel = dest - (((i & 7) << 8) | FioReadByte());
 

	
 
				i = -(i >> 3);
 
				num -= i;
 

	
 
				for (; i > 0; --i) *dest++ = *rel++;
 
			}
 
		}
 

	
 
		return sprite;
 
	}
 
}
 

	
 

	
 
bool LoadNextSprite(int load_index, byte file_index)
 
{
 
	SpriteCache *sc;
 
	uint32 file_pos = FioGetPos() | (file_index << 24);
 

	
 
	if (!ReadSpriteHeaderSkipData()) return false;
 

	
 
	if (load_index >= MAX_SPRITES) {
 
		error("Tried to load too many sprites (#%d; max %d)", load_index, MAX_SPRITES);
 
	}
 

	
 
	sc = AllocateSpriteCache(load_index);
 
	sc->file_pos = file_pos;
 
	sc->ptr = NULL;
 
	sc->lru = 0;
 

	
 
	return true;
 
}
 

	
 

	
 
void DupSprite(SpriteID old, SpriteID new)
 
{
 
	SpriteCache *scold = GetSpriteCache(old);
 
	SpriteCache *scnew = AllocateSpriteCache(new);
 

	
 
	scnew->file_pos = scold->file_pos;
 
	scnew->ptr = NULL;
 
}
 

	
 

	
 
void SkipSprites(uint count)
 
{
 
	for (; count > 0; --count) {
 
		if (!ReadSpriteHeaderSkipData()) return;
 
	}
 
}
 

	
 

	
 
#define S_FREE_MASK 1
 

	
 
static inline MemBlock* NextBlock(MemBlock* block)
 
{
 
	return (MemBlock*)((byte*)block + (block->size & ~S_FREE_MASK));
 
}
 

	
 
static uint32 GetSpriteCacheUsage(void)
 
{
 
	uint32 tot_size = 0;
 
	MemBlock* s;
 

	
 
	for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s))
 
		if (!(s->size & S_FREE_MASK)) tot_size += s->size;
 

	
 
	return tot_size;
 
}
 

	
 

	
 
void IncreaseSpriteLRU(void)
 
{
 
	// Increase all LRU values
 
	if (_sprite_lru_counter > 16384) {
 
		SpriteID i;
 

	
 
		DEBUG(sprite, 3, "Fixing lru %d, inuse=%d", _sprite_lru_counter, GetSpriteCacheUsage());
 

	
 
		for (i = 0; i != _spritecache_items; i++) {
 
			SpriteCache *sc = GetSpriteCache(i);
 
			if (sc->ptr != NULL) {
 
				if (sc->lru >= 0) {
 
					sc->lru = -1;
 
				} else if (sc->lru != -32768) {
 
					sc->lru--;
 
				}
 
			}
 
		}
 
		_sprite_lru_counter = 0;
 
	}
 

	
 
	// Compact sprite cache every now and then.
 
	if (++_compact_cache_counter >= 740) {
 
		CompactSpriteCache();
 
		_compact_cache_counter = 0;
 
	}
 
}
 

	
 
// Called when holes in the sprite cache should be removed.
 
// That is accomplished by moving the cached data.
 
static void CompactSpriteCache(void)
 
{
 
	MemBlock *s;
 

	
 
	DEBUG(sprite, 3, "Compacting sprite cache, inuse=%d", GetSpriteCacheUsage());
 

	
 
	for (s = _spritecache_ptr; s->size != 0;) {
 
		if (s->size & S_FREE_MASK) {
 
			MemBlock* next = NextBlock(s);
 
			MemBlock temp;
 
			SpriteID i;
 

	
 
			// Since free blocks are automatically coalesced, this should hold true.
 
			assert(!(next->size & S_FREE_MASK));
 

	
 
			// If the next block is the sentinel block, we can safely return
 
			if (next->size == 0)
 
				break;
 

	
 
			// Locate the sprite belonging to the next pointer.
 
			for (i = 0; GetSpriteCache(i)->ptr != next->data; i++) {
 
				assert(i != _spritecache_items);
 
			}
 

	
 
			GetSpriteCache(i)->ptr = s->data; // Adjust sprite array entry
 
			// Swap this and the next block
 
			temp = *s;
 
			memmove(s, next, next->size);
 
			s = NextBlock(s);
 
			*s = temp;
 

	
 
			// Coalesce free blocks
 
			while (NextBlock(s)->size & S_FREE_MASK) {
 
				s->size += NextBlock(s)->size & ~S_FREE_MASK;
 
			}
 
		} else {
 
			s = NextBlock(s);
 
		}
 
	}
 
}
 

	
 
static void DeleteEntryFromSpriteCache(void)
 
{
 
	SpriteID i;
 
	uint best = -1;
 
	MemBlock* s;
 
	int cur_lru;
 

	
 
	DEBUG(sprite, 3, "DeleteEntryFromSpriteCache, inuse=%d", GetSpriteCacheUsage());
 

	
 
	cur_lru = 0xffff;
 
	for (i = 0; i != _spritecache_items; i++) {
 
		SpriteCache *sc = GetSpriteCache(i);
 
		if (sc->ptr != NULL && sc->lru < cur_lru) {
 
			cur_lru = sc->lru;
 
			best = i;
 
		}
 
	}
 

	
 
	// Display an error message and die, in case we found no sprite at all.
 
	// This shouldn't really happen, unless all sprites are locked.
 
	if (best == (uint)-1)
 
		error("Out of sprite memory");
 

	
 
	// Mark the block as free (the block must be in use)
 
	s = (MemBlock*)GetSpriteCache(best)->ptr - 1;
 
	assert(!(s->size & S_FREE_MASK));
 
	s->size |= S_FREE_MASK;
 
	GetSpriteCache(best)->ptr = NULL;
 

	
 
	// And coalesce adjacent free blocks
 
	for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
 
		if (s->size & S_FREE_MASK) {
 
			while (NextBlock(s)->size & S_FREE_MASK) {
 
				s->size += NextBlock(s)->size & ~S_FREE_MASK;
 
			}
 
		}
 
	}
 
}
 

	
 
static void* AllocSprite(size_t mem_req)
 
{
 
	mem_req += sizeof(MemBlock);
 

	
 
	/* Align this to an uint32 boundary. This also makes sure that the 2 least
 
	 * bits are not used, so we could use those for other things. */
 
	mem_req = ALIGN(mem_req, sizeof(uint32));
 

	
 
	for (;;) {
 
		MemBlock* s;
 

	
 
		for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
 
			if (s->size & S_FREE_MASK) {
 
				size_t cur_size = s->size & ~S_FREE_MASK;
 

	
 
				/* Is the block exactly the size we need or
 
				 * big enough for an additional free block? */
 
				if (cur_size == mem_req ||
 
						cur_size >= mem_req + sizeof(MemBlock)) {
 
					// Set size and in use
 
					s->size = mem_req;
 

	
 
					// Do we need to inject a free block too?
 
					if (cur_size != mem_req) {
 
						NextBlock(s)->size = (cur_size - mem_req) | S_FREE_MASK;
 
					}
 

	
 
					return s->data;
 
				}
 
			}
 
		}
 

	
 
		// Reached sentinel, but no block found yet. Delete some old entry.
 
		DeleteEntryFromSpriteCache();
 
	}
 
}
 

	
 

	
 
const void *GetRawSprite(SpriteID sprite)
 
{
 
	SpriteCache *sc;
 
	void* p;
 

	
 
	assert(sprite < MAX_SPRITES);
 

	
 
	sc = GetSpriteCache(sprite);
 

	
 
	// Update LRU
 
	sc->lru = ++_sprite_lru_counter;
 

	
 
	p = sc->ptr;
 

	
 
	// Load the sprite, if it is not loaded, yet
 
	if (p == NULL) p = ReadSprite(sc, sprite);
 
	return p;
 
}
 

	
 

	
 
void GfxInitSpriteMem(void)
 
{
 
	// initialize sprite cache heap
 
	if (_spritecache_ptr == NULL) _spritecache_ptr = malloc(SPRITE_CACHE_SIZE);
 

	
 
	// A big free block
 
	_spritecache_ptr->size = (SPRITE_CACHE_SIZE - sizeof(MemBlock)) | S_FREE_MASK;
 
	// Sentinel block (identified by size == 0)
 
	NextBlock(_spritecache_ptr)->size = 0;
 

	
 
	/* Reset the spritecache 'pool' */
 
	free(_spritecache);
 
	_spritecache_items = 0;
 
	_spritecache = NULL;
 

	
 
	_compact_cache_counter = 0;
 
}
src/station_cmd.c
Show inline comments
 
deleted file
src/station_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/** @file station_cmd.c */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.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"
 

	
 
typedef enum StationRectModes
 
{
 
	RECT_MODE_TEST = 0,
 
	RECT_MODE_TRY,
 
	RECT_MODE_FORCE
 
} StationRectMode;
 

	
 
static void StationRect_Init(Station *st);
 
static bool StationRect_IsEmpty(Station *st);
 
static bool StationRect_BeforeAddTile(Station *st, TileIndex tile, StationRectMode mode);
 
static bool StationRect_BeforeAddRect(Station *st, TileIndex tile, int w, int h, StationRectMode mode);
 
static bool StationRect_AfterRemoveTile(Station *st, TileIndex tile);
 
static bool StationRect_AfterRemoveRect(Station *st, TileIndex tile, int w, int h);
 

	
 

	
 
/**
 
 * Called if a new block is added to the station-pool
 
 */
 
static void StationPoolNewBlock(uint start_item)
 
{
 
	Station *st;
 

	
 
	/* 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 (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)
 
{
 
	uint i;
 

	
 
	for (i = start_item; i <= end_item; i++) {
 
		Station *st = GetStation(i);
 
		free(st->speclist);
 
		st->speclist = NULL;
 
	}
 
}
 

	
 
/**
 
 * Called if a new block is added to the roadstop-pool
 
 */
 
static void RoadStopPoolNewBlock(uint start_item)
 
{
 
	RoadStop *rs;
 

	
 
	/* 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 (rs = GetRoadStop(start_item); rs != NULL; rs = (rs->index + 1U < GetRoadStopPoolSize()) ? GetRoadStop(rs->index + 1U) : NULL) rs->index = start_item++;
 
}
 

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

	
 

	
 
extern void UpdateAirplanesOnNewStation(Station *st);
 

	
 
static bool TileBelongsToRailStation(const Station *st, TileIndex tile)
 
{
 
	return IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st->index && IsRailwayStation(tile);
 
}
 

	
 
void MarkStationTilesDirty(const Station *st)
 
{
 
	TileIndex tile = st->train_tile;
 
	int w, h;
 

	
 
	// XXX No station is recorded as 0, not INVALID_TILE...
 
	if (tile == 0) return;
 

	
 
	for (h = 0; h < st->trainst_h; h++) {
 
		for (w = 0; w < st->trainst_w; w++) {
 
			if (TileBelongsToRailStation(st, tile)) {
 
				MarkTileDirtyByTile(tile);
 
			}
 
			tile += TileDiffXY(1, 0);
 
		}
 
		tile += TileDiffXY(-w, 1);
 
	}
 
}
 

	
 
static void MarkStationDirty(const Station* st)
 
{
 
	if (st->sign.width_1 != 0) {
 
		InvalidateWindowWidget(WC_STATION_VIEW, st->index, 1);
 

	
 
		MarkAllViewportsDirty(
 
			st->sign.left - 6,
 
			st->sign.top,
 
			st->sign.left + (st->sign.width_1 << 2) + 12,
 
			st->sign.top + 48);
 
	}
 
}
 

	
 
static void InitializeRoadStop(RoadStop *road_stop, RoadStop *previous, TileIndex tile, StationID index)
 
{
 
	road_stop->xy = tile;
 
	road_stop->used = true;
 
	road_stop->status = 3; //stop is free
 
	road_stop->next = NULL;
 
	road_stop->prev = previous;
 
	road_stop->station = index;
 
	road_stop->num_vehicles = 0;
 
}
 

	
 
RoadStop* GetPrimaryRoadStop(const Station* st, RoadStopType type)
 
{
 
	switch (type) {
 
		case RS_BUS:   return st->bus_stops;
 
		case RS_TRUCK: return st->truck_stops;
 
		default: NOT_REACHED();
 
	}
 

	
 
	return NULL;
 
}
 

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

	
 
	for (rs = GetPrimaryRoadStop(st, type); rs->xy != tile; rs = rs->next) {
 
		assert(rs->next != NULL);
 
	}
 

	
 
	return rs;
 
}
 

	
 
uint GetNumRoadStopsInStation(const Station* st, RoadStopType type)
 
{
 
	uint num = 0;
 
	const RoadStop *rs;
 

	
 
	assert(st != NULL);
 
	for (rs = GetPrimaryRoadStop(st, type); rs != NULL; rs = rs->next) num++;
 

	
 
	return num;
 
}
 

	
 
RoadStop *AllocateRoadStop(void)
 
{
 
	RoadStop *rs;
 

	
 
	/* 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 (rs = GetRoadStop(0); rs != NULL; rs = (rs->index + 1U < GetRoadStopPoolSize()) ? GetRoadStop(rs->index + 1U) : NULL) {
 
		if (!IsValidRoadStop(rs)) {
 
			RoadStopID index = rs->index;
 

	
 
			memset(rs, 0, sizeof(*rs));
 
			rs->index = index;
 

	
 
			return rs;
 
		}
 
	}
 

	
 
	/* Check if we can add a block to the pool */
 
	if (AddBlockToPool(&_RoadStop_pool)) return AllocateRoadStop();
 

	
 
	return NULL;
 
}
 

	
 
/* Calculate the radius of the station. Basicly it is the biggest
 
 *  radius that is available within the station */
 
static uint FindCatchmentRadius(const Station* st)
 
{
 
	uint ret = 0;
 

	
 
	if (st->bus_stops != NULL)   ret = max(ret, CA_BUS);
 
	if (st->truck_stops != NULL) ret = max(ret, CA_TRUCK);
 
	if (st->train_tile) ret = max(ret, CA_TRAIN);
 
	if (st->dock_tile)  ret = max(ret, CA_DOCK);
 

	
 
	if (st->airport_tile) {
 
		switch (st->airport_type) {
 
			case AT_OILRIG:        ret = max(ret, CA_AIR_OILPAD);   break;
 
			case AT_SMALL:         ret = max(ret, CA_AIR_SMALL);    break;
 
			case AT_HELIPORT:      ret = max(ret, CA_AIR_HELIPORT); break;
 
			case AT_LARGE:         ret = max(ret, CA_AIR_LARGE);    break;
 
			case AT_METROPOLITAN:  ret = max(ret, CA_AIR_METRO);    break;
 
			case AT_INTERNATIONAL: ret = max(ret, CA_AIR_INTER);    break;
 
			case AT_COMMUTER:      ret = max(ret, CA_AIR_COMMUTER); break;
 
			case AT_HELIDEPOT:     ret = max(ret, CA_AIR_HELIDEPOT); break;
 
			case AT_INTERCON:      ret = max(ret, CA_AIR_INTERCON); break;
 
			case AT_HELISTATION:   ret = max(ret, CA_AIR_HELISTATION); break;
 
		}
 
	}
 

	
 
	return ret;
 
}
 

	
 
#define CHECK_STATIONS_ERR ((Station*)-1)
 

	
 
static Station* GetStationAround(TileIndex tile, int w, int h, StationID closest_station)
 
{
 
	// check around to see if there's any stations there
 
	BEGIN_TILE_LOOP(tile_cur, w + 2, h + 2, tile - TileDiffXY(1, 1))
 
		if (IsTileType(tile_cur, MP_STATION)) {
 
			StationID t = GetStationIndex(tile_cur);
 
			{
 
				Station *st = GetStation(t);
 
				// you cannot take control of an oilrig!!
 
				if (st->airport_type == AT_OILRIG && st->facilities == (FACIL_AIRPORT|FACIL_DOCK))
 
					continue;
 
			}
 

	
 
			if (closest_station == INVALID_STATION) {
 
				closest_station = t;
 
			} else if (closest_station != t) {
 
				_error_message = STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING;
 
				return CHECK_STATIONS_ERR;
 
			}
 
		}
 
	END_TILE_LOOP(tile_cur, w + 2, h + 2, tile - TileDiffXY(1, 1))
 
	return (closest_station == INVALID_STATION) ? NULL : GetStation(closest_station);
 
}
 

	
 
static Station *AllocateStation(void)
 
{
 
	Station *st = NULL;
 

	
 
	/* 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 (st = GetStation(0); st != NULL; st = (st->index + 1U < GetStationPoolSize()) ? GetStation(st->index + 1U) : NULL) {
 
		if (!IsValidStation(st)) {
 
			StationID index = st->index;
 

	
 
			memset(st, 0, sizeof(Station));
 
			st->index = index;
 

	
 
			return st;
 
		}
 
	}
 

	
 
	/* Check if we can add a block to the pool */
 
	if (AddBlockToPool(&_Station_pool)) return AllocateStation();
 

	
 
	_error_message = STR_3008_TOO_MANY_STATIONS_LOADING;
 
	return NULL;
 
}
 

	
 

	
 
/**
 
 * Counts the numbers of tiles matching a specific type in the area around
 
 * @param tile the center tile of the 'count area'
 
 * @param type the type of tile searched for
 
 * @param industry when type == MP_INDUSTRY, the type of the industry,
 
 *                 in all other cases this parameter is ignored
 
 * @result the noumber of matching tiles around
 
 */
 
static int CountMapSquareAround(TileIndex tile, TileType type, IndustryType industry)
 
{
 
	TileIndex cur_tile;
 
	int dx, dy;
 
	int num = 0;
 

	
 
	for (dx = -3; dx <= 3; dx++) {
 
		for (dy = -3; dy <= 3; dy++) {
 
			cur_tile = TILE_MASK(tile + TileDiffXY(dx, dy));
 

	
 
			if (IsTileType(cur_tile, type)) {
 
				switch (type) {
 
					case MP_INDUSTRY:
 
						if (GetIndustryType(cur_tile) == industry)
 
							num++;
 
						break;
 

	
 
					case MP_WATER:
 
						if (!IsWater(cur_tile))
 
							break;
 
						/* FALL THROUGH WHEN WATER TILE */
 
					case MP_TREES:
 
						num++;
 
						break;
 

	
 
					default:
 
						break;
 
				}
 
			}
 
		}
 
	}
 

	
 
	return num;
 
}
 

	
 
#define M(x) ((x) - STR_SV_STNAME)
 

	
 
static bool GenerateStationName(Station *st, TileIndex tile, int flag)
 
{
 
	static const uint32 _gen_station_name_bits[] = {
 
		0,                                      /* 0 */
 
		1 << M(STR_SV_STNAME_AIRPORT),          /* 1 */
 
		1 << M(STR_SV_STNAME_OILFIELD),         /* 2 */
 
		1 << M(STR_SV_STNAME_DOCKS),            /* 3 */
 
		0x1FF << M(STR_SV_STNAME_BUOY_1),       /* 4 */
 
		1 << M(STR_SV_STNAME_HELIPORT),         /* 5 */
 
	};
 

	
 
	Town *t = st->town;
 
	uint32 free_names = (uint32)-1;
 
	int found;
 
	uint z,z2;
 
	unsigned long tmp;
 

	
 
	{
 
		Station *s;
 

	
 
		FOR_ALL_STATIONS(s) {
 
			if (s != st && s->town==t) {
 
				uint str = M(s->string_id);
 
				if (str <= 0x20) {
 
					if (str == M(STR_SV_STNAME_FOREST))
 
						str = M(STR_SV_STNAME_WOODS);
 
					CLRBIT(free_names, str);
 
				}
 
			}
 
		}
 
	}
 

	
 
	/* check default names */
 
	tmp = free_names & _gen_station_name_bits[flag];
 
	if (tmp != 0) {
 
		found = FindFirstBit(tmp);
 
		goto done;
 
	}
 

	
 
	/* check mine? */
 
	if (HASBIT(free_names, M(STR_SV_STNAME_MINES))) {
 
		if (CountMapSquareAround(tile, MP_INDUSTRY, IT_COAL_MINE) >= 2 ||
 
				CountMapSquareAround(tile, MP_INDUSTRY, IT_IRON_MINE) >= 2 ||
 
				CountMapSquareAround(tile, MP_INDUSTRY, IT_COPPER_MINE) >= 2 ||
 
				CountMapSquareAround(tile, MP_INDUSTRY, IT_GOLD_MINE) >= 2 ||
 
				CountMapSquareAround(tile, MP_INDUSTRY, IT_DIAMOND_MINE) >= 2) {
 
			found = M(STR_SV_STNAME_MINES);
 
			goto done;
 
		}
 
	}
 

	
 
	/* check close enough to town to get central as name? */
 
	if (DistanceMax(tile,t->xy) < 8) {
 
		found = M(STR_SV_STNAME);
 
		if (HASBIT(free_names, M(STR_SV_STNAME))) goto done;
 

	
 
		found = M(STR_SV_STNAME_CENTRAL);
 
		if (HASBIT(free_names, M(STR_SV_STNAME_CENTRAL))) goto done;
 
	}
 

	
 
	/* Check lakeside */
 
	if (HASBIT(free_names, M(STR_SV_STNAME_LAKESIDE)) &&
 
			DistanceFromEdge(tile) < 20 &&
 
			CountMapSquareAround(tile, MP_WATER, 0) >= 5) {
 
		found = M(STR_SV_STNAME_LAKESIDE);
 
		goto done;
 
	}
 

	
 
	/* Check woods */
 
	if (HASBIT(free_names, M(STR_SV_STNAME_WOODS)) && (
 
				CountMapSquareAround(tile, MP_TREES, 0) >= 8 ||
 
				CountMapSquareAround(tile, MP_INDUSTRY, IT_FOREST) >= 2)
 
			) {
 
		found = _opt.landscape == LT_DESERT ?
 
			M(STR_SV_STNAME_FOREST) : M(STR_SV_STNAME_WOODS);
 
		goto done;
 
	}
 

	
 
	/* check elevation compared to town */
 
	z = GetTileZ(tile);
 
	z2 = GetTileZ(t->xy);
 
	if (z < z2) {
 
		found = M(STR_SV_STNAME_VALLEY);
 
		if (HASBIT(free_names, M(STR_SV_STNAME_VALLEY))) goto done;
 
	} else if (z > z2) {
 
		found = M(STR_SV_STNAME_HEIGHTS);
 
		if (HASBIT(free_names, M(STR_SV_STNAME_HEIGHTS))) goto done;
 
	}
 

	
 
	/* check direction compared to town */
 
	{
 
		static const int8 _direction_and_table[] = {
 
			~( (1<<M(STR_SV_STNAME_WEST)) | (1<<M(STR_SV_STNAME_EAST)) | (1<<M(STR_SV_STNAME_NORTH)) ),
 
			~( (1<<M(STR_SV_STNAME_SOUTH)) | (1<<M(STR_SV_STNAME_WEST)) | (1<<M(STR_SV_STNAME_NORTH)) ),
 
			~( (1<<M(STR_SV_STNAME_SOUTH)) | (1<<M(STR_SV_STNAME_EAST)) | (1<<M(STR_SV_STNAME_NORTH)) ),
 
			~( (1<<M(STR_SV_STNAME_SOUTH)) | (1<<M(STR_SV_STNAME_WEST)) | (1<<M(STR_SV_STNAME_EAST)) ),
 
		};
 

	
 
		free_names &= _direction_and_table[
 
			(TileX(tile) < TileX(t->xy)) +
 
			(TileY(tile) < TileY(t->xy)) * 2];
 
	}
 

	
 
	tmp = free_names & ((1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<6)|(1<<7)|(1<<12)|(1<<26)|(1<<27)|(1<<28)|(1<<29)|(1<<30));
 
	if (tmp == 0) {
 
		_error_message = STR_3007_TOO_MANY_STATIONS_LOADING;
 
		return false;
 
	}
 
	found = FindFirstBit(tmp);
 

	
 
done:
 
	st->string_id = found + STR_SV_STNAME;
 
	return true;
 
}
 
#undef M
 

	
 
static Station* GetClosestStationFromTile(TileIndex tile, uint threshold, PlayerID owner)
 
{
 
	Station* best_station = NULL;
 
	Station* st;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		if ((owner == PLAYER_SPECTATOR || st->owner == owner)) {
 
			uint cur_dist = DistanceManhattan(tile, st->xy);
 

	
 
			if (cur_dist < threshold) {
 
				threshold = cur_dist;
 
				best_station = st;
 
			}
 
		}
 
	}
 

	
 
	return best_station;
 
}
 

	
 
static void StationInitialize(Station *st, TileIndex tile)
 
{
 
	GoodsEntry *ge;
 

	
 
	st->xy = tile;
 
	st->airport_tile = st->dock_tile = st->train_tile = 0;
 
	st->bus_stops = st->truck_stops = NULL;
 
	st->had_vehicle_of_type = 0;
 
	st->time_since_load = 255;
 
	st->time_since_unload = 255;
 
	st->delete_ctr = 0;
 
	st->facilities = 0;
 

	
 
	st->last_vehicle_type = VEH_Invalid;
 

	
 
	for (ge = st->goods; ge != endof(st->goods); ge++) {
 
		ge->waiting_acceptance = 0;
 
		ge->days_since_pickup = 0;
 
		ge->enroute_from = INVALID_STATION;
 
		ge->rating = 175;
 
		ge->last_speed = 0;
 
		ge->last_age = 0xFF;
 
		ge->feeder_profit = 0;
 
	}
 

	
 
	st->random_bits = Random();
 
	st->waiting_triggers = 0;
 

	
 
	StationRect_Init(st);
 
}
 

	
 
// Update the virtual coords needed to draw the station sign.
 
// st = Station to update for.
 
static void UpdateStationVirtCoord(Station *st)
 
{
 
	Point pt = RemapCoords2(TileX(st->xy) * TILE_SIZE, TileY(st->xy) * TILE_SIZE);
 

	
 
	pt.y -= 32;
 
	if (st->facilities & FACIL_AIRPORT && st->airport_type == AT_OILRIG) pt.y -= 16;
 

	
 
	SetDParam(0, st->index);
 
	SetDParam(1, st->facilities);
 
	UpdateViewportSignPos(&st->sign, pt.x, pt.y, STR_305C_0);
 
}
 

	
 
// Update the virtual coords needed to draw the station sign for all stations.
 
void UpdateAllStationVirtCoord(void)
 
{
 
	Station* st;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		UpdateStationVirtCoord(st);
 
	}
 
}
 

	
 
// Update the station virt coords while making the modified parts dirty.
 
static void UpdateStationVirtCoordDirty(Station *st)
 
{
 
	MarkStationDirty(st);
 
	UpdateStationVirtCoord(st);
 
	MarkStationDirty(st);
 
}
 

	
 
// Get a mask of the cargo types that the station accepts.
 
static uint GetAcceptanceMask(const Station *st)
 
{
 
	uint mask = 0;
 
	uint i;
 

	
 
	for (i = 0; i != NUM_CARGO; i++) {
 
		if (st->goods[i].waiting_acceptance & 0x8000) mask |= 1 << i;
 
	}
 
	return mask;
 
}
 

	
 
// Items contains the two cargo names that are to be accepted or rejected.
 
// msg is the string id of the message to display.
 
static void ShowRejectOrAcceptNews(const Station *st, uint32 items, StringID msg)
 
{
 
	if (items) {
 
		SetDParam(2, GB(items, 16, 16));
 
		SetDParam(1, GB(items,  0, 16));
 
		SetDParam(0, st->index);
 
		AddNewsItem(msg + (GB(items, 16, 16) ? 1 : 0), NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_TILE, NT_ACCEPTANCE, 0), st->xy, 0);
 
	}
 
}
 

	
 
// Get a list of the cargo types being produced around the tile.
 
void GetProductionAroundTiles(AcceptedCargo produced, TileIndex tile,
 
	int w, int h, int rad)
 
{
 
	int x,y;
 
	int x1,y1,x2,y2;
 
	int xc,yc;
 

	
 
	memset(produced, 0, sizeof(AcceptedCargo));
 

	
 
	x = TileX(tile);
 
	y = TileY(tile);
 

	
 
	// expand the region by rad tiles on each side
 
	// while making sure that we remain inside the board.
 
	x2 = min(x + w + rad, MapSizeX());
 
	x1 = max(x - rad, 0);
 

	
 
	y2 = min(y + h + rad, MapSizeY());
 
	y1 = max(y - rad, 0);
 

	
 
	assert(x1 < x2);
 
	assert(y1 < y2);
 
	assert(w > 0);
 
	assert(h > 0);
 

	
 
	for (yc = y1; yc != y2; yc++) {
 
		for (xc = x1; xc != x2; xc++) {
 
			if (!(IS_INSIDE_1D(xc, x, w) && IS_INSIDE_1D(yc, y, h))) {
 
				GetProducedCargoProc *gpc;
 
				TileIndex tile = TileXY(xc, yc);
 

	
 
				gpc = _tile_type_procs[GetTileType(tile)]->get_produced_cargo_proc;
 
				if (gpc != NULL) {
 
					CargoID cargos[2] = { CT_INVALID, CT_INVALID };
 

	
 
					gpc(tile, cargos);
 
					if (cargos[0] != CT_INVALID) {
 
						produced[cargos[0]]++;
 
						if (cargos[1] != CT_INVALID) {
 
							produced[cargos[1]]++;
 
						}
 
					}
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 
// Get a list of the cargo types that are accepted around the tile.
 
void GetAcceptanceAroundTiles(AcceptedCargo accepts, TileIndex tile,
 
	int w, int h, int rad)
 
{
 
	int x,y;
 
	int x1,y1,x2,y2;
 
	int xc,yc;
 

	
 
	memset(accepts, 0, sizeof(AcceptedCargo));
 

	
 
	x = TileX(tile);
 
	y = TileY(tile);
 

	
 
	// expand the region by rad tiles on each side
 
	// while making sure that we remain inside the board.
 
	x2 = min(x + w + rad, MapSizeX());
 
	y2 = min(y + h + rad, MapSizeY());
 
	x1 = max(x - rad, 0);
 
	y1 = max(y - rad, 0);
 

	
 
	assert(x1 < x2);
 
	assert(y1 < y2);
 
	assert(w > 0);
 
	assert(h > 0);
 

	
 
	for (yc = y1; yc != y2; yc++) {
 
		for (xc = x1; xc != x2; xc++) {
 
			TileIndex tile = TileXY(xc, yc);
 

	
 
			if (!IsTileType(tile, MP_STATION)) {
 
				AcceptedCargo ac;
 
				uint i;
 

	
 
				GetAcceptedCargo(tile, ac);
 
				for (i = 0; i < lengthof(ac); ++i) accepts[i] += ac[i];
 
			}
 
		}
 
	}
 
}
 

	
 
typedef struct ottd_Rectangle {
 
	uint min_x;
 
	uint min_y;
 
	uint max_x;
 
	uint max_y;
 
} ottd_Rectangle;
 

	
 
static inline void MergePoint(ottd_Rectangle* rect, TileIndex tile)
 
{
 
	uint x = TileX(tile);
 
	uint y = TileY(tile);
 

	
 
	if (rect->min_x > x) rect->min_x = x;
 
	if (rect->min_y > y) rect->min_y = y;
 
	if (rect->max_x < x) rect->max_x = x;
 
	if (rect->max_y < y) rect->max_y = y;
 
}
 

	
 
// Update the acceptance for a station.
 
// show_msg controls whether to display a message that acceptance was changed.
 
static void UpdateStationAcceptance(Station *st, bool show_msg)
 
{
 
	uint old_acc, new_acc;
 
	const RoadStop *cur_rs;
 
	int i;
 
	ottd_Rectangle rect;
 
	int rad;
 
	AcceptedCargo accepts;
 

	
 
	rect.min_x = MapSizeX();
 
	rect.min_y = MapSizeY();
 
	rect.max_x = rect.max_y = 0;
 
	// Don't update acceptance for a buoy
 
	if (IsBuoy(st)) return;
 

	
 
	/* old accepted goods types */
 
	old_acc = GetAcceptanceMask(st);
 

	
 
	// Put all the tiles that span an area in the table.
 
	if (st->train_tile != 0) {
 
		MergePoint(&rect, st->train_tile);
 
		MergePoint(&rect,
 
			st->train_tile + TileDiffXY(st->trainst_w - 1, st->trainst_h - 1)
 
		);
 
	}
 

	
 
	if (st->airport_tile != 0) {
 
		const AirportFTAClass* afc = GetAirport(st->airport_type);
 

	
 
		MergePoint(&rect, st->airport_tile);
 
		MergePoint(&rect,
 
			st->airport_tile + TileDiffXY(afc->size_x - 1, afc->size_y - 1)
 
		);
 
	}
 

	
 
	if (st->dock_tile != 0) MergePoint(&rect, st->dock_tile);
 

	
 
	for (cur_rs = st->bus_stops; cur_rs != NULL; cur_rs = cur_rs->next) {
 
		MergePoint(&rect, cur_rs->xy);
 
	}
 

	
 
	for (cur_rs = st->truck_stops; cur_rs != NULL; cur_rs = cur_rs->next) {
 
		MergePoint(&rect, cur_rs->xy);
 
	}
 

	
 
	rad = (_patches.modified_catchment) ? FindCatchmentRadius(st) : 4;
 

	
 
	// And retrieve the acceptance.
 
	if (rect.max_x >= rect.min_x) {
 
		GetAcceptanceAroundTiles(
 
			accepts,
 
			TileXY(rect.min_x, rect.min_y),
 
			rect.max_x - rect.min_x + 1,
 
			rect.max_y - rect.min_y + 1,
 
			rad
 
		);
 
	} else {
 
		memset(accepts, 0, sizeof(accepts));
 
	}
 

	
 
	// Adjust in case our station only accepts fewer kinds of goods
 
	for (i = 0; i != NUM_CARGO; i++) {
 
		uint amt = min(accepts[i], 15);
 

	
 
		// Make sure the station can accept the goods type.
 
		if ((i != CT_PASSENGERS && !(st->facilities & (byte)~FACIL_BUS_STOP)) ||
 
				(i == CT_PASSENGERS && !(st->facilities & (byte)~FACIL_TRUCK_STOP)))
 
			amt = 0;
 

	
 
		SB(st->goods[i].waiting_acceptance, 12, 4, amt);
 
	}
 

	
 
	// Only show a message in case the acceptance was actually changed.
 
	new_acc = GetAcceptanceMask(st);
 
	if (old_acc == new_acc)
 
		return;
 

	
 
	// show a message to report that the acceptance was changed?
 
	if (show_msg && st->owner == _local_player && st->facilities) {
 
		uint32 accept=0, reject=0; /* these contain two string ids each */
 
		const StringID *str = _cargoc.names_s;
 

	
 
		do {
 
			if (new_acc & 1) {
 
				if (!(old_acc & 1)) accept = (accept << 16) | *str;
 
			} else {
 
				if (old_acc & 1) reject = (reject << 16) | *str;
 
			}
 
		} while (str++,(new_acc>>=1) != (old_acc>>=1));
 

	
 
		ShowRejectOrAcceptNews(st, accept, STR_3040_NOW_ACCEPTS);
 
		ShowRejectOrAcceptNews(st, reject, STR_303E_NO_LONGER_ACCEPTS);
 
	}
 

	
 
	// redraw the station view since acceptance changed
 
	InvalidateWindowWidget(WC_STATION_VIEW, st->index, 4);
 
}
 

	
 
static void UpdateStationSignCoord(Station *st)
 
{
 
	Rect *r = &st->rect;
 

	
 
	if (StationRect_IsEmpty(st)) return; // no tiles belong to this station
 

	
 
	// clamp sign coord to be inside the station rect
 
	st->xy = TileXY(clampu(TileX(st->xy), r->left, r->right), clampu(TileY(st->xy), r->top, r->bottom));
 
	UpdateStationVirtCoordDirty(st);
 
}
 

	
 
// This is called right after a station was deleted.
 
// It checks if the whole station is free of substations, and if so, the station will be
 
// deleted after a little while.
 
static void DeleteStationIfEmpty(Station* st)
 
{
 
	if (st->facilities == 0) {
 
		st->delete_ctr = 0;
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
	}
 
	/* station remains but it probably lost some parts - station sign should stay in the station boundaries */
 
	UpdateStationSignCoord(st);
 
}
 

	
 
static int32 ClearTile_Station(TileIndex tile, byte flags);
 

	
 
// Tries to clear the given area. Returns the cost in case of success.
 
// Or an error code if it failed.
 
int32 CheckFlatLandBelow(TileIndex tile, uint w, uint h, uint flags, uint invalid_dirs, StationID* station)
 
{
 
	int32 cost = 0, ret;
 

	
 
	Slope tileh;
 
	uint z;
 
	int allowed_z = -1;
 
	int flat_z;
 

	
 
	BEGIN_TILE_LOOP(tile_cur, w, h, tile)
 
		if (MayHaveBridgeAbove(tile_cur) && IsBridgeAbove(tile_cur)) {
 
			return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 
		}
 

	
 
		if (!EnsureNoVehicle(tile_cur)) return CMD_ERROR;
 

	
 
		tileh = GetTileSlope(tile_cur, &z);
 

	
 
		/* Prohibit building if
 
		 *   1) The tile is "steep" (i.e. stretches two height levels)
 
		 * -OR-
 
		 *   2) The tile is non-flat if
 
		 *     a) the player building is an "old-school" AI
 
		 *   -OR-
 
		 *     b) the build_on_slopes switch is disabled
 
		 */
 
		if (IsSteepSlope(tileh) ||
 
				((_is_old_ai_player || !_patches.build_on_slopes) && tileh != SLOPE_FLAT)) {
 
			return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
 
		}
 

	
 
		flat_z = z;
 
		if (tileh != SLOPE_FLAT) {
 
			// need to check so the entrance to the station is not pointing at a slope.
 
			if ((invalid_dirs&1 && !(tileh & SLOPE_NE) && (uint)w_cur == w) ||
 
					(invalid_dirs&2 && !(tileh & SLOPE_SE) && h_cur == 1) ||
 
					(invalid_dirs&4 && !(tileh & SLOPE_SW) && w_cur == 1) ||
 
					(invalid_dirs&8 && !(tileh & SLOPE_NW) && (uint)h_cur == h)) {
 
				return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
 
			}
 
			cost += _price.terraform;
 
			flat_z += TILE_HEIGHT;
 
		}
 

	
 
		// get corresponding flat level and make sure that all parts of the station have the same level.
 
		if (allowed_z == -1) {
 
			// first tile
 
			allowed_z = flat_z;
 
		} else if (allowed_z != flat_z) {
 
			return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
 
		}
 

	
 
		// if station is set, then we have special handling to allow building on top of already existing stations.
 
		// so station points to INVALID_STATION if we can build on any station. or it points to a station if we're only allowed to build
 
		// on exactly that station.
 
		if (station != NULL && IsTileType(tile_cur, MP_STATION)) {
 
			if (!IsRailwayStation(tile_cur)) {
 
				return ClearTile_Station(tile_cur, DC_AUTO); // get error message
 
			} else {
 
				StationID st = GetStationIndex(tile_cur);
 
				if (*station == INVALID_STATION) {
 
					*station = st;
 
				} else if (*station != st) {
 
					return_cmd_error(STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING);
 
				}
 
			}
 
		} else {
 
			ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
			if (CmdFailed(ret)) return ret;
 
			cost += ret;
 
		}
 
	END_TILE_LOOP(tile_cur, w, h, tile)
 

	
 
	return cost;
 
}
 

	
 
static bool CanExpandRailroadStation(Station* st, uint* fin, Axis axis)
 
{
 
	uint curw = st->trainst_w, curh = st->trainst_h;
 
	TileIndex tile = fin[0];
 
	uint w = fin[1];
 
	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  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)
 
{
 
	Station *st;
 
	int w_org, h_org;
 
	int32 cost, ret;
 
	StationID est;
 
	int plat_len, numtracks;
 
	Axis axis;
 
	uint finalvalues[3];
 
	const StationSpec *statspec;
 
	int specindex;
 

	
 
	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 = p1 & 1;
 
	numtracks = GB(p1,  8, 8);
 
	plat_len  = GB(p1, 16, 8);
 
	/* w = length, h = num_tracks */
 
	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
 
	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)
 
	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;
 
	cost = ret + (numtracks * _price.train_station_track + _price.train_station_length) * plat_len;
 

	
 
	// Make sure there are no similar stations around us.
 
	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, 8, _current_player);
 
		if (st != NULL && st->facilities) st = NULL;
 
	}
 

	
 
	if (st != NULL) {
 
		// Reuse an existing station.
 
		if (st->owner != OWNER_NONE && 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 (!StationRect_BeforeAddRect(st, tile_org, w_org, h_org, RECT_MODE_TEST)) return CMD_ERROR;
 
	} else {
 
		// Create a new station
 
		st = AllocateStation();
 
		if (st == NULL) return CMD_ERROR;
 

	
 
		st->town = ClosestTownFromTile(tile_org, (uint)-1);
 
		if (IsValidPlayer(_current_player) && (flags & DC_EXEC))
 
			SETBIT(st->town->have_ratings, _current_player);
 

	
 
		if (!GenerateStationName(st, tile_org, 0)) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) StationInitialize(st, tile_org);
 
	}
 

	
 
	/* 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 */
 
	statspec = GetCustomStationSpec(GB(p2, 8, 8), GB(p2, 16, 8));
 
	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;
 

	
 
		// Now really clear the land below the station
 
		// It should never return CMD_ERROR.. but you never know ;)
 
		//  (a bit strange function name for it, but it really does clear the land, when DC_EXEC is in flags)
 
		ret = CheckFlatLandBelow(tile_org, w_org, h_org, flags, 5 << axis, _patches.nonuniform_stations ? &est : NULL);
 
		if (CmdFailed(ret)) return ret;
 

	
 
		st->train_tile = finalvalues[0];
 
		if (!st->facilities) st->xy = finalvalues[0];
 
		st->facilities |= FACIL_TRAIN;
 
		st->owner = _current_player;
 

	
 
		st->trainst_w = finalvalues[1];
 
		st->trainst_h = finalvalues[2];
 

	
 
		st->build_date = _date;
 

	
 
		StationRect_BeforeAddRect(st, tile_org, w_org, h_org, RECT_MODE_TRY);
 

	
 
		tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
 
		track = AxisToTrack(axis);
 

	
 
		layout_ptr = alloca(numtracks * plat_len);
 
		GetStationLayout(layout_ptr, numtracks, plat_len, statspec);
 

	
 
		numtracks_orig = numtracks;
 

	
 
		do {
 
			TileIndex tile = tile_org;
 
			int w = plat_len;
 
			do {
 
				byte layout = *layout_ptr++;
 
				MakeRailStation(tile, st->owner, st->index, axis, layout, GB(p2, 0, 4));
 
				SetCustomStationSpecIndex(tile, specindex);
 
				SetStationTileRandomBits(tile, GB(Random(), 0, 4));
 

	
 
				if (statspec != NULL) {
 
					/* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
 
					uint32 platinfo = GetPlatformInfo(AXIS_X, 0, plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false);
 
					uint16 callback = GetStationCallback(CBID_STATION_TILE_LAYOUT, platinfo, 0, statspec, st, tile);
 
					if (callback != CALLBACK_FAILED && callback < 8) SetStationGfx(tile, callback + axis);
 
				}
 

	
 
				tile += tile_delta;
 
			} while (--w);
 
			SetSignalsOnBothDir(tile_org, track);
 
			YapfNotifyTrackLayoutChange(tile_org, track);
 
			tile_org += tile_delta ^ TileDiffXY(1, 1); // perpendicular to tile_delta
 
		} while (--numtracks);
 

	
 
		MarkStationTilesDirty(st);
 
		UpdateStationVirtCoordDirty(st);
 
		UpdateStationAcceptance(st, false);
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
	}
 

	
 
	return cost;
 
}
 

	
 
static void MakeRailwayStationAreaSmaller(Station *st)
 
{
 
	uint w = st->trainst_w;
 
	uint h = st->trainst_h;
 
	TileIndex tile = st->train_tile;
 
	uint i;
 

	
 
restart:
 

	
 
	// too small?
 
	if (w != 0 && h != 0) {
 
		// check the left side, x = constant, y changes
 
		for (i = 0; !TileBelongsToRailStation(st, tile + TileDiffXY(0, i));) {
 
			// the left side is unused?
 
			if (++i == h) {
 
				tile += TileDiffXY(1, 0);
 
				w--;
 
				goto restart;
 
			}
 
		}
 

	
 
		// check the right side, x = constant, y changes
 
		for (i = 0; !TileBelongsToRailStation(st, tile + TileDiffXY(w - 1, i));) {
 
			// the right side is unused?
 
			if (++i == h) {
 
				w--;
 
				goto restart;
 
			}
 
		}
 

	
 
		// check the upper side, y = constant, x changes
 
		for (i = 0; !TileBelongsToRailStation(st, tile + TileDiffXY(i, 0));) {
 
			// the left side is unused?
 
			if (++i == w) {
 
				tile += TileDiffXY(0, 1);
 
				h--;
 
				goto restart;
 
			}
 
		}
 

	
 
		// check the lower side, y = constant, x changes
 
		for (i = 0; !TileBelongsToRailStation(st, tile + TileDiffXY(i, h - 1));) {
 
			// the left side is unused?
 
			if (++i == w) {
 
				h--;
 
				goto restart;
 
			}
 
		}
 
	} else {
 
		tile = 0;
 
	}
 

	
 
	st->trainst_w = w;
 
	st->trainst_h = h;
 
	st->train_tile = tile;
 
}
 

	
 
/** Remove a single tile from a railroad station.
 
 * This allows for custom-built station with holes and weird layouts
 
 * @param tile tile of station piece to remove
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdRemoveFromRailroadStation(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Station *st;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	// make sure the specified tile belongs to the current player, and that it is a railroad station.
 
	if (!IsTileType(tile, MP_STATION) || !IsRailwayStation(tile) || !_patches.nonuniform_stations) return CMD_ERROR;
 
	st = GetStationByTile(tile);
 
	if (_current_player != OWNER_WATER && (!CheckOwnership(st->owner) || !EnsureNoVehicle(tile))) return CMD_ERROR;
 

	
 
	// if we reached here, it means we can actually delete it. do that.
 
	if (flags & DC_EXEC) {
 
		uint specindex = GetCustomStationSpecIndex(tile);
 
		Track track = GetRailStationTrack(tile);
 
		DoClearSquare(tile);
 
		StationRect_AfterRemoveTile(st, tile);
 
		SetSignalsOnBothDir(tile, track);
 
		YapfNotifyTrackLayoutChange(tile, track);
 

	
 
		DeallocateSpecFromStation(st, specindex);
 

	
 
		// now we need to make the "spanned" area of the railway station smaller if we deleted something at the edges.
 
		// we also need to adjust train_tile.
 
		MakeRailwayStationAreaSmaller(st);
 
		MarkStationTilesDirty(st);
 
		UpdateStationSignCoord(st);
 

	
 
		// if we deleted the whole station, delete the train facility.
 
		if (st->train_tile == 0) {
 
			st->facilities &= ~FACIL_TRAIN;
 
			UpdateStationVirtCoordDirty(st);
 
			DeleteStationIfEmpty(st);
 
		}
 
	}
 
	return _price.remove_rail_station;
 
}
 

	
 
// determine the number of platforms for the station
 
uint GetStationPlatforms(const Station *st, TileIndex tile)
 
{
 
	TileIndex t;
 
	TileIndexDiff delta;
 
	Axis axis;
 
	uint len;
 
	assert(TileBelongsToRailStation(st, tile));
 

	
 
	len = 0;
 
	axis = GetRailStationAxis(tile);
 
	delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
 

	
 
	// find starting tile..
 
	t = tile;
 
	do {
 
		t -= delta;
 
		len++;
 
	} while (TileBelongsToRailStation(st, t) && GetRailStationAxis(t) == axis);
 

	
 
	// find ending tile
 
	t = tile;
 
	do {
 
		t += delta;
 
		len++;
 
	} while (TileBelongsToRailStation(st, t) && GetRailStationAxis(t) == axis);
 

	
 
	return len - 1;
 
}
 

	
 
/** Determines the REMAINING length of a platform, starting at (and including)
 
 * the given tile.
 
 * @param tile the tile from which to start searching. Must be a railway station tile
 
 * @param dir The direction in which to search.
 
 * @return The platform length
 
 */
 
uint GetPlatformLength(TileIndex tile, DiagDirection dir)
 
{
 
	TileIndex start_tile = tile;
 
	uint length = 0;
 
	assert(IsRailwayStationTile(tile));
 
	assert(dir < DIAGDIR_END);
 

	
 
	do {
 
		length ++;
 
		tile += TileOffsByDiagDir(dir);
 
	} while (IsCompatibleTrainStationTile(tile, start_tile));
 

	
 
	return length;
 
}
 

	
 

	
 
static int32 RemoveRailroadStation(Station *st, TileIndex tile, uint32 flags)
 
{
 
	int w,h;
 
	int32 cost = 0;
 

	
 
	/* if there is flooding and non-uniform stations are enabled, remove platforms tile by tile */
 
	if (_current_player == OWNER_WATER && _patches.nonuniform_stations)
 
		return DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_FROM_RAILROAD_STATION);
 

	
 
	/* Current player owns the station? */
 
	if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
 
		return CMD_ERROR;
 

	
 
	/* determine width and height of platforms */
 
	tile = st->train_tile;
 
	w = st->trainst_w;
 
	h = st->trainst_h;
 

	
 
	assert(w != 0 && h != 0);
 

	
 
	/* clear all areas of the station */
 
	do {
 
		int w_bak = w;
 
		do {
 
			// for nonuniform stations, only remove tiles that are actually train station tiles
 
			if (TileBelongsToRailStation(st, tile)) {
 
				if (!EnsureNoVehicle(tile))
 
					return CMD_ERROR;
 
				cost += _price.remove_rail_station;
 
				if (flags & DC_EXEC) {
 
					Track track = GetRailStationTrack(tile);
 
					DoClearSquare(tile);
 
					SetSignalsOnBothDir(tile, track);
 
					YapfNotifyTrackLayoutChange(tile, track);
 
				}
 
			}
 
			tile += TileDiffXY(1, 0);
 
		} while (--w);
 
		w = w_bak;
 
		tile += TileDiffXY(-w, 1);
 
	} while (--h);
 

	
 
	if (flags & DC_EXEC) {
 
		StationRect_AfterRemoveRect(st, st->train_tile, st->trainst_w, st->trainst_h);
 

	
 
		st->train_tile = 0;
 
		st->trainst_w = st->trainst_h = 0;
 
		st->facilities &= ~FACIL_TRAIN;
 

	
 
		free(st->speclist);
 
		st->num_specs = 0;
 
		st->speclist  = NULL;
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		DeleteStationIfEmpty(st);
 
	}
 

	
 
	return cost;
 
}
 

	
 
int32 DoConvertStationRail(TileIndex tile, RailType totype, bool exec)
 
{
 
	const Station* st = GetStationByTile(tile);
 

	
 
	if (!CheckOwnership(st->owner) || !EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	// tile is not a railroad station?
 
	if (!IsRailwayStation(tile)) return CMD_ERROR;
 

	
 
	if (GetRailType(tile) == totype) return CMD_ERROR;
 

	
 
	// 'hidden' elrails can't be downgraded to normal rail when elrails are disabled
 
	if (_patches.disable_elrails && totype == RAILTYPE_RAIL && GetRailType(tile) == RAILTYPE_ELECTRIC) return CMD_ERROR;
 

	
 
	if (exec) {
 
		SetRailType(tile, totype);
 
		MarkTileDirtyByTile(tile);
 
		YapfNotifyTrackLayoutChange(tile, GetRailStationTrack(tile));
 
	}
 

	
 
	return _price.build_rail >> 1;
 
}
 

	
 
/** Heavy wizardry used to add a roadstop to a station.
 
 * To understand the function, lets first look at what is passed around,
 
 * especially the last two parameters. CmdBuildRoadStop allocates a road
 
 * stop and needs to put that stop into the linked list of road stops.
 
 * It (CmdBuildRoadStop) has a **currstop pointer which points to element
 
 * in the linked list of stops (each element in this list being a pointer
 
 * in itself, hence the double pointer). We (FindRoadStopSpot) need to
 
 * modify this pointer (**currstop) thus we need to pass by reference,
 
 * obtaining a triple pointer (***currstop). When finished, **currstop
 
 * in CmdBuildRoadStop will contain the address of the pointer which will
 
 * then point into the global roadstop array. *prev (in CmdBuildRoadStop)
 
 * is the pointer tino the global roadstop array which has *currstop in
 
 * its ->next element.
 
 * @param[in] truck_station Determines whether a stop is RS_BUS or RS_TRUCK
 
 * @param[in] station The station to do the whole procedure for
 
 * @param[out] currstop See the detailed function description
 
 * @param prev See the detailed function description
 
 */
 
static void FindRoadStopSpot(bool truck_station, Station* st, RoadStop*** currstop, RoadStop** prev)
 
{
 
	RoadStop **primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
 
	assert(*prev == NULL);
 

	
 
	if (*primary_stop == NULL) {
 
		//we have no roadstop of the type yet, so write a "primary stop"
 
		*currstop = primary_stop;
 
	} else {
 
		//there are stops already, so append to the end of the list
 
		*prev = *primary_stop;
 
		*currstop = &(*primary_stop)->next;
 
		while (**currstop != NULL) {
 
			*prev = (*prev)->next;
 
			*currstop = &(**currstop)->next;
 
		}
 
	}
 
}
 

	
 
/** Build a bus or truck stop
 
 * @param tile tile to build the stop at
 
 * @param p1 entrance direction (DiagDirection)
 
 * @param p2 0 for Bus stops, 1 for truck stops
 
 */
 
int32 CmdBuildRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Station *st;
 
	RoadStop *road_stop;
 
	RoadStop **currstop;
 
	RoadStop *prev = NULL;
 
	int32 cost;
 
	int32 ret;
 
	bool type = !!p2;
 

	
 
	/* Saveguard the parameters */
 
	if (!IsValidDiagDirection(p1)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
 
		return CMD_ERROR;
 

	
 
	ret = CheckFlatLandBelow(tile, 1, 1, flags, 1 << p1, NULL);
 
	if (CmdFailed(ret)) return ret;
 
	cost = ret;
 

	
 
	st = GetStationAround(tile, 1, 1, -1);
 
	if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 

	
 
	/* Find a station close to us */
 
	if (st == NULL) {
 
		st = GetClosestStationFromTile(tile, 8, _current_player);
 
		if (st != NULL && st->facilities != 0) st = NULL;
 
	}
 

	
 
	//give us a road stop in the list, and check if something went wrong
 
	road_stop = AllocateRoadStop();
 
	if (road_stop == NULL) {
 
		return_cmd_error(type ? STR_3008B_TOO_MANY_TRUCK_STOPS : STR_3008A_TOO_MANY_BUS_STOPS);
 
	}
 

	
 
	if (st != NULL &&
 
			GetNumRoadStopsInStation(st, RS_BUS) + GetNumRoadStopsInStation(st, RS_TRUCK) >= ROAD_STOP_LIMIT) {
 
		return_cmd_error(type ? STR_3008B_TOO_MANY_TRUCK_STOPS : STR_3008A_TOO_MANY_BUS_STOPS);
 
	}
 

	
 
	if (st != NULL) {
 
		if (st->owner != OWNER_NONE && st->owner != _current_player) {
 
			return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
 
		}
 

	
 
		if (!StationRect_BeforeAddTile(st, tile, RECT_MODE_TEST)) return CMD_ERROR;
 

	
 
		FindRoadStopSpot(type, st, &currstop, &prev);
 
	} else {
 
		Town *t;
 

	
 
		st = AllocateStation();
 
		if (st == NULL) return CMD_ERROR;
 

	
 
		st->town = t = ClosestTownFromTile(tile, (uint)-1);
 

	
 
		FindRoadStopSpot(type, st, &currstop, &prev);
 

	
 
		if (IsValidPlayer(_current_player) && (flags & DC_EXEC)) {
 
			SETBIT(t->have_ratings, _current_player);
 
		}
 

	
 
		st->sign.width_1 = 0;
 

	
 
		if (!GenerateStationName(st, tile, 0)) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) StationInitialize(st, tile);
 
	}
 

	
 
	cost += (type) ? _price.build_truck_station : _price.build_bus_station;
 

	
 
	if (flags & DC_EXEC) {
 
		//point to the correct item in the _busstops or _truckstops array
 
		*currstop = road_stop;
 

	
 
		//initialize an empty station
 
		InitializeRoadStop(road_stop, prev, tile, st->index);
 
		if (!st->facilities) st->xy = tile;
 
		st->facilities |= (type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP;
 
		st->owner = _current_player;
 

	
 
		st->build_date = _date;
 

	
 
		StationRect_BeforeAddTile(st, tile, RECT_MODE_TRY);
 

	
 
		MakeRoadStop(tile, st->owner, st->index, type, p1);
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		UpdateStationAcceptance(st, false);
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
	}
 
	return cost;
 
}
 

	
 
// Remove a bus station
 
static int32 RemoveRoadStop(Station *st, uint32 flags, TileIndex tile)
 
{
 
	RoadStop **primary_stop;
 
	RoadStop *cur_stop;
 
	bool is_truck = IsTruckStop(tile);
 

	
 
	if (_current_player != OWNER_WATER && !CheckOwnership(st->owner)) {
 
		return CMD_ERROR;
 
	}
 

	
 
	if (is_truck) { // truck stop
 
		primary_stop = &st->truck_stops;
 
		cur_stop = GetRoadStopByTile(tile, RS_TRUCK);
 
	} else {
 
		primary_stop = &st->bus_stops;
 
		cur_stop = GetRoadStopByTile(tile, RS_BUS);
 
	}
 

	
 
	assert(cur_stop != NULL);
 

	
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		//we only had one stop left
 
		if (cur_stop->next == NULL && cur_stop->prev == NULL) {
 
			//so we remove ALL stops
 
			*primary_stop = NULL;
 
			st->facilities &= (is_truck) ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP;
 
		} else if (cur_stop == *primary_stop) {
 
			//removed the first stop in the list
 
			//need to set the primary element to the next stop
 
			*primary_stop = (*primary_stop)->next;
 
		}
 

	
 
		DeleteRoadStop(cur_stop);
 
		DoClearSquare(tile);
 
		StationRect_AfterRemoveTile(st, tile);
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		DeleteStationIfEmpty(st);
 
	}
 

	
 
	return (is_truck) ? _price.remove_truck_station : _price.remove_bus_station;
 
}
 

	
 

	
 

	
 
// FIXME -- need to move to its corresponding Airport variable
 
// Country Airfield (small)
 
static const byte _airport_sections_country[] = {
 
	54, 53, 52, 65,
 
	58, 57, 56, 55,
 
	64, 63, 63, 62
 
};
 

	
 
// City Airport (large)
 
static const byte _airport_sections_town[] = {
 
	31,  9, 33,  9,  9, 32,
 
	27, 36, 29, 34,  8, 10,
 
	30, 11, 35, 13, 20, 21,
 
	51, 12, 14, 17, 19, 28,
 
	38, 13, 15, 16, 18, 39,
 
	26, 22, 23, 24, 25, 26
 
};
 

	
 
// Metropolitain Airport (large) - 2 runways
 
static const byte _airport_sections_metropolitan[] = {
 
	 31,  9, 33,  9,  9, 32,
 
	 27, 36, 29, 34,  8, 10,
 
	 30, 11, 35, 13, 20, 21,
 
	102,  8,  8,  8,  8, 28,
 
	 83, 84, 84, 84, 84, 83,
 
	 26, 23, 23, 23, 23, 26
 
};
 

	
 
// International Airport (large) - 2 runways
 
static const byte _airport_sections_international[] = {
 
	88, 89, 89, 89, 89, 89,  88,
 
	51,  8,  8,  8,  8,  8,  32,
 
	30,  8, 11, 27, 11,  8,  10,
 
	32,  8, 11, 27, 11,  8, 114,
 
	87,  8, 11, 85, 11,  8, 114,
 
	87,  8,  8,  8,  8,  8,  90,
 
	26, 23, 23, 23, 23, 23,  26
 
};
 

	
 
// Intercontinental Airport (vlarge) - 4 runways
 
static const byte _airport_sections_intercontinental[] = {
 
	102, 120,  89,  89,  89,  89,  89,  89, 118,
 
	120,  22,  22,  22,  22,  22,  22, 119, 117,
 
	 87,  54,  87,   8,   8,   8,   8,  51, 117,
 
	 87, 162,  87,  85, 116, 116,   8,   9,  10,
 
	 87,   8,   8,  11,  31,  11,   8, 160,  32,
 
	 32, 160,   8,  11,  27,  11,   8,   8,  10,
 
	 87,   8,   8,  11,  30,  11,   8,   8,  10,
 
	 87, 142,   8,  11,  29,  11,  10, 163,  10,
 
	 87, 164,  87,   8,   8,   8,  10,  37, 117,
 
	 87, 120,  89,  89,  89,  89,  89,  89, 119,
 
	121,  22,  22,  22,  22,  22,  22, 119,  37
 
};
 

	
 

	
 
// Commuter Airfield (small)
 
static const byte _airport_sections_commuter[] = {
 
	85, 30, 115, 115, 32,
 
	87, 8,    8,   8, 10,
 
	87, 11,  11,  11, 10,
 
	26, 23,  23,  23, 26
 
};
 

	
 
// Heliport
 
static const byte _airport_sections_heliport[] = {
 
	66,
 
};
 

	
 
// Helidepot
 
static const byte _airport_sections_helidepot[] = {
 
	124, 32,
 
	122, 123
 
};
 

	
 
// Helistation
 
static const byte _airport_sections_helistation[] = {
 
	 32, 134, 159, 158,
 
	161, 142, 142, 157
 
};
 

	
 
static const byte * const _airport_sections[] = {
 
	_airport_sections_country,           // Country Airfield (small)
 
	_airport_sections_town,              // City Airport (large)
 
	_airport_sections_heliport,          // Heliport
 
	_airport_sections_metropolitan,      // Metropolitain Airport (large)
 
	_airport_sections_international,     // International Airport (xlarge)
 
	_airport_sections_commuter,          // Commuter Airport (small)
 
	_airport_sections_helidepot,         // Helidepot
 
	_airport_sections_intercontinental,  // Intercontinental Airport (xxlarge)
 
	_airport_sections_helistation        // Helistation
 
};
 

	
 
/** Place an Airport.
 
 * @param tile tile where airport will be built
 
 * @param p1 airport type, @see airport.h
 
 * @param p2 unused
 
 */
 
int32 CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Town *t;
 
	Station *st;
 
	int32 cost;
 
	int32 ret;
 
	int w, h;
 
	bool airport_upgrade = true;
 
	const AirportFTAClass* afc;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* Check if a valid, buildable airport was chosen for construction */
 
	if (p1 > lengthof(_airport_sections) || !HASBIT(GetValidAirports(), p1)) return CMD_ERROR;
 

	
 
	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
 
		return CMD_ERROR;
 

	
 
	t = ClosestTownFromTile(tile, (uint)-1);
 

	
 
	/* Check if local auth refuses a new airport */
 
	{
 
		uint num = 0;
 
		FOR_ALL_STATIONS(st) {
 
			if (st->town == t && st->facilities&FACIL_AIRPORT && st->airport_type != AT_OILRIG)
 
				num++;
 
		}
 
		if (num >= 2) {
 
			SetDParam(0, t->index);
 
			return_cmd_error(STR_2035_LOCAL_AUTHORITY_REFUSES);
 
		}
 
	}
 

	
 
	afc = GetAirport(p1);
 
	w = afc->size_x;
 
	h = afc->size_y;
 

	
 
	ret = CheckFlatLandBelow(tile, w, h, flags, 0, NULL);
 
	if (CmdFailed(ret)) return ret;
 
	cost = ret;
 

	
 
	st = GetStationAround(tile, w, h, -1);
 
	if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 

	
 
	/* Find a station close to us */
 
	if (st == NULL) {
 
		st = GetClosestStationFromTile(tile, 8, _current_player);
 
		if (st != NULL && st->facilities) st = NULL;
 
	}
 

	
 
	if (w > _patches.station_spread || h > _patches.station_spread) {
 
		_error_message = STR_306C_STATION_TOO_SPREAD_OUT;
 
		return CMD_ERROR;
 
	}
 

	
 
	if (st != NULL) {
 
		if (st->owner != OWNER_NONE && st->owner != _current_player)
 
			return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
 

	
 
		if (!StationRect_BeforeAddRect(st, tile, w, h, RECT_MODE_TEST)) return CMD_ERROR;
 

	
 
		if (st->airport_tile != 0)
 
			return_cmd_error(STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT);
 
	} else {
 
		airport_upgrade = false;
 

	
 
		st = AllocateStation();
 
		if (st == NULL) return CMD_ERROR;
 

	
 
		st->town = t;
 

	
 
		if (IsValidPlayer(_current_player) && (flags & DC_EXEC))
 
			SETBIT(t->have_ratings, _current_player);
 

	
 
		st->sign.width_1 = 0;
 

	
 
		// if airport type equals Heliport then generate
 
		// type 5 name, which is heliport, otherwise airport names (1)
 
		if (!GenerateStationName(st, tile, (p1 == AT_HELIPORT)||(p1 == AT_HELIDEPOT)||(p1 == AT_HELISTATION) ? 5 : 1))
 
			return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) StationInitialize(st, tile);
 
	}
 

	
 
	cost += _price.build_airport * w * h;
 

	
 
	if (flags & DC_EXEC) {
 
		st->owner = _current_player;
 
		st->airport_tile = tile;
 
		if (!st->facilities) st->xy = tile;
 
		st->facilities |= FACIL_AIRPORT;
 
		st->airport_type = (byte)p1;
 
		st->airport_flags = 0;
 

	
 
		st->build_date = _date;
 

	
 
		StationRect_BeforeAddRect(st, tile, w, h, RECT_MODE_TRY);
 

	
 
		/* if airport was demolished while planes were en-route to it, the
 
		 * positions can no longer be the same (v->u.air.pos), since different
 
		 * airports have different indexes. So update all planes en-route to this
 
		 * airport. Only update if
 
		 * 1. airport is upgraded
 
		 * 2. airport is added to existing station (unfortunately unavoideable)
 
		 */
 
		if (airport_upgrade) UpdateAirplanesOnNewStation(st);
 

	
 
		{
 
			const byte *b = _airport_sections[p1];
 

	
 
			BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
 
				MakeAirport(tile_cur, st->owner, st->index, *b++);
 
			} END_TILE_LOOP(tile_cur, w, h, tile)
 
		}
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		UpdateStationAcceptance(st, false);
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
	}
 

	
 
	return cost;
 
}
 

	
 
static int32 RemoveAirport(Station *st, uint32 flags)
 
{
 
	TileIndex tile;
 
	int w,h;
 
	int32 cost;
 
	const AirportFTAClass* afc;
 

	
 
	if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
 
		return CMD_ERROR;
 

	
 
	tile = st->airport_tile;
 

	
 
	afc = GetAirport(st->airport_type);
 
	w = afc->size_x;
 
	h = afc->size_y;
 

	
 
	cost = w * h * _price.remove_airport;
 

	
 
	BEGIN_TILE_LOOP(tile_cur, w, h, tile) {
 
		if (!EnsureNoVehicle(tile_cur)) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
 
			DeleteAnimatedTile(tile_cur);
 
			DoClearSquare(tile_cur);
 
		}
 
	} END_TILE_LOOP(tile_cur, w,h,tile)
 

	
 
	if (flags & DC_EXEC) {
 
		uint i;
 

	
 
		for (i = 0; i < afc->nof_depots; ++i) {
 
			DeleteWindowById(
 
				WC_VEHICLE_DEPOT, tile + ToTileIndexDiff(afc->airport_depots[i])
 
			);
 
		}
 

	
 
		StationRect_AfterRemoveRect(st, tile, w, h);
 

	
 
		st->airport_tile = 0;
 
		st->facilities &= ~FACIL_AIRPORT;
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		DeleteStationIfEmpty(st);
 
	}
 

	
 
	return cost;
 
}
 

	
 
/** Build a buoy.
 
 * @param tile tile where to place the bouy
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdBuildBuoy(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Station *st;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!IsClearWaterTile(tile) || tile == 0) return_cmd_error(STR_304B_SITE_UNSUITABLE);
 

	
 
	st = AllocateStation();
 
	if (st == NULL) return CMD_ERROR;
 

	
 
	st->town = ClosestTownFromTile(tile, (uint)-1);
 
	st->sign.width_1 = 0;
 

	
 
	if (!GenerateStationName(st, tile, 4)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		StationInitialize(st, tile);
 
		st->dock_tile = tile;
 
		st->facilities |= FACIL_DOCK;
 
		/* Buoys are marked in the Station struct by this flag. Yes, it is this
 
		 * braindead.. */
 
		st->had_vehicle_of_type |= HVOT_BUOY;
 
		st->owner = OWNER_NONE;
 

	
 
		st->build_date = _date;
 

	
 
		MakeBuoy(tile, st->index);
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		UpdateStationAcceptance(st, false);
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
	}
 

	
 
	return _price.build_dock;
 
}
 

	
 
/* Checks if any ship is servicing the buoy specified. Returns yes or no */
 
static bool CheckShipsOnBuoy(Station *st)
 
{
 
	const Vehicle *v;
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Ship) {
 
			const Order *order;
 
			FOR_VEHICLE_ORDERS(v, order) {
 
				if (order->type == OT_GOTO_STATION && order->dest == st->index) {
 
					return true;
 
				}
 
			}
 
		}
 
	}
 
	return false;
 
}
 

	
 
static int32 RemoveBuoy(Station *st, uint32 flags)
 
{
 
	TileIndex tile;
 

	
 
	/* XXX: strange stuff */
 
	if (!IsValidPlayer(_current_player))  return_cmd_error(INVALID_STRING_ID);
 

	
 
	tile = st->dock_tile;
 

	
 
	if (CheckShipsOnBuoy(st))   return_cmd_error(STR_BUOY_IS_IN_USE);
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		st->dock_tile = 0;
 
		/* Buoys are marked in the Station struct by this flag. Yes, it is this
 
		 * braindead.. */
 
		st->facilities &= ~FACIL_DOCK;
 
		st->had_vehicle_of_type &= ~HVOT_BUOY;
 

	
 
		MakeWater(tile);
 
		MarkTileDirtyByTile(tile);
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		DeleteStationIfEmpty(st);
 
	}
 

	
 
	return _price.remove_truck_station;
 
}
 

	
 
static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
 
	{-1,  0},
 
	{ 0,  0},
 
	{ 0,  0},
 
	{ 0, -1}
 
};
 
static const byte _dock_w_chk[4] = { 2, 1, 2, 1 };
 
static const byte _dock_h_chk[4] = { 1, 2, 1, 2 };
 

	
 
/** Build a dock/haven.
 
 * @param tile tile where dock will be built
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdBuildDock(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	TileIndex tile_cur;
 
	DiagDirection direction;
 
	int32 cost;
 
	Station *st;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	switch (GetTileSlope(tile, NULL)) {
 
		case SLOPE_SW: direction = DIAGDIR_NE; break;
 
		case SLOPE_SE: direction = DIAGDIR_NW; break;
 
		case SLOPE_NW: direction = DIAGDIR_SE; break;
 
		case SLOPE_NE: direction = DIAGDIR_SW; break;
 
		default: return_cmd_error(STR_304B_SITE_UNSUITABLE);
 
	}
 

	
 
	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile)) return CMD_ERROR;
 

	
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

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

	
 
	tile_cur = tile + TileOffsByDiagDir(direction);
 

	
 
	if (!EnsureNoVehicle(tile_cur)) return CMD_ERROR;
 

	
 
	if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
 
		return_cmd_error(STR_304B_SITE_UNSUITABLE);
 
	}
 

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

	
 
	tile_cur += TileOffsByDiagDir(direction);
 
	if (!IsTileType(tile_cur, MP_WATER) || GetTileSlope(tile_cur, NULL) != SLOPE_FLAT) {
 
		return_cmd_error(STR_304B_SITE_UNSUITABLE);
 
	}
 

	
 
	/* middle */
 
	st = GetStationAround(
 
		tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]),
 
		_dock_w_chk[direction], _dock_h_chk[direction], -1);
 
	if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 

	
 
	/* Find a station close to us */
 
	if (st == NULL) {
 
		st = GetClosestStationFromTile(tile, 8, _current_player);
 
		if (st!=NULL && st->facilities) st = NULL;
 
	}
 

	
 
	if (st != NULL) {
 
		if (st->owner != OWNER_NONE && st->owner != _current_player)
 
			return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
 

	
 
		if (!StationRect_BeforeAddRect(st, tile, _dock_w_chk[direction], _dock_h_chk[direction], RECT_MODE_TEST)) return CMD_ERROR;
 

	
 
		if (st->dock_tile != 0) return_cmd_error(STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK);
 
	} else {
 
		Town *t;
 

	
 
		st = AllocateStation();
 
		if (st == NULL) return CMD_ERROR;
 

	
 
		st->town = t = ClosestTownFromTile(tile, (uint)-1);
 

	
 
		if (IsValidPlayer(_current_player) && (flags & DC_EXEC))
 
			SETBIT(t->have_ratings, _current_player);
 

	
 
		st->sign.width_1 = 0;
 

	
 
		if (!GenerateStationName(st, tile, 3)) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) StationInitialize(st, tile);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		st->dock_tile = tile;
 
		if (!st->facilities) st->xy = tile;
 
		st->facilities |= FACIL_DOCK;
 
		st->owner = _current_player;
 

	
 
		st->build_date = _date;
 

	
 
		StationRect_BeforeAddRect(st, tile, _dock_w_chk[direction], _dock_h_chk[direction], RECT_MODE_TRY);
 

	
 
		MakeDock(tile, st->owner, st->index, direction);
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		UpdateStationAcceptance(st, false);
 
		RebuildStationLists();
 
		InvalidateWindow(WC_STATION_LIST, st->owner);
 
	}
 
	return _price.build_dock;
 
}
 

	
 
static int32 RemoveDock(Station *st, uint32 flags)
 
{
 
	TileIndex tile1;
 
	TileIndex tile2;
 

	
 
	if (!CheckOwnership(st->owner)) return CMD_ERROR;
 

	
 
	tile1 = st->dock_tile;
 
	tile2 = tile1 + TileOffsByDiagDir(GetDockDirection(tile1));
 

	
 
	if (!EnsureNoVehicle(tile1)) return CMD_ERROR;
 
	if (!EnsureNoVehicle(tile2)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		DoClearSquare(tile1);
 
		MakeWater(tile2);
 

	
 
		StationRect_AfterRemoveTile(st, tile1);
 
		StationRect_AfterRemoveTile(st, tile2);
 

	
 
		MarkTileDirtyByTile(tile2);
 

	
 
		st->dock_tile = 0;
 
		st->facilities &= ~FACIL_DOCK;
 

	
 
		UpdateStationVirtCoordDirty(st);
 
		DeleteStationIfEmpty(st);
 
	}
 

	
 
	return _price.remove_dock;
 
}
 

	
 
#include "table/station_land.h"
 

	
 
const DrawTileSprites *GetStationTileLayout(byte gfx)
 
{
 
	return &_station_display_datas[gfx];
 
}
 

	
 
static void DrawTile_Station(TileInfo *ti)
 
{
 
	uint32 image;
 
	const DrawTileSeqStruct *dtss;
 
	const DrawTileSprites *t = NULL;
 
	RailType railtype = GetRailType(ti->tile);
 
	const RailtypeInfo *rti = GetRailTypeInfo(railtype);
 
	uint32 relocation = 0;
 
	const Station *st = NULL;
 
	const StationSpec *statspec = NULL;
 
	PlayerID owner = GetTileOwner(ti->tile);
 
	uint32 palette;
 

	
 
	if (IsValidPlayer(owner)) {
 
		palette = PLAYER_SPRITE_COLOR(owner);
 
	} else {
 
		// Some stations are not owner by a player, namely oil rigs
 
		palette = PALETTE_TO_GREY;
 
	}
 

	
 
	// don't show foundation for docks
 
	if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile))
 
		DrawFoundation(ti, ti->tileh);
 

	
 
	if (IsCustomStationSpecIndex(ti->tile)) {
 
		// look for customization
 
		st = GetStationByTile(ti->tile);
 
		statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
 

	
 
		//debug("Cust-o-mized %p", statspec);
 

	
 
		if (statspec != NULL) {
 
			uint tile = GetStationGfx(ti->tile);
 

	
 
			relocation = GetCustomStationRelocation(statspec, st, ti->tile);
 

	
 
			if (HASBIT(statspec->callbackmask, CBM_CUSTOM_LAYOUT)) {
 
				uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
 
				if (callback != CALLBACK_FAILED) tile = (callback & ~1) + GetRailStationAxis(ti->tile);
 
			}
 

	
 
			/* Ensure the chosen tile layout is valid for this custom station */
 
			if (statspec->renderdata != NULL) {
 
				t = &statspec->renderdata[tile < statspec->tiles ? tile : GetRailStationAxis(ti->tile)];
 
			}
 
		}
 
	}
 

	
 
	if (t == NULL || t->seq == NULL) t = &_station_display_datas[GetStationGfx(ti->tile)];
 

	
 
	image = t->ground_sprite;
 
	if (HASBIT(image, 31)) {
 
		CLRBIT(image, 31);
 
		image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
 
		image += rti->custom_ground_offset;
 
	} else {
 
		image += rti->total_offset;
 
	}
 
	if (image & PALETTE_MODIFIER_COLOR) image |= palette;
 

	
 
	// station_land array has been increased from 82 elements to 114
 
	// but this is something else. If AI builds station with 114 it looks all weird
 
	DrawGroundSprite(image);
 

	
 
	if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
 

	
 
	foreach_draw_tile_seq(dtss, t->seq) {
 
		image = dtss->image;
 
		if (HASBIT(image, 30)) {
 
			CLRBIT(image, 30);
 
			image += rti->total_offset;
 
		} else {
 
			image += relocation;
 
		}
 

	
 
		if (_display_opt & DO_TRANS_BUILDINGS) {
 
			MAKE_TRANSPARENT(image);
 
		} else if (image & PALETTE_MODIFIER_COLOR) {
 
			image |= palette;
 
		}
 

	
 
		if ((byte)dtss->delta_z != 0x80) {
 
			AddSortableSpriteToDraw(
 
				image,
 
				ti->x + dtss->delta_x, ti->y + dtss->delta_y,
 
				dtss->size_x, dtss->size_y,
 
				dtss->size_z, ti->z + dtss->delta_z
 
			);
 
		} else {
 
			AddChildSpriteScreen(image, dtss->delta_x, dtss->delta_y);
 
		}
 
	}
 
}
 

	
 
void StationPickerDrawSprite(int x, int y, RailType railtype, int image)
 
{
 
	uint32 ormod, img;
 
	const DrawTileSeqStruct *dtss;
 
	const DrawTileSprites *t;
 
	const RailtypeInfo *rti = GetRailTypeInfo(railtype);
 

	
 
	ormod = PLAYER_SPRITE_COLOR(_local_player);
 

	
 
	t = &_station_display_datas[image];
 

	
 
	img = t->ground_sprite;
 
	if (img & PALETTE_MODIFIER_COLOR) img |= ormod;
 
	DrawSprite(img + rti->total_offset, x, y);
 

	
 
	foreach_draw_tile_seq(dtss, t->seq) {
 
		Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z);
 
		DrawSprite((dtss->image | ormod) + rti->total_offset, x + pt.x, y + pt.y);
 
	}
 
}
 

	
 
static uint GetSlopeZ_Station(TileIndex tile, uint x, uint y)
 
{
 
	return GetTileMaxZ(tile);
 
}
 

	
 
static Slope GetSlopeTileh_Station(TileIndex tile, Slope tileh)
 
{
 
	return SLOPE_FLAT;
 
}
 

	
 
static void GetAcceptedCargo_Station(TileIndex tile, AcceptedCargo ac)
 
{
 
	/* not used */
 
}
 

	
 
static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
 
{
 
	StringID str;
 

	
 
	td->owner = GetTileOwner(tile);
 
	td->build_date = GetStationByTile(tile)->build_date;
 

	
 
	switch (GetStationType(tile)) {
 
		default: NOT_REACHED();
 
		case STATION_RAIL:    str = STR_305E_RAILROAD_STATION; break;
 
		case STATION_AIRPORT:
 
			str = (IsHangar(tile) ? STR_305F_AIRCRAFT_HANGAR : STR_3060_AIRPORT);
 
			break;
 
		case STATION_TRUCK:   str = STR_3061_TRUCK_LOADING_AREA; break;
 
		case STATION_BUS:     str = STR_3062_BUS_STATION; break;
 
		case STATION_OILRIG:  str = STR_4807_OIL_RIG; break;
 
		case STATION_DOCK:    str = STR_3063_SHIP_DOCK; break;
 
		case STATION_BUOY:    str = STR_3069_BUOY; break;
 
	}
 
	td->str = str;
 
}
 

	
 

	
 
static uint32 GetTileTrackStatus_Station(TileIndex tile, TransportType mode)
 
{
 
	switch (mode) {
 
		case TRANSPORT_RAIL:
 
			if (IsRailwayStation(tile) && !IsStationTileBlocked(tile)) {
 
				return TrackToTrackBits(GetRailStationTrack(tile)) * 0x101;
 
			}
 
			break;
 

	
 
		case TRANSPORT_WATER:
 
			// buoy is coded as a station, it is always on open water
 
			if (IsBuoy_(tile)) return TRACK_BIT_ALL * 0x101;
 
			break;
 

	
 
		case TRANSPORT_ROAD:
 
			if (IsRoadStopTile(tile)) {
 
				return AxisToTrackBits(DiagDirToAxis(GetRoadStopDir(tile))) * 0x101;
 
			}
 
			break;
 

	
 
		default:
 
			break;
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
static void TileLoop_Station(TileIndex tile)
 
{
 
	// FIXME -- GetTileTrackStatus_Station -> animated stationtiles
 
	// hardcoded.....not good
 
	switch (GetStationGfx(tile)) {
 
		case GFX_RADAR_LARGE_FIRST:
 
		case GFX_WINDSACK_FIRST : // for small airport
 
		case GFX_RADAR_INTERNATIONAL_FIRST:
 
		case GFX_RADAR_METROPOLITAN_FIRST:
 
		case GFX_RADAR_DISTRICTWE_FIRST: // radar district W-E airport
 
		case GFX_WINDSACK_INTERCON_FIRST : // for intercontinental airport
 
			AddAnimatedTile(tile);
 
			break;
 

	
 
		case GFX_OILRIG_BASE: //(station part)
 
		case GFX_BUOY_BASE:
 
			TileLoop_Water(tile);
 
			break;
 

	
 
		default: break;
 
	}
 
}
 

	
 

	
 
static void AnimateTile_Station(TileIndex tile)
 
{
 
	typedef struct AnimData {
 
		StationGfx from; // first sprite
 
		StationGfx to;   // last sprite
 
		byte delay;
 
	} AnimData;
 

	
 
	static const AnimData data[] = {
 
		{ GFX_RADAR_LARGE_FIRST,         GFX_RADAR_LARGE_LAST,         3 },
 
		{ GFX_WINDSACK_FIRST,            GFX_WINDSACK_LAST,            1 },
 
		{ GFX_RADAR_INTERNATIONAL_FIRST, GFX_RADAR_INTERNATIONAL_LAST, 3 },
 
		{ GFX_RADAR_METROPOLITAN_FIRST,  GFX_RADAR_METROPOLITAN_LAST,  3 },
 
		{ GFX_RADAR_DISTRICTWE_FIRST,    GFX_RADAR_DISTRICTWE_LAST,    3 },
 
		{ GFX_WINDSACK_INTERCON_FIRST,   GFX_WINDSACK_INTERCON_LAST,   1 }
 
	};
 

	
 
	StationGfx gfx = GetStationGfx(tile);
 
	const AnimData* i;
 

	
 
	for (i = data; i != endof(data); i++) {
 
		if (i->from <= gfx && gfx <= i->to) {
 
			if ((_tick_counter & i->delay) == 0) {
 
				SetStationGfx(tile, gfx < i->to ? gfx + 1 : i->from);
 
				MarkTileDirtyByTile(tile);
 
			}
 
			break;
 
		}
 
	}
 
}
 

	
 

	
 
static void ClickTile_Station(TileIndex tile)
 
{
 
	if (IsHangar(tile)) {
 
		ShowDepotWindow(tile, VEH_Aircraft);
 
	} else {
 
		ShowStationViewWindow(GetStationIndex(tile));
 
	}
 
}
 

	
 
static const byte _enter_station_speedtable[12] = {
 
	215, 195, 175, 155, 135, 115, 95, 75, 55, 35, 15, 0
 
};
 

	
 
static uint32 VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
 
{
 
	if (v->type == VEH_Train) {
 
		if (IsRailwayStation(tile) && IsFrontEngine(v) &&
 
				!IsCompatibleTrainStationTile(tile + TileOffsByDiagDir(DirToDiagDir(v->direction)), tile)) {
 
			StationID station_id = GetStationIndex(tile);
 

	
 
			if ((!(v->current_order.flags & OF_NON_STOP) && !_patches.new_nonstop) ||
 
					(v->current_order.type == OT_GOTO_STATION && v->current_order.dest == station_id)) {
 
				if (!(_patches.new_nonstop && v->current_order.flags & OF_NON_STOP) &&
 
						v->current_order.type != OT_LEAVESTATION &&
 
						v->last_station_visited != station_id) {
 
					DiagDirection dir = DirToDiagDir(v->direction);
 

	
 
					x &= 0xF;
 
					y &= 0xF;
 

	
 
					if (DiagDirToAxis(dir) != AXIS_X) intswap(x, y);
 
					if (y == TILE_SIZE / 2) {
 
						if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
 
						if (x == 12) return 2 | (station_id << 8); /* enter station */
 
						if (x < 12) {
 
							uint16 spd;
 

	
 
							v->vehstatus |= VS_TRAIN_SLOWING;
 
							spd = _enter_station_speedtable[x];
 
							if (spd < v->cur_speed) v->cur_speed = spd;
 
						}
 
					}
 
				}
 
			}
 
		}
 
	} else if (v->type == VEH_Road) {
 
		if (v->u.road.state < 16 && !HASBIT(v->u.road.state, 2) && v->u.road.frame == 0) {
 
			if (IsRoadStop(tile)) {
 
				/* Attempt to allocate a parking bay in a road stop */
 
				RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
 

	
 
				/* rs->status bits 0 and 1 describe current the two parking spots.
 
				 * 0 means occupied, 1 means free. */
 

	
 
				// Check if station is busy or if there are no free bays.
 
				if (HASBIT(rs->status, 7) || GB(rs->status, 0, 2) == 0)
 
					return 8;
 

	
 
				v->u.road.state += 32;
 

	
 
				// if the first bay is free, allocate that, else the second bay must be free.
 
				if (HASBIT(rs->status, 0)) {
 
					CLRBIT(rs->status, 0);
 
				} else {
 
					CLRBIT(rs->status, 1);
 
					v->u.road.state += 2;
 
				}
 

	
 
				// mark the station as busy
 
				SETBIT(rs->status, 7);
 
			}
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
/**
 
 * Cleanup a RoadStop. Make sure no vehicles try to go to this roadstop.
 
 */
 
void DestroyRoadStop(RoadStop* rs)
 
{
 
	Vehicle *v;
 

	
 
	/* Clear the slot assignment of all vehicles heading for this road stop */
 
	if (rs->num_vehicles != 0) {
 
		FOR_ALL_VEHICLES(v) {
 
			if (v->type == VEH_Road && v->u.road.slot == rs) {
 
				ClearSlot(v);
 
			}
 
		}
 
	}
 
	assert(rs->num_vehicles == 0);
 

	
 
	if (rs->prev != NULL) rs->prev->next = rs->next;
 
	if (rs->next != NULL) rs->next->prev = rs->prev;
 
}
 

	
 
/**
 
 * Clean up a station by clearing vehicle orders and invalidating windows.
 
 * Aircraft-Hangar orders need special treatment here, as the hangars are
 
 * actually part of a station (tiletype is STATION), but the order type
 
 * is OT_GOTO_DEPOT.
 
 * @param st Station to be deleted
 
 */
 
void DestroyStation(Station *st)
 
{
 
	StationID index;
 

	
 
	index = st->index;
 

	
 
	DeleteName(st->string_id);
 
	MarkStationDirty(st);
 
	RebuildStationLists();
 
	InvalidateWindowClasses(WC_STATION_LIST);
 

	
 
	DeleteWindowById(WC_STATION_VIEW, index);
 

	
 
	/* Now delete all orders that go to the station */
 
	RemoveOrderFromAllVehicles(OT_GOTO_STATION, index);
 

	
 
	//Subsidies need removal as well
 
	DeleteSubsidyWithStation(index);
 

	
 
	free(st->speclist);
 
}
 

	
 
void DeleteAllPlayerStations(void)
 
{
 
	Station *st;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		if (IsValidPlayer(st->owner)) DeleteStation(st);
 
	}
 
}
 

	
 
/* this function is called for one station each tick */
 
static void StationHandleBigTick(Station *st)
 
{
 
	UpdateStationAcceptance(st, true);
 

	
 
	if (st->facilities == 0 && ++st->delete_ctr >= 8) DeleteStation(st);
 

	
 
}
 

	
 
static inline void byte_inc_sat(byte *p) { byte b = *p + 1; if (b != 0) *p = b; }
 

	
 
static void UpdateStationRating(Station *st)
 
{
 
	GoodsEntry *ge;
 
	int rating;
 
	StationID index;
 
	int waiting;
 
	bool waiting_changed = false;
 

	
 
	byte_inc_sat(&st->time_since_load);
 
	byte_inc_sat(&st->time_since_unload);
 

	
 
	ge = st->goods;
 
	do {
 
		if (ge->enroute_from != INVALID_STATION) {
 
			byte_inc_sat(&ge->enroute_time);
 
			byte_inc_sat(&ge->days_since_pickup);
 

	
 
			rating = 0;
 

	
 
			{
 
				int b = ge->last_speed;
 
				if ((b-=85) >= 0)
 
					rating += b >> 2;
 
			}
 

	
 
			{
 
				byte age = ge->last_age;
 
				(age >= 3) ||
 
				(rating += 10, age >= 2) ||
 
				(rating += 10, age >= 1) ||
 
				(rating += 13, true);
 
			}
 

	
 
			if (IsValidPlayer(st->owner) && HASBIT(st->town->statues, st->owner)) rating += 26;
 

	
 
			{
 
				byte days = ge->days_since_pickup;
 
				if (st->last_vehicle_type == VEH_Ship)
 
							days >>= 2;
 
				(days > 21) ||
 
				(rating += 25, days > 12) ||
 
				(rating += 25, days > 6) ||
 
				(rating += 45, days > 3) ||
 
				(rating += 35, true);
 
			}
 

	
 
			{
 
				waiting = GB(ge->waiting_acceptance, 0, 12);
 
				(rating -= 90, waiting > 1500) ||
 
				(rating += 55, waiting > 1000) ||
 
				(rating += 35, waiting > 600) ||
 
				(rating += 10, waiting > 300) ||
 
				(rating += 20, waiting > 100) ||
 
				(rating += 10, true);
 
			}
 

	
 
			{
 
				int or = ge->rating; // old rating
 

	
 
				// only modify rating in steps of -2, -1, 0, 1 or 2
 
				ge->rating = rating = or + clamp(clamp(rating, 0, 255) - or, -2, 2);
 

	
 
				// if rating is <= 64 and more than 200 items waiting, remove some random amount of goods from the station
 
				if (rating <= 64 && waiting >= 200) {
 
					int dec = Random() & 0x1F;
 
					if (waiting < 400) dec &= 7;
 
					waiting -= dec + 1;
 
					waiting_changed = true;
 
				}
 

	
 
				// if rating is <= 127 and there are any items waiting, maybe remove some goods.
 
				if (rating <= 127 && waiting != 0) {
 
					uint32 r = Random();
 
					if ( (uint)rating <= (r & 0x7F) ) {
 
						waiting = max(waiting - ((r >> 8)&3) - 1, 0);
 
						waiting_changed = true;
 
					}
 
				}
 

	
 
				if (waiting_changed) SB(ge->waiting_acceptance, 0, 12, waiting);
 
			}
 
		}
 
	} while (++ge != endof(st->goods));
 

	
 
	index = st->index;
 

	
 
	if (waiting_changed) {
 
		InvalidateWindow(WC_STATION_VIEW, index);
 
	} else {
 
		InvalidateWindowWidget(WC_STATION_VIEW, index, 5);
 
	}
 
}
 

	
 
/* called for every station each tick */
 
static void StationHandleSmallTick(Station *st)
 
{
 
	byte b;
 

	
 
	if (st->facilities == 0) return;
 

	
 
	b = st->delete_ctr + 1;
 
	if (b >= 185) b = 0;
 
	st->delete_ctr = b;
 

	
 
	if (b == 0) UpdateStationRating(st);
 
}
 

	
 
void OnTick_Station(void)
 
{
 
	uint i;
 
	Station *st;
 

	
 
	if (_game_mode == GM_EDITOR) return;
 

	
 
	i = _station_tick_ctr;
 
	if (++_station_tick_ctr > GetMaxStationIndex()) _station_tick_ctr = 0;
 

	
 
	if (IsValidStationID(i)) StationHandleBigTick(GetStation(i));
 

	
 
	FOR_ALL_STATIONS(st) {
 
		StationHandleSmallTick(st);
 
	}
 
}
 

	
 
void StationMonthlyLoop(void)
 
{
 
}
 

	
 

	
 
void ModifyStationRatingAround(TileIndex tile, PlayerID owner, int amount, uint radius)
 
{
 
	Station *st;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		if (st->owner == owner &&
 
				DistanceManhattan(tile, st->xy) <= radius) {
 
			uint i;
 

	
 
			for (i = 0; i != NUM_CARGO; i++) {
 
				GoodsEntry* ge = &st->goods[i];
 

	
 
				if (ge->enroute_from != INVALID_STATION) {
 
					ge->rating = clamp(ge->rating + amount, 0, 255);
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 
static void UpdateStationWaiting(Station *st, int type, uint amount)
 
{
 
	SB(st->goods[type].waiting_acceptance, 0, 12,
 
		min(0xFFF, GB(st->goods[type].waiting_acceptance, 0, 12) + amount)
 
	);
 

	
 
	st->goods[type].enroute_time = 0;
 
	st->goods[type].enroute_from = st->index;
 
	InvalidateWindow(WC_STATION_VIEW, st->index);
 
	MarkStationTilesDirty(st);
 
}
 

	
 
/** Rename a station
 
 * @param tile unused
 
 * @param p1 station ID that is to be renamed
 
 * @param p2 unused
 
 */
 
int32 CmdRenameStation(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	StringID str;
 
	Station *st;
 

	
 
	if (!IsValidStationID(p1) || _cmd_text[0] == '\0') return CMD_ERROR;
 
	st = GetStation(p1);
 

	
 
	if (!CheckOwnership(st->owner)) return CMD_ERROR;
 

	
 
	str = AllocateNameUnique(_cmd_text, 6);
 
	if (str == 0) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		StringID old_str = st->string_id;
 

	
 
		st->string_id = str;
 
		UpdateStationVirtCoord(st);
 
		DeleteName(old_str);
 
		ResortStationLists();
 
		MarkWholeScreenDirty();
 
	} else {
 
		DeleteName(str);
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
uint MoveGoodsToStation(TileIndex tile, int w, int h, int type, uint amount)
 
{
 
	Station* around[8];
 
	uint i;
 
	uint moved;
 
	uint best_rating, best_rating2;
 
	Station *st1, *st2;
 
	uint t;
 
	int rad = 0;
 
	int w_prod; //width and height of the "producer" of the cargo
 
	int h_prod;
 
	int max_rad;
 

	
 
	for (i = 0; i < lengthof(around); i++) around[i] = NULL;
 

	
 
	if (_patches.modified_catchment) {
 
		w_prod = w;
 
		h_prod = h;
 
		w += 16;
 
		h += 16;
 
		max_rad = 8;
 
	} else {
 
		w_prod = 0;
 
		h_prod = 0;
 
		w += 8;
 
		h += 8;
 
		max_rad = 4;
 
	}
 

	
 
	BEGIN_TILE_LOOP(cur_tile, w, h, tile - TileDiffXY(max_rad, max_rad))
 
		Station* st;
 

	
 
		cur_tile = TILE_MASK(cur_tile);
 
		if (!IsTileType(cur_tile, MP_STATION)) continue;
 

	
 
		st = GetStationByTile(cur_tile);
 

	
 
		for (i = 0; i != lengthof(around); i++) {
 
			if (around[i] == NULL) {
 
				if (!IsBuoy(st) &&
 
						(st->town->exclusive_counter == 0 || st->town->exclusivity == st->owner) && // check exclusive transport rights
 
						st->goods[type].rating != 0 &&
 
						(!_patches.selectgoods || st->goods[type].last_speed > 0) && // if last_speed is 0, no vehicle has been there.
 
						((st->facilities & ~FACIL_BUS_STOP)   != 0 || type == CT_PASSENGERS) && // if we have other fac. than a bus stop, or the cargo is passengers
 
						((st->facilities & ~FACIL_TRUCK_STOP) != 0 || type != CT_PASSENGERS)) { // if we have other fac. than a cargo bay or the cargo is not passengers
 
					int x_dist;
 
					int y_dist;
 

	
 
					if (_patches.modified_catchment) {
 
						// min and max coordinates of the producer relative
 
						const int x_min_prod = 9;
 
						const int x_max_prod = 8 + w_prod;
 
						const int y_min_prod = 9;
 
						const int y_max_prod = 8 + h_prod;
 

	
 
						rad = FindCatchmentRadius(st);
 

	
 
						x_dist = min(w_cur - x_min_prod, x_max_prod - w_cur);
 
						if (w_cur < x_min_prod) {
 
							x_dist = x_min_prod - w_cur;
 
						} else if (w_cur > x_max_prod) {
 
							x_dist = w_cur - x_max_prod;
 
						}
 

	
 
						y_dist = min(h_cur - y_min_prod, y_max_prod - h_cur);
 
						if (h_cur < y_min_prod) {
 
							y_dist = y_min_prod - h_cur;
 
						} else if (h_cur > y_max_prod) {
 
							y_dist = h_cur - y_max_prod;
 
						}
 
					} else {
 
						x_dist = 0;
 
						y_dist = 0;
 
					}
 

	
 
					if (x_dist <= rad && y_dist <= rad) around[i] = st;
 
				}
 
				break;
 
			} else if (around[i] == st) {
 
				break;
 
			}
 
		}
 
	END_TILE_LOOP(cur_tile, w, h, tile - TileDiffXY(max_rad, max_rad))
 

	
 
	/* no stations around at all? */
 
	if (around[0] == NULL) return 0;
 

	
 
	if (around[1] == NULL) {
 
		/* only one station around */
 
		moved = (amount * around[0]->goods[type].rating >> 8) + 1;
 
		UpdateStationWaiting(around[0], type, moved);
 
		return moved;
 
	}
 

	
 
	/* several stations around, find the two with the highest rating */
 
	st2 = st1 = NULL;
 
	best_rating = best_rating2 = 0;
 

	
 
	for (i = 0; i != lengthof(around) && around[i] != NULL; i++) {
 
		if (around[i]->goods[type].rating >= best_rating) {
 
			best_rating2 = best_rating;
 
			st2 = st1;
 

	
 
			best_rating = around[i]->goods[type].rating;
 
			st1 = around[i];
 
		} else if (around[i]->goods[type].rating >= best_rating2) {
 
			best_rating2 = around[i]->goods[type].rating;
 
			st2 = around[i];
 
		}
 
	}
 

	
 
	assert(st1 != NULL);
 
	assert(st2 != NULL);
 
	assert(best_rating != 0 || best_rating2 != 0);
 

	
 
	/* the 2nd highest one gets a penalty */
 
	best_rating2 >>= 1;
 

	
 
	/* amount given to station 1 */
 
	t = (best_rating * (amount + 1)) / (best_rating + best_rating2);
 

	
 
	moved = 0;
 
	if (t != 0) {
 
		moved = t * best_rating / 256 + 1;
 
		amount -= t;
 
		UpdateStationWaiting(st1, type, moved);
 
	}
 

	
 
	if (amount != 0) {
 
		amount = amount * best_rating2 / 256 + 1;
 
		moved += amount;
 
		UpdateStationWaiting(st2, type, amount);
 
	}
 

	
 
	return moved;
 
}
 

	
 
void BuildOilRig(TileIndex tile)
 
{
 
	uint j;
 
	Station *st = AllocateStation();
 

	
 
	if (st == NULL) {
 
		DEBUG(misc, 0, "Can't allocate station for oilrig at 0x%X, reverting to oilrig only", tile);
 
		return;
 
	}
 
	if (!GenerateStationName(st, tile, 2)) {
 
		DEBUG(misc, 0, "Can't allocate station-name for oilrig at 0x%X, reverting to oilrig only", tile);
 
		return;
 
	}
 

	
 
	st->town = ClosestTownFromTile(tile, (uint)-1);
 
	st->sign.width_1 = 0;
 

	
 
	MakeOilrig(tile, st->index);
 

	
 
	st->owner = OWNER_NONE;
 
	st->airport_flags = 0;
 
	st->airport_type = AT_OILRIG;
 
	st->xy = tile;
 
	st->bus_stops = NULL;
 
	st->truck_stops = NULL;
 
	st->airport_tile = tile;
 
	st->dock_tile = tile;
 
	st->train_tile = 0;
 
	st->had_vehicle_of_type = 0;
 
	st->time_since_load = 255;
 
	st->time_since_unload = 255;
 
	st->delete_ctr = 0;
 
	st->last_vehicle_type = VEH_Invalid;
 
	st->facilities = FACIL_AIRPORT | FACIL_DOCK;
 
	st->build_date = _date;
 

	
 
	for (j = 0; j != NUM_CARGO; j++) {
 
		st->goods[j].waiting_acceptance = 0;
 
		st->goods[j].days_since_pickup = 0;
 
		st->goods[j].enroute_from = INVALID_STATION;
 
		st->goods[j].rating = 175;
 
		st->goods[j].last_speed = 0;
 
		st->goods[j].last_age = 255;
 
	}
 

	
 
	UpdateStationVirtCoordDirty(st);
 
	UpdateStationAcceptance(st, false);
 
}
 

	
 
void DeleteOilRig(TileIndex tile)
 
{
 
	Station* st = GetStationByTile(tile);
 

	
 
	DoClearSquare(tile);
 

	
 
	st->dock_tile = 0;
 
	st->airport_tile = 0;
 
	st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK);
 
	st->airport_flags = 0;
 
	UpdateStationVirtCoordDirty(st);
 
	DeleteStation(st);
 
}
 

	
 
static void ChangeTileOwner_Station(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	if (!IsTileOwner(tile, old_player)) return;
 

	
 
	if (new_player != PLAYER_SPECTATOR) {
 
		Station* st = GetStationByTile(tile);
 

	
 
		SetTileOwner(tile, new_player);
 
		st->owner = new_player;
 
		RebuildStationLists();
 
		InvalidateWindowClasses(WC_STATION_LIST);
 
	} else {
 
		DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
	}
 
}
 

	
 
static int32 ClearTile_Station(TileIndex tile, byte flags)
 
{
 
	Station *st;
 

	
 
	if (flags & DC_AUTO) {
 
		switch (GetStationType(tile)) {
 
			case STATION_RAIL:    return_cmd_error(STR_300B_MUST_DEMOLISH_RAILROAD);
 
			case STATION_AIRPORT: return_cmd_error(STR_300E_MUST_DEMOLISH_AIRPORT_FIRST);
 
			case STATION_TRUCK:   return_cmd_error(STR_3047_MUST_DEMOLISH_TRUCK_STATION);
 
			case STATION_BUS:     return_cmd_error(STR_3046_MUST_DEMOLISH_BUS_STATION);
 
			case STATION_BUOY:    return_cmd_error(STR_306A_BUOY_IN_THE_WAY);
 
			case STATION_DOCK:    return_cmd_error(STR_304D_MUST_DEMOLISH_DOCK_FIRST);
 
			case STATION_OILRIG:
 
				SetDParam(0, STR_4807_OIL_RIG);
 
				return_cmd_error(STR_4800_IN_THE_WAY);
 
		}
 
	}
 

	
 
	st = GetStationByTile(tile);
 

	
 
	switch (GetStationType(tile)) {
 
		case STATION_RAIL:    return RemoveRailroadStation(st, tile, flags);
 
		case STATION_AIRPORT: return RemoveAirport(st, flags);
 
		case STATION_TRUCK:
 
		case STATION_BUS:     return RemoveRoadStop(st, flags, tile);
 
		case STATION_BUOY:    return RemoveBuoy(st, flags);
 
		case STATION_DOCK:    return RemoveDock(st, flags);
 
		default: break;
 
	}
 

	
 
	return CMD_ERROR;
 
}
 

	
 
void InitializeStations(void)
 
{
 
	/* Clean the station pool and create 1 block in it */
 
	CleanPool(&_Station_pool);
 
	AddBlockToPool(&_Station_pool);
 

	
 
	/* Clean the roadstop pool and create 1 block in it */
 
	CleanPool(&_RoadStop_pool);
 
	AddBlockToPool(&_RoadStop_pool);
 

	
 
	_station_tick_ctr = 0;
 

	
 
}
 

	
 

	
 
void AfterLoadStations(void)
 
{
 
	Station *st;
 
	uint i;
 
	TileIndex tile;
 

	
 
	/* Update the speclists of all stations to point to the currently loaded custom stations. */
 
	FOR_ALL_STATIONS(st) {
 
		for (i = 0; i < st->num_specs; i++) {
 
			if (st->speclist[i].grfid == 0) continue;
 

	
 
			st->speclist[i].spec = GetCustomStationSpecByGrf(st->speclist[i].grfid, st->speclist[i].localidx);
 
		}
 
	}
 

	
 
	for (tile = 0; tile < MapSize(); tile++) {
 
		if (GetTileType(tile) != MP_STATION) continue;
 
		st = GetStationByTile(tile);
 
		StationRect_BeforeAddTile(st, tile, RECT_MODE_FORCE);
 
	}
 
}
 

	
 

	
 
const TileTypeProcs _tile_type_station_procs = {
 
	DrawTile_Station,           /* draw_tile_proc */
 
	GetSlopeZ_Station,          /* get_slope_z_proc */
 
	ClearTile_Station,          /* clear_tile_proc */
 
	GetAcceptedCargo_Station,   /* get_accepted_cargo_proc */
 
	GetTileDesc_Station,        /* get_tile_desc_proc */
 
	GetTileTrackStatus_Station, /* get_tile_track_status_proc */
 
	ClickTile_Station,          /* click_tile_proc */
 
	AnimateTile_Station,        /* animate_tile_proc */
 
	TileLoop_Station,           /* tile_loop_clear */
 
	ChangeTileOwner_Station,    /* change_tile_owner_clear */
 
	NULL,                       /* get_produced_cargo_proc */
 
	VehicleEnter_Station,       /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_Station,      /* get_slope_tileh_proc */
 
};
 

	
 
static const SaveLoad _roadstop_desc[] = {
 
	SLE_VAR(RoadStop,xy,           SLE_UINT32),
 
	SLE_VAR(RoadStop,used,         SLE_BOOL),
 
	SLE_VAR(RoadStop,status,       SLE_UINT8),
 
	/* Index was saved in some versions, but this is not needed */
 
	SLE_CONDNULL(4, 0, 8),
 
	SLE_VAR(RoadStop,station,      SLE_UINT16),
 
	SLE_CONDNULL(1, 0, 25),
 

	
 
	SLE_REF(RoadStop,next,         REF_ROADSTOPS),
 
	SLE_REF(RoadStop,prev,         REF_ROADSTOPS),
 

	
 
	SLE_CONDNULL(4, 0, 24),
 
	SLE_CONDNULL(1, 25, 25),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _station_desc[] = {
 
	SLE_CONDVAR(Station, xy,                         SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Station, xy,                         SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Station, bus_tile_obsolete,          SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Station, lorry_tile_obsolete,        SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Station, train_tile,                 SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Station, train_tile,                 SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Station, airport_tile,               SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Station, airport_tile,               SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Station, dock_tile,                  SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Station, dock_tile,                  SLE_UINT32,                  6, SL_MAX_VERSION),
 
	    SLE_REF(Station, town,                       REF_TOWN),
 
	    SLE_VAR(Station, trainst_w,                  SLE_UINT8),
 
	SLE_CONDVAR(Station, trainst_h,                  SLE_UINT8,                   2, SL_MAX_VERSION),
 

	
 
	// alpha_order was stored here in savegame format 0 - 3
 
	SLE_CONDNULL(1, 0, 3),
 

	
 
	    SLE_VAR(Station, string_id,                  SLE_STRINGID),
 
	    SLE_VAR(Station, had_vehicle_of_type,        SLE_UINT16),
 

	
 
	    SLE_VAR(Station, time_since_load,            SLE_UINT8),
 
	    SLE_VAR(Station, time_since_unload,          SLE_UINT8),
 
	    SLE_VAR(Station, delete_ctr,                 SLE_UINT8),
 
	    SLE_VAR(Station, owner,                      SLE_UINT8),
 
	    SLE_VAR(Station, facilities,                 SLE_UINT8),
 
	    SLE_VAR(Station, airport_type,               SLE_UINT8),
 

	
 
	// truck/bus_stop_status was stored here in savegame format 0 - 6
 
	SLE_CONDVAR(Station, truck_stop_status_obsolete, SLE_UINT8, 0, 5),
 
	SLE_CONDVAR(Station, bus_stop_status_obsolete,   SLE_UINT8, 0, 5),
 

	
 
	// blocked_months was stored here in savegame format 0 - 4.0
 
	SLE_CONDVAR(Station, blocked_months_obsolete,    SLE_UINT8, 0, 4),
 

	
 
	SLE_CONDVAR(Station, airport_flags,              SLE_VAR_U32 | SLE_FILE_U16,  0,  2),
 
	SLE_CONDVAR(Station, airport_flags,              SLE_UINT32,                  3, SL_MAX_VERSION),
 

	
 
	SLE_CONDNULL(2, 0, 25), /* Ex last-vehicle */
 
	SLE_CONDVAR(Station, last_vehicle_type,          SLE_UINT8,                  26, SL_MAX_VERSION),
 

	
 
	// Was custom station class and id
 
	SLE_CONDNULL(2, 3, 25),
 
	SLE_CONDVAR(Station, build_date,                 SLE_FILE_U16 | SLE_VAR_I32,  3, 30),
 
	SLE_CONDVAR(Station, build_date,                 SLE_INT32,                  31, SL_MAX_VERSION),
 

	
 
	SLE_CONDREF(Station, bus_stops,                  REF_ROADSTOPS,               6, SL_MAX_VERSION),
 
	SLE_CONDREF(Station, truck_stops,                REF_ROADSTOPS,               6, SL_MAX_VERSION),
 

	
 
	/* Used by newstations for graphic variations */
 
	SLE_CONDVAR(Station, random_bits,                SLE_UINT16,                 27, SL_MAX_VERSION),
 
	SLE_CONDVAR(Station, waiting_triggers,           SLE_UINT8,                  27, SL_MAX_VERSION),
 
	SLE_CONDVAR(Station, num_specs,                  SLE_UINT8,                  27, SL_MAX_VERSION),
 

	
 
	// reserve extra space in savegame here. (currently 32 bytes)
 
	SLE_CONDNULL(32, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _goods_desc[] = {
 
	    SLE_VAR(GoodsEntry, waiting_acceptance, SLE_UINT16),
 
	    SLE_VAR(GoodsEntry, days_since_pickup,  SLE_UINT8),
 
	    SLE_VAR(GoodsEntry, rating,             SLE_UINT8),
 
	SLE_CONDVAR(GoodsEntry, enroute_from,       SLE_FILE_U8 | SLE_VAR_U16,  0, 6),
 
	SLE_CONDVAR(GoodsEntry, enroute_from,       SLE_UINT16,                 7, SL_MAX_VERSION),
 
	    SLE_VAR(GoodsEntry, enroute_time,       SLE_UINT8),
 
	    SLE_VAR(GoodsEntry, last_speed,         SLE_UINT8),
 
	    SLE_VAR(GoodsEntry, last_age,           SLE_UINT8),
 
	SLE_CONDVAR(GoodsEntry, feeder_profit,      SLE_INT32,                 14, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _station_speclist_desc[] = {
 
	SLE_CONDVAR(StationSpecList, grfid,    SLE_UINT32, 27, SL_MAX_VERSION),
 
	SLE_CONDVAR(StationSpecList, localidx, SLE_UINT8,  27, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 

	
 
static void SaveLoad_STNS(Station *st)
 
{
 
	uint i;
 

	
 
	SlObject(st, _station_desc);
 
	for (i = 0; i != NUM_CARGO; i++) {
 
		SlObject(&st->goods[i], _goods_desc);
 

	
 
		/* In older versions, enroute_from had 0xFF as INVALID_STATION, is now 0xFFFF */
 
		if (CheckSavegameVersion(7) && st->goods[i].enroute_from == 0xFF) {
 
			st->goods[i].enroute_from = INVALID_STATION;
 
		}
 
	}
 

	
 
	if (st->num_specs != 0) {
 
		/* Allocate speclist memory when loading a game */
 
		if (st->speclist == NULL) st->speclist = calloc(st->num_specs, sizeof(*st->speclist));
 
		for (i = 0; i < st->num_specs; i++) SlObject(&st->speclist[i], _station_speclist_desc);
 
	}
 
}
 

	
 
static void Save_STNS(void)
 
{
 
	Station *st;
 
	// Write the stations
 
	FOR_ALL_STATIONS(st) {
 
		SlSetArrayIndex(st->index);
 
		SlAutolength((AutolengthProc*)SaveLoad_STNS, st);
 
	}
 
}
 

	
 
static void Load_STNS(void)
 
{
 
	int index;
 
	while ((index = SlIterateArray()) != -1) {
 
		Station *st;
 

	
 
		if (!AddBlockIfNeeded(&_Station_pool, index))
 
			error("Stations: failed loading savegame: too many stations");
 

	
 
		st = GetStation(index);
 
		SaveLoad_STNS(st);
 

	
 
		// this means it's an oldstyle savegame without support for nonuniform stations
 
		if (st->train_tile != 0 && st->trainst_h == 0) {
 
			uint w = GB(st->trainst_w, 4, 4);
 
			uint h = GB(st->trainst_w, 0, 4);
 

	
 
			if (GetRailStationAxis(st->train_tile) == AXIS_Y) uintswap(w, h);
 
			st->trainst_w = w;
 
			st->trainst_h = h;
 
		}
 

	
 
		/* In older versions, we had just 1 tile for a bus/lorry, now we have more..
 
		 *  convert, if needed */
 
		if (CheckSavegameVersion(6)) {
 
			if (st->bus_tile_obsolete != 0) {
 
				st->bus_stops = AllocateRoadStop();
 
				if (st->bus_stops == NULL)
 
					error("Station: too many busstations in savegame");
 

	
 
				InitializeRoadStop(st->bus_stops, NULL, st->bus_tile_obsolete, st->index);
 
			}
 
			if (st->lorry_tile_obsolete != 0) {
 
				st->truck_stops = AllocateRoadStop();
 
				if (st->truck_stops == NULL)
 
					error("Station: too many truckstations in savegame");
 

	
 
				InitializeRoadStop(st->truck_stops, NULL, st->lorry_tile_obsolete, st->index);
 
			}
 
		}
 
	}
 

	
 
	/* This is to ensure all pointers are within the limits of _stations_size */
 
	if (_station_tick_ctr > GetMaxStationIndex()) _station_tick_ctr = 0;
 
}
 

	
 
static void Save_ROADSTOP(void)
 
{
 
	RoadStop *rs;
 

	
 
	FOR_ALL_ROADSTOPS(rs) {
 
		SlSetArrayIndex(rs->index);
 
		SlObject(rs, _roadstop_desc);
 
	}
 
}
 

	
 
static void Load_ROADSTOP(void)
 
{
 
	int index;
 
	Vehicle *v;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		RoadStop *rs;
 

	
 
		if (!AddBlockIfNeeded(&_RoadStop_pool, index))
 
			error("RoadStops: failed loading savegame: too many RoadStops");
 

	
 
		rs = GetRoadStop(index);
 
		SlObject(rs, _roadstop_desc);
 
	}
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Road && v->u.road.slot != NULL) v->u.road.slot->num_vehicles++;
 
	}
 
}
 

	
 
const ChunkHandler _station_chunk_handlers[] = {
 
	{ 'STNS', Save_STNS,      Load_STNS,      CH_ARRAY },
 
	{ 'ROAD', Save_ROADSTOP,  Load_ROADSTOP,  CH_ARRAY | CH_LAST},
 
};
 

	
 

	
 
static inline bool PtInRectXY(Rect *r, int x, int y)
 
{
 
	return (r->left <= x && x <= r->right && r->top <= y && y <= r->bottom);
 
}
 

	
 
static void StationRect_Init(Station *st)
 
{
 
	Rect *r = &st->rect;
 
	r->left = r->top = r->right = r->bottom = 0;
 
}
 

	
 
static bool StationRect_IsEmpty(Station *st)
 
{
 
	return (st->rect.left == 0 || st->rect.left > st->rect.right || st->rect.top > st->rect.bottom);
 
}
 

	
 
static bool StationRect_BeforeAddTile(Station *st, TileIndex tile, StationRectMode mode)
 
{
 
	Rect *r = &st->rect;
 
	int x = TileX(tile);
 
	int y = TileY(tile);
 
	if (StationRect_IsEmpty(st)) {
 
		// we are adding the first station tile
 
		r->left = r->right = x;
 
		r->top = r->bottom = y;
 
	} else if (!PtInRectXY(r, x, y)) {
 
		// current rect is not empty and new point is outside this rect
 
		// make new spread-out rectangle
 
		Rect new_rect = {min(x, r->left), min(y, r->top), max(x, r->right), max(y, r->bottom)};
 
		// check new rect dimensions against preset max
 
		int w = new_rect.right - new_rect.left + 1;
 
		int h = new_rect.bottom - new_rect.top + 1;
 
		if (mode != RECT_MODE_FORCE && (w > _patches.station_spread || h > _patches.station_spread)) {
 
			assert(mode != RECT_MODE_TRY);
 
			_error_message = STR_306C_STATION_TOO_SPREAD_OUT;
 
			return false;
 
		}
 
		// spread-out ok, return true
 
		if (mode != RECT_MODE_TEST) {
 
			// we should update the station rect
 
			*r = new_rect;
 
		}
 
	} else {
 
		; // new point is inside the rect, we don't need to do anything
 
	}
 
	return true;
 
}
 

	
 
static bool StationRect_BeforeAddRect(Station *st, TileIndex tile, int w, int h, StationRectMode mode)
 
{
 
	return StationRect_BeforeAddTile(st, tile, mode) && StationRect_BeforeAddTile(st, TILE_ADDXY(tile, w - 1, h - 1), mode);
 
}
 

	
 
static inline bool ScanRectForStationTiles(StationID st_id, int left, int top, int right, int bottom)
 
{
 
	TileIndex top_left = TileXY(left, top);
 
	int width = right - left + 1;
 
	int height = bottom - top + 1;
 
	BEGIN_TILE_LOOP(tile, width, height, top_left)
 
		if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st_id) return true;
 
	END_TILE_LOOP(tile, width, height, top_left);
 
	return false;
 
}
 

	
 
static bool StationRect_AfterRemoveTile(Station *st, TileIndex tile)
 
{
 
	Rect *r = &st->rect;
 
	int x = TileX(tile);
 
	int y = TileY(tile);
 
	bool reduce_x, reduce_y;
 

	
 
	// look if removed tile was on the bounding rect edge
 
	// and try to reduce the rect by this edge
 
	// do it until we have empty rect or nothing to do
 
	for (;;) {
 
		// check if removed tile is on rect edge
 
		bool left_edge = (x == r->left);
 
		bool right_edge = (x == r->right);
 
		bool top_edge = (y == r->top);
 
		bool bottom_edge = (y == r->bottom);
 
		// can we reduce the rect in either direction?
 
		reduce_x = ((left_edge || right_edge) && !ScanRectForStationTiles(st->index, x, r->top, x, r->bottom));
 
		reduce_y = ((top_edge || bottom_edge) && !ScanRectForStationTiles(st->index, r->left, y, r->right, y));
 
		if (!(reduce_x || reduce_y)) break; // nothing to do (can't reduce)
 
		if (reduce_x) {
 
			// reduce horizontally
 
			if (left_edge) {
 
				// move left edge right
 
				r->left = x = x + 1;
 
			} else {
 
				// move right edge left
 
				r->right = x = x - 1;
 
			}
 
		}
 
		if (reduce_y) {
 
			// reduce vertically
 
			if (top_edge) {
 
				// move top edge down
 
				r->top = y = y + 1;
 
			} else {
 
				// move bottom edge up
 
				r->bottom = y = y - 1;
 
			}
 
		}
 
		if (r->left > r->right || r->top > r->bottom) {
 
		// can't continue, if the remaining rectangle is empty
 
			StationRect_Init(st);
 
			return true; // empty remaining rect
 
		}
 
	}
 
	return false; // non-empty remaining rect
 
}
 

	
 
static bool StationRect_AfterRemoveRect(Station *st, TileIndex tile, int w, int h)
 
{
 
	bool empty;
 
	assert(PtInRectXY(&st->rect, TileX(tile), TileY(tile)));
 
	assert(PtInRectXY(&st->rect, TileX(tile) + w - 1, TileY(tile) + h - 1));
 
	empty = StationRect_AfterRemoveTile(st, tile);
 
	if (w != 1 || h != 1) empty = empty || StationRect_AfterRemoveTile(st, TILE_ADDXY(tile, w - 1, h - 1));
 
	return empty;
 
}
src/station_gui.c
Show inline comments
 
deleted file
src/station_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "strings.h"
 
#include "table/strings.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "station.h"
 
#include "gfx.h"
 
#include "player.h"
 
#include "economy.h"
 
#include "town.h"
 
#include "command.h"
 
#include "variables.h"
 
#include "vehicle_gui.h"
 
#include "date.h"
 
#include "vehicle.h"
 

	
 
enum StationListWidgets {
 
	STATIONLIST_WIDGET_CLOSEBOX = 0,
 
	STATIONLIST_WIDGET_LIST = 3,
 
	STATIONLIST_WIDGET_TRAIN =6,
 
	STATIONLIST_WIDGET_TRUCK,
 
	STATIONLIST_WIDGET_BUS,
 
	STATIONLIST_WIDGET_AIRPLANE,
 
	STATIONLIST_WIDGET_SHIP,
 
	STATIONLIST_WIDGET_CARGOSTART = 12,
 
	STATIONLIST_WIDGET_NOCARGOWAITING = 24,
 
	STATIONLIST_WIDGET_FACILALL = 26,
 
	STATIONLIST_WIDGET_CARGOALL,
 
	STATIONLIST_WIDGET_SORTBY,
 
	STATIONLIST_WIDGET_SORTCRITERIA,
 
	STATIONLIST_WIDGET_SORTDROPBTN,
 
	CARGO_ALL_SELECTED = 0x1FFF,
 
};
 

	
 
typedef int CDECL StationSortListingTypeFunction(const void*, const void*);
 

	
 
static StationSortListingTypeFunction StationNameSorter;
 
static StationSortListingTypeFunction StationTypeSorter;
 
static StationSortListingTypeFunction StationWaitingSorter;
 
static StationSortListingTypeFunction StationRatingMaxSorter;
 

	
 
/** Draw small boxes of cargo amount and ratings data at the given
 
 * coordinates. If amount exceeds 576 units, it is shown 'full', same
 
 * goes for the rating: at above 90% orso (224) it is also 'full'
 
 * Each cargo-bar is 16 pixels wide and 6 pixels high
 
 * Each rating 14 pixels wide and 1 pixel high and is 1 pixel below the cargo-bar
 
 * @param x,y X/Y coordinate to draw the box at
 
 * @param type Cargo type
 
 * @param amount Cargo amount
 
 * @param rating ratings data for that particular cargo */
 
static void StationsWndShowStationRating(int x, int y, CargoID type, uint amount, byte rating)
 
{
 
	int colour = _cargo_colours[type];
 
	uint w = (minu(amount, 576) + 5) / 36;
 

	
 
	/* Draw total cargo (limited) on station (fits into 16 pixels) */
 
	if (w != 0) GfxFillRect(x, y, x + w - 1, y + 6, colour);
 

	
 
	/* Draw a one pixel-wide bar of additional cargo meter, useful
 
	 * for stations with only a small amount (<=30) */
 
	if (w == 0) {
 
		uint rest = amount / 5;
 
		if (rest != 0) {
 
			w += x;
 
			GfxFillRect(w, y + 6 - rest, w, y + 6, colour);
 
		}
 
	}
 

	
 
	DrawString(x + 1, y, _cargoc.names_short[type], 0x10);
 

	
 
	/* Draw green/red ratings bar (fits into 14 pixels) */
 
	y += 8;
 
	GfxFillRect(x + 1, y, x + 14, y, 0xB8);
 
	rating = minu(rating,  224) / 16;
 
	if (rating != 0) GfxFillRect(x + 1, y, x + rating, y, 0xD0);
 
}
 

	
 
const StringID _station_sort_listing[] = {
 
	STR_SORT_BY_DROPDOWN_NAME,
 
	STR_SORT_BY_FACILITY,
 
	STR_SORT_BY_WAITING,
 
	STR_SORT_BY_RATING_MAX,
 
	INVALID_STRING_ID
 
};
 

	
 
static char _bufcache[64];
 
static const Station* _last_station;
 
static int _internal_sort_order;
 

	
 
static int CDECL StationNameSorter(const void *a, const void *b)
 
{
 
	const Station* st1 = *(const Station**)a;
 
	const Station* st2 = *(const Station**)b;
 
	char buf1[64];
 
	int r;
 

	
 
	SetDParam(0, st1->index);
 
	GetString(buf1, STR_STATION, lastof(buf1));
 

	
 
	if (st2 != _last_station) {
 
		_last_station = st2;
 
		SetDParam(0, st2->index);
 
		GetString(_bufcache, STR_STATION, lastof(_bufcache));
 
	}
 

	
 
	r =  strcmp(buf1, _bufcache); // sort by name
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
static int CDECL StationTypeSorter(const void *a, const void *b)
 
{
 
	const Station* st1 = *(const Station**)a;
 
	const Station* st2 = *(const Station**)b;
 
	return (_internal_sort_order & 1) ? st2->facilities - st1->facilities : st1->facilities - st2->facilities;
 
}
 

	
 
static int CDECL StationWaitingSorter(const void *a, const void *b)
 
{
 
	const Station* st1 = *(const Station**)a;
 
	const Station* st2 = *(const Station**)b;
 
	int sum1 = 0, sum2 = 0;
 
	int j;
 

	
 
	for (j = 0; j < NUM_CARGO; j++) {
 
		if (st1->goods[j].waiting_acceptance & 0xfff) sum1 += GetTransportedGoodsIncome(st1->goods[j].waiting_acceptance & 0xfff, 20, 50, j);
 
		if (st2->goods[j].waiting_acceptance & 0xfff) sum2 += GetTransportedGoodsIncome(st2->goods[j].waiting_acceptance & 0xfff, 20, 50, j);
 
	}
 

	
 
	return (_internal_sort_order & 1) ? sum2 - sum1 : sum1 - sum2;
 
}
 

	
 
static int CDECL StationRatingMaxSorter(const void *a, const void *b)
 
{
 
	const Station* st1 = *(const Station**)a;
 
	const Station* st2 = *(const Station**)b;
 
	byte maxr1 = 0;
 
	byte maxr2 = 0;
 
	int j;
 

	
 
	for (j = 0; j < NUM_CARGO; j++) {
 
		if (st1->goods[j].waiting_acceptance & 0xfff) maxr1 = max(maxr1, st1->goods[j].rating);
 
		if (st2->goods[j].waiting_acceptance & 0xfff) maxr2 = max(maxr2, st2->goods[j].rating);
 
	}
 

	
 
	return (_internal_sort_order & 1) ? maxr2 - maxr1 : maxr1 - maxr2;
 
}
 

	
 
typedef enum StationListFlags {
 
	SL_ORDER   = 0x01,
 
	SL_RESORT  = 0x02,
 
	SL_REBUILD = 0x04,
 
} StationListFlags;
 

	
 
typedef struct plstations_d {
 
	const Station** sort_list;
 
	uint16 list_length;
 
	byte sort_type;
 
	StationListFlags flags;
 
	uint16 resort_timer;  //was byte refresh_counter;
 
} plstations_d;
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(plstations_d));
 

	
 
void RebuildStationLists(void)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class == WC_STATION_LIST) {
 
			WP(w, plstations_d).flags |= SL_REBUILD;
 
			SetWindowDirty(w);
 
		}
 
	}
 
}
 

	
 
void ResortStationLists(void)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class == WC_STATION_LIST) {
 
			WP(w, plstations_d).flags |= SL_RESORT;
 
			SetWindowDirty(w);
 
		}
 
	}
 
}
 

	
 
static void BuildStationsList(plstations_d* sl, PlayerID owner, byte facilities, uint16 cargo_filter)
 
{
 
	uint n = 0;
 
	uint i, j;
 
	const Station** station_sort;
 
	const Station *st;
 

	
 
	if (!(sl->flags & SL_REBUILD)) return;
 

	
 
	/* Create array for sorting */
 
	station_sort = malloc((GetMaxStationIndex() + 1) * sizeof(station_sort[0]));
 
	if (station_sort == NULL) error("Could not allocate memory for the station-sorting-list");
 

	
 
	DEBUG(misc, 3, "Building station list for player %d", owner);
 

	
 
	FOR_ALL_STATIONS(st) {
 
		if (st->owner == owner) {
 
			if (facilities & st->facilities) { //only stations with selected facilities
 
				int num_waiting_cargo = 0;
 
				for (j = 0; j < NUM_CARGO; j++) {
 
					if (st->goods[j].waiting_acceptance & 0xFFF) {
 
						num_waiting_cargo++; //count number of waiting cargo
 
						if (HASBIT(cargo_filter, j)) {
 
							station_sort[n++] = st;
 
							break;
 
						}
 
					}
 
				}
 
				//stations without waiting cargo
 
				if (num_waiting_cargo == 0 && HASBIT(cargo_filter, NUM_CARGO)) {
 
					station_sort[n++] = st;
 
				}
 
			}
 
		}
 
	}
 

	
 
	free((void*)sl->sort_list);
 
	sl->sort_list = malloc(n * sizeof(sl->sort_list[0]));
 
	if (n != 0 && sl->sort_list == NULL) error("Could not allocate memory for the station-sorting-list");
 
	sl->list_length = n;
 

	
 
	for (i = 0; i < n; ++i) sl->sort_list[i] = station_sort[i];
 

	
 
	sl->flags &= ~SL_REBUILD;
 
	sl->flags |= SL_RESORT;
 
	free((void*)station_sort);
 
}
 

	
 
static void SortStationsList(plstations_d *sl)
 
{
 
	static StationSortListingTypeFunction* const _station_sorter[] = {
 
		&StationNameSorter,
 
		&StationTypeSorter,
 
		&StationWaitingSorter,
 
		&StationRatingMaxSorter
 
	};
 

	
 
	if (!(sl->flags & SL_RESORT)) return;
 

	
 
	_internal_sort_order = sl->flags & SL_ORDER;
 
	_last_station = NULL; // used for "cache" in namesorting
 
	qsort((void*)sl->sort_list, sl->list_length, sizeof(sl->sort_list[0]), _station_sorter[sl->sort_type]);
 

	
 
	sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
 
	sl->flags &= ~SL_RESORT;
 
}
 

	
 
static void PlayerStationsWndProc(Window *w, WindowEvent *e)
 
{
 
	const PlayerID owner = w->window_number;
 
	static byte facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
 
	static uint16 cargo_filter = CARGO_ALL_SELECTED;
 
	plstations_d *sl = &WP(w, plstations_d);
 

	
 
	switch (e->event) {
 
	case WE_CREATE: { /* set up resort timer */
 
		int i;
 
		for (i = 0; i < 5; i++) {
 
			if (HASBIT(facilities, i)) LowerWindowWidget(w, i + STATIONLIST_WIDGET_TRAIN);
 
		}
 
		for (i = 0; i < NUM_CARGO; i++) {
 
			if (HASBIT(cargo_filter, i)) LowerWindowWidget(w, i + STATIONLIST_WIDGET_CARGOSTART);
 
		}
 
		SetWindowWidgetLoweredState(w, STATIONLIST_WIDGET_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
 
		SetWindowWidgetLoweredState(w, STATIONLIST_WIDGET_CARGOALL, cargo_filter == CARGO_ALL_SELECTED);
 
		SetWindowWidgetLoweredState(w, STATIONLIST_WIDGET_NOCARGOWAITING, HASBIT(cargo_filter, STATIONLIST_WIDGET_NOCARGOWAITING - NUM_CARGO));
 

	
 
		sl->sort_list = NULL;
 
		sl->flags = SL_REBUILD;
 
		sl->sort_type = 0;
 
		sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
 
		break;
 
	}
 

	
 
	case WE_PAINT: {
 
		BuildStationsList(sl, owner, facilities, cargo_filter);
 
		SortStationsList(sl);
 

	
 
		SetVScrollCount(w, sl->list_length);
 

	
 
		/* draw widgets, with player's name in the caption */
 
		{
 
			const Player* p = GetPlayer(owner);
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			SetDParam(2, w->vscroll.count);
 
			DrawWindowWidgets(w);
 
		}
 

	
 
		{
 
			int max;
 
			int i, cg_ofst;
 
			int x = 0, y = 0, xb = 2; // offset from top of widget
 

	
 
			/* draw sorting criteria string */
 
			DrawString(85, 26, _station_sort_listing[sl->sort_type], 0x10);
 
			/* draw arrow pointing up/down for ascending/descending sorting */
 
			DoDrawString(sl->flags & SL_ORDER ? DOWNARROW : UPARROW, 69, 26, 0x10);
 

	
 

	
 
			x = 89;
 
			y = 14;
 

	
 
			for (i = 0; i < NUM_CARGO; i++) {
 
				cg_ofst = IsWindowWidgetLowered(w, i + STATIONLIST_WIDGET_CARGOSTART) ? 2 : 1;
 

	
 
				GfxFillRect(x + cg_ofst, y + cg_ofst, x + cg_ofst + 10 , y + cg_ofst + 7, _cargo_colours[i]);
 
				DrawStringCentered(x + 6 + cg_ofst, y + cg_ofst, _cargoc.names_short[i], 0x10);
 
				x += 14;
 
			}
 

	
 
			x += 6;
 
			cg_ofst = IsWindowWidgetLowered(w, STATIONLIST_WIDGET_NOCARGOWAITING) ? 2 : 1;
 
			DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_NONE, 16);
 
			x += 14;
 
			cg_ofst = IsWindowWidgetLowered(w, STATIONLIST_WIDGET_CARGOALL) ? 2 : 1;
 
			DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, 16);
 

	
 
			cg_ofst = IsWindowWidgetLowered(w, STATIONLIST_WIDGET_FACILALL) ? 2 : 1;
 
			DrawString(71 + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, 16);
 

	
 
			if (w->vscroll.count == 0) { // player has no stations
 
				DrawString(xb, 40, STR_304A_NONE, 0);
 
				return;
 
			}
 

	
 
			max = min(w->vscroll.pos + w->vscroll.cap, sl->list_length);
 
			y = 40; // start of the list-widget
 

	
 
			for (i = w->vscroll.pos; i < max; ++i) { // do until max number of stations of owner
 
				const Station *st = sl->sort_list[i];
 
				CargoID j;
 
				int x;
 

	
 
				assert(st->xy != 0);
 
				assert(st->owner == owner);
 

	
 
				SetDParam(0, st->index);
 
				SetDParam(1, st->facilities);
 
				x = DrawString(xb, y, STR_3049_0, 0) + 5;
 

	
 
				// show cargo waiting and station ratings
 
				for (j = 0; j != NUM_CARGO; j++) {
 
					uint amount = GB(st->goods[j].waiting_acceptance, 0, 12);
 

	
 
					if (amount != 0) {
 
						StationsWndShowStationRating(x, y, j, amount, st->goods[j].rating);
 
						x += 20;
 
					}
 
				}
 
				y += 10;
 
			}
 
		}
 
	} break;
 

	
 
	case WE_CLICK: {
 
		switch (e->we.click.widget) {
 
		case STATIONLIST_WIDGET_LIST: {
 
			const Station* st;
 

	
 
			uint32 id_v = (e->we.click.pt.y - 41) / 10;
 

	
 
			if (id_v >= w->vscroll.cap) return; // click out of bounds
 

	
 
			id_v += w->vscroll.pos;
 

	
 
			if (id_v >= sl->list_length) return; // click out of list bound
 

	
 
			st = sl->sort_list[id_v];
 
			assert(st->owner == owner);
 
			ScrollMainWindowToTile(st->xy);
 
			break;
 
		}
 

	
 
		case STATIONLIST_WIDGET_TRAIN:
 
		case STATIONLIST_WIDGET_TRUCK:
 
		case STATIONLIST_WIDGET_BUS:
 
		case STATIONLIST_WIDGET_AIRPLANE:
 
		case STATIONLIST_WIDGET_SHIP:
 
			if (_ctrl_pressed) {
 
				TOGGLEBIT(facilities, e->we.click.widget - STATIONLIST_WIDGET_TRAIN);
 
				ToggleWidgetLoweredState(w, e->we.click.widget);
 
			} else {
 
				int i;
 
				for (i = 0; facilities != 0; i++, facilities >>= 1) {
 
					if (HASBIT(facilities, 0)) RaiseWindowWidget(w, i + STATIONLIST_WIDGET_TRAIN);
 
				}
 
				SETBIT(facilities, e->we.click.widget - STATIONLIST_WIDGET_TRAIN);
 
				LowerWindowWidget(w, e->we.click.widget);
 
			}
 
			SetWindowWidgetLoweredState(w, STATIONLIST_WIDGET_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
 
			sl->flags |= SL_REBUILD;
 
			SetWindowDirty(w);
 
		break;
 

	
 
		case STATIONLIST_WIDGET_FACILALL: {
 
			int i;
 
			for (i = 0; i < 5; i++) {
 
				LowerWindowWidget(w, i + STATIONLIST_WIDGET_TRAIN);
 
			}
 
			LowerWindowWidget(w, STATIONLIST_WIDGET_FACILALL);
 

	
 
			facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
 
			sl->flags |= SL_REBUILD;
 
			SetWindowDirty(w);
 
			break;
 
		}
 
		case STATIONLIST_WIDGET_CARGOALL: {
 
			int i;
 
			for (i = 0; i < NUM_CARGO; i++) {
 
				LowerWindowWidget(w, i + STATIONLIST_WIDGET_CARGOSTART);
 
			}
 
			LowerWindowWidget(w, STATIONLIST_WIDGET_NOCARGOWAITING);
 
			LowerWindowWidget(w, STATIONLIST_WIDGET_CARGOALL);
 

	
 
			cargo_filter = CARGO_ALL_SELECTED;
 
			sl->flags |= SL_REBUILD;
 
			SetWindowDirty(w);
 
			break;
 
		}
 
		case STATIONLIST_WIDGET_SORTBY: /*flip sorting method asc/desc*/
 
			TOGGLEBIT(sl->flags, 0); //DESC-flag
 
			sl->flags |= SL_RESORT;
 
			w->flags4 |= 5 << WF_TIMEOUT_SHL;
 
			LowerWindowWidget(w, STATIONLIST_WIDGET_SORTBY);
 
			SetWindowDirty(w);
 
		break;
 

	
 
		case STATIONLIST_WIDGET_SORTCRITERIA:
 
		case STATIONLIST_WIDGET_SORTDROPBTN: /* select sorting criteria dropdown menu */
 
			ShowDropDownMenu(w, _station_sort_listing, sl->sort_type, STATIONLIST_WIDGET_SORTDROPBTN, 0, 0);
 
		break;
 

	
 
		default:
 
			if (e->we.click.widget >= STATIONLIST_WIDGET_CARGOSTART && e->we.click.widget <= STATIONLIST_WIDGET_NOCARGOWAITING) { //change cargo_filter
 
				if (_ctrl_pressed) {
 
					TOGGLEBIT(cargo_filter, e->we.click.widget - STATIONLIST_WIDGET_CARGOSTART);
 
					ToggleWidgetLoweredState(w, e->we.click.widget);
 
				} else {
 
					int i;
 
					for (i = 0; cargo_filter != 0; i++, cargo_filter >>= 1) {
 
						if (HASBIT(cargo_filter, 0)) RaiseWindowWidget(w, i + STATIONLIST_WIDGET_CARGOSTART);
 
					}
 
					SETBIT(cargo_filter, e->we.click.widget - STATIONLIST_WIDGET_CARGOSTART);
 
					LowerWindowWidget(w, e->we.click.widget);
 
				}
 
				sl->flags |= SL_REBUILD;
 
				SetWindowWidgetLoweredState(w, STATIONLIST_WIDGET_CARGOALL, cargo_filter == CARGO_ALL_SELECTED);
 
				SetWindowDirty(w);
 
			}
 
		}
 
	} break;
 

	
 
	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
		if (sl->sort_type != e->we.dropdown.index) {
 
			// value has changed -> resort
 
			sl->sort_type = e->we.dropdown.index;
 
			sl->flags |= SL_RESORT;
 
		}
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_TICK:
 
		if (--sl->resort_timer == 0) {
 
			DEBUG(misc, 3, "Periodic rebuild station list player %d", owner);
 
			sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
 
			sl->flags |= VL_REBUILD;
 
			SetWindowDirty(w);
 
		}
 
		break;
 

	
 
	case WE_TIMEOUT:
 
		RaiseWindowWidget(w, STATIONLIST_WIDGET_SORTBY);
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_RESIZE:
 
		w->vscroll.cap += e->we.sizing.diff.y / 10;
 
		break;
 
	}
 
}
 

	
 
static const Widget _player_stations_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   345,     0,    13, STR_3048_STATIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,     RESIZE_LR,    14,   346,   357,     0,    13, 0x0,               STR_STICKY_BUTTON},
 
{      WWT_PANEL,     RESIZE_RB,    14,     0,   345,    37,   161, 0x0,               STR_3057_STATION_NAMES_CLICK_ON},
 
{  WWT_SCROLLBAR,    RESIZE_LRB,    14,   346,   357,    37,   149, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   346,   357,   150,   161, 0x0,               STR_RESIZE_BUTTON},
 
//Index 6
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     0,    13,    14,    24, STR_TRAIN,         STR_USE_CTRL_TO_SELECT_MORE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    14,    27,    14,    24, STR_LORRY,         STR_USE_CTRL_TO_SELECT_MORE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    28,    41,    14,    24, STR_BUS,           STR_USE_CTRL_TO_SELECT_MORE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    42,    55,    14,    24, STR_PLANE,         STR_USE_CTRL_TO_SELECT_MORE},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,    56,    69,    14,    24, STR_SHIP,          STR_USE_CTRL_TO_SELECT_MORE},
 
//Index 11
 
{      WWT_PANEL,   RESIZE_NONE,    14,    83,    88,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    89,   102,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   103,   116,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   117,   130,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   131,   144,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   145,   158,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   159,   172,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   173,   186,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   187,   200,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   201,   214,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   215,   228,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   229,   242,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   243,   256,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   257,   270,    14,    24, 0x0,               STR_NO_WAITING_CARGO},
 
{      WWT_PANEL,  RESIZE_RIGHT,    14,   285,   357,    14,    24, 0x0,               STR_NULL},
 

	
 
//26
 
{      WWT_PANEL,   RESIZE_NONE,    14,    70,    83,    14,    24, 0x0,               STR_SELECT_ALL_FACILITIES},
 
{      WWT_PANEL,   RESIZE_NONE,    14,   271,   284,    14,    24, 0x0,               STR_SELECT_ALL_TYPES},
 

	
 
//28
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,     0,    80,    25,    36, STR_SORT_BY,       STR_SORT_ORDER_TIP},
 
{      WWT_PANEL,   RESIZE_NONE,    14,    81,   232,    25,    36, 0x0,               STR_SORT_CRITERIA_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,    14,   233,   243,    25,    36, STR_0225,          STR_SORT_CRITERIA_TIP},
 
{      WWT_PANEL,  RESIZE_RIGHT,    14,   244,   357,    25,    36, 0x0,               STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _player_stations_desc = {
 
	WDP_AUTO, WDP_AUTO, 358, 162,
 
	WC_STATION_LIST,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_player_stations_widgets,
 
	PlayerStationsWndProc
 
};
 

	
 

	
 
void ShowPlayerStations(PlayerID player)
 
{
 
	Window *w;
 

	
 
	if (!IsValidPlayer(player)) return;
 

	
 
	w = AllocateWindowDescFront(&_player_stations_desc, player);
 
	if (w != NULL) {
 
		w->caption_color = (byte)w->window_number;
 
		w->vscroll.cap = 12;
 
		w->resize.step_height = 10;
 
		w->resize.height = w->height - 10 * 7; // minimum if 5 in the list
 
	}
 
}
 

	
 
static const Widget _station_view_expanded_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   236,     0,    13, STR_300A_0,        STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    14,   237,   248,     0,    13, 0x0,               STR_STICKY_BUTTON},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   236,    14,    65, 0x0,               STR_NULL},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,    14,   237,   248,    14,    65, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,               STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   248,    66,   197, 0x0,               STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    63,   198,   209, STR_00E4_LOCATION, STR_3053_CENTER_MAIN_VIEW_ON_STATION},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,    64,   128,   198,   209, STR_3033_ACCEPTS,  STR_3056_SHOW_LIST_OF_ACCEPTED_CARGO},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   129,   192,   198,   209, STR_0130_RENAME,   STR_3055_CHANGE_NAME_OF_STATION},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   193,   206,   198,   209, STR_TRAIN,         STR_SCHEDULED_TRAINS_TIP },
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   207,   220,   198,   209, STR_LORRY,         STR_SCHEDULED_ROAD_VEHICLES_TIP },
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   221,   234,   198,   209, STR_PLANE,         STR_SCHEDULED_AIRCRAFT_TIP },
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   235,   248,   198,   209, STR_SHIP,          STR_SCHEDULED_SHIPS_TIP },
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _station_view_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   236,     0,    13, STR_300A_0,        STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    14,   237,   248,     0,    13, 0x0,               STR_STICKY_BUTTON},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   236,    14,    65, 0x0,               STR_NULL},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,    14,   237,   248,    14,    65, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   248,    66,    97, 0x0,               STR_NULL},
 
{      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,               STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    63,    98,   109, STR_00E4_LOCATION, STR_3053_CENTER_MAIN_VIEW_ON_STATION},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,    64,   128,    98,   109, STR_3032_RATINGS,  STR_3054_SHOW_STATION_RATINGS},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   129,   192,    98,   109, STR_0130_RENAME,   STR_3055_CHANGE_NAME_OF_STATION},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   193,   206,    98,   109, STR_TRAIN,         STR_SCHEDULED_TRAINS_TIP },
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   207,   220,    98,   109, STR_LORRY,         STR_SCHEDULED_ROAD_VEHICLES_TIP },
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   221,   234,    98,   109, STR_PLANE,         STR_SCHEDULED_AIRCRAFT_TIP },
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   235,   248,    98,   109, STR_SHIP,          STR_SCHEDULED_SHIPS_TIP },
 
{   WIDGETS_END},
 
};
 

	
 
static void DrawStationViewWindow(Window *w)
 
{
 
	StationID station_id = w->window_number;
 
	const Station* st = GetStation(station_id);
 
	uint i;
 
	uint num;
 
	int x,y;
 
	int pos;
 
	StringID str;
 

	
 
	num = 1;
 
	for (i = 0; i != NUM_CARGO; i++) {
 
		if (GB(st->goods[i].waiting_acceptance, 0, 12) != 0) {
 
			num++;
 
			if (st->goods[i].enroute_from != station_id) num++;
 
		}
 
	}
 
	SetVScrollCount(w, num);
 

	
 
	SetWindowWidgetDisabledState(w,  9, st->owner != _local_player);
 
	SetWindowWidgetDisabledState(w, 10, !(st->facilities & FACIL_TRAIN));
 
	SetWindowWidgetDisabledState(w, 11, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
 
	SetWindowWidgetDisabledState(w, 12, !(st->facilities & FACIL_AIRPORT));
 
	SetWindowWidgetDisabledState(w, 13, !(st->facilities & FACIL_DOCK));
 

	
 
	SetDParam(0, st->index);
 
	SetDParam(1, st->facilities);
 
	DrawWindowWidgets(w);
 

	
 
	x = 2;
 
	y = 15;
 
	pos = w->vscroll.pos;
 

	
 
	if (--pos < 0) {
 
		str = STR_00D0_NOTHING;
 
		for (i = 0; i != NUM_CARGO; i++) {
 
			if (GB(st->goods[i].waiting_acceptance, 0, 12) != 0) str = STR_EMPTY;
 
		}
 
		SetDParam(0, str);
 
		DrawString(x, y, STR_0008_WAITING, 0);
 
		y += 10;
 
	}
 

	
 
	i = 0;
 
	do {
 
		uint waiting = GB(st->goods[i].waiting_acceptance, 0, 12);
 
		if (waiting == 0) continue;
 

	
 
		num = (waiting + 5) / 10;
 
		if (num != 0) {
 
			int cur_x = x;
 
			num = min(num, 23);
 
			do {
 
				DrawSprite(_cargoc.sprites[i], cur_x, y);
 
				cur_x += 10;
 
			} while (--num);
 
		}
 

	
 
		if ( st->goods[i].enroute_from == station_id) {
 
			if (--pos < 0) {
 
				SetDParam(1, waiting);
 
				SetDParam(0, i);
 
				DrawStringRightAligned(x + 234, y, STR_0009, 0);
 
				y += 10;
 
			}
 
		} else {
 
			/* enroute */
 
			if (--pos < 0) {
 
				SetDParam(1, waiting);
 
				SetDParam(0, i);
 
				DrawStringRightAligned(x + 234, y, STR_000A_EN_ROUTE_FROM, 0);
 
				y += 10;
 
			}
 

	
 
			if (pos > -5 && --pos < 0) {
 
				SetDParam(0, st->goods[i].enroute_from);
 
				DrawStringRightAligned(x + 234, y, STR_000B, 0);
 
				y += 10;
 
			}
 
		}
 
	} while (pos > -5 && ++i != NUM_CARGO);
 

	
 
	if (IsWindowOfPrototype(w, _station_view_widgets)) {
 
		char *b = _userstring;
 
		bool first = true;
 

	
 
		b = InlineString(b, STR_000C_ACCEPTS);
 

	
 
		for (i = 0; i != NUM_CARGO; i++) {
 
			if (b >= endof(_userstring) - 5 - 1) break;
 
			if (st->goods[i].waiting_acceptance & 0x8000) {
 
				if (first) {
 
					first = false;
 
				} else {
 
					/* Add a comma if this is not the first item */
 
					*b++ = ',';
 
					*b++ = ' ';
 
				}
 
				b = InlineString(b, _cargoc.names_s[i]);
 
			}
 
		}
 

	
 
		/* If first is still true then no cargo is accepted */
 
		if (first) b = InlineString(b, STR_00D0_NOTHING);
 

	
 
		*b = '\0';
 
		DrawStringMultiLine(2, 67, STR_SPEC_USERSTRING, 245);
 
	} else {
 
		DrawString(2, 67, STR_3034_LOCAL_RATING_OF_TRANSPORT, 0);
 

	
 
		y = 77;
 
		for (i = 0; i != NUM_CARGO; i++) {
 
			if (st->goods[i].enroute_from != INVALID_STATION) {
 
				SetDParam(0, _cargoc.names_s[i]);
 
				SetDParam(2, st->goods[i].rating * 101 >> 8);
 
				SetDParam(1, STR_3035_APPALLING + (st->goods[i].rating >> 5));
 
				DrawString(8, y, STR_303D, 0);
 
				y += 10;
 
			}
 
		}
 
	}
 
}
 

	
 

	
 
static void StationViewWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		DrawStationViewWindow(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 7:
 
			ScrollMainWindowToTile(GetStation(w->window_number)->xy);
 
			break;
 

	
 
		case 8:
 
			SetWindowDirty(w);
 

	
 
			/* toggle height/widget set */
 
			if (IsWindowOfPrototype(w, _station_view_expanded_widgets)) {
 
				AssignWidgetToWindow(w, _station_view_widgets);
 
				w->height = 110;
 
			} else {
 
				AssignWidgetToWindow(w, _station_view_expanded_widgets);
 
				w->height = 210;
 
			}
 

	
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 9: {
 
			SetDParam(0, w->window_number);
 
			ShowQueryString(STR_STATION, STR_3030_RENAME_STATION_LOADING, 31, 180, w, CS_ALPHANUMERAL);
 
		} break;
 

	
 
		case 10: { /* Show a list of scheduled trains to this station */
 
			const Station *st = GetStation(w->window_number);
 
			ShowVehicleListWindow(st->owner, w->window_number, VEH_Train);
 
			break;
 
		}
 

	
 
		case 11: { /* Show a list of scheduled road-vehicles to this station */
 
			const Station *st = GetStation(w->window_number);
 
			ShowVehicleListWindow(st->owner, w->window_number, VEH_Road);
 
			break;
 
		}
 

	
 
		case 12: { /* Show a list of scheduled aircraft to this station */
 
			const Station *st = GetStation(w->window_number);
 
			/* Since oilrigs have no owners, show the scheduled aircraft of current player */
 
			PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
 
			ShowVehicleListWindow(owner, w->window_number, VEH_Aircraft);
 
			break;
 
		}
 

	
 
		case 13: { /* Show a list of scheduled ships to this station */
 
			const Station *st = GetStation(w->window_number);
 
			/* Since oilrigs/bouys have no owners, show the scheduled ships of current player */
 
			PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
 
			ShowVehicleListWindow(owner, w->window_number, VEH_Ship);
 
			break;
 
		}
 
		}
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT:
 
		if (e->we.edittext.str[0] != '\0') {
 
			_cmd_text = e->we.edittext.str;
 
			DoCommandP(0, w->window_number, 0, NULL,
 
				CMD_RENAME_STATION | CMD_MSG(STR_3031_CAN_T_RENAME_STATION));
 
		}
 
		break;
 

	
 
	case WE_DESTROY: {
 
		WindowNumber wno =
 
			(w->window_number << 16) | GetStation(w->window_number)->owner;
 

	
 
		DeleteWindowById(WC_TRAINS_LIST, wno);
 
		DeleteWindowById(WC_ROADVEH_LIST, wno);
 
		DeleteWindowById(WC_SHIPS_LIST, wno);
 
		DeleteWindowById(WC_AIRCRAFT_LIST, wno);
 
		break;
 
	}
 
	}
 
}
 

	
 

	
 
static const WindowDesc _station_view_desc = {
 
	WDP_AUTO, WDP_AUTO, 249, 110,
 
	WC_STATION_VIEW,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
 
	_station_view_widgets,
 
	StationViewWndProc
 
};
 

	
 
void ShowStationViewWindow(StationID station)
 
{
 
	Window *w;
 

	
 
	w = AllocateWindowDescFront(&_station_view_desc, station);
 
	if (w != NULL) {
 
		PlayerID owner = GetStation(w->window_number)->owner;
 
		if (owner != OWNER_NONE) w->caption_color = owner;
 
		w->vscroll.cap = 5;
 
	}
 
}
src/station_map.c
Show inline comments
 
deleted file
src/station_map.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "station_map.h"
 

	
 

	
 
StationType GetStationType(TileIndex t)
 
{
 
	assert(IsTileType(t, MP_STATION));
 
	if (IsRailwayStation(t)) return STATION_RAIL;
 
	if (IsAirport(t)) return STATION_AIRPORT;
 
	if (IsTruckStop(t)) return STATION_TRUCK;
 
	if (IsBusStop(t)) return STATION_BUS;
 
	if (IsOilRig(t)) return STATION_OILRIG;
 
	if (IsDock(t)) return STATION_DOCK;
 
	assert(IsBuoy_(t));
 
	return STATION_BUOY;
 
}
src/strgen/strgen.c
Show inline comments
 
deleted file
src/strgen/strgen.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 
#include "../macros.h"
 
#include "../string.h"
 
#include "../table/control_codes.h"
 
#include <stdio.h>
 
#include <string.h>
 
#include <stdlib.h>
 
#include <stdarg.h>
 

	
 
#if (!defined(WIN32) && !defined(WIN64)) || defined(__CYGWIN__)
 
#include <unistd.h>
 
#endif
 

	
 
#if defined WIN32 || defined __WATCOMC__
 
#include <direct.h>
 
#endif /* WIN32 || __WATCOMC__ */
 

	
 
#ifdef __MORPHOS__
 
#ifdef stderr
 
#undef stderr
 
#endif
 
#define stderr stdout
 
#endif /* __MORPHOS__ */
 

	
 
#ifdef __WATCOMC__
 
	uint _map_log_x;     // an unpleasant hack required because Watcom is insisting on
 
	uint _map_size_x;    // these variables being valid references in map.h
 
	uint _map_size_y;
 
	uint _map_tile_mask;
 
	uint _map_size;
 
#endif /* __WATCOMC__ */
 

	
 
/* Compiles a list of strings into a compiled string list */
 

	
 
typedef void (*ParseCmdProc)(char *buf, int value);
 

	
 
typedef struct LanguagePackHeader {
 
	uint32 ident;
 
	uint32 version;     // 32-bits of auto generated version info which is basically a hash of strings.h
 
	char name[32];      // the international name of this language
 
	char own_name[32];  // the localized name of this language
 
	char isocode[16];   // the ISO code for the language (not country code)
 
	uint16 offsets[32]; // the offsets
 
	byte plural_form;   // plural form index
 
	byte pad[3];        // pad header to be a multiple of 4
 
} LanguagePackHeader;
 

	
 
typedef struct CmdStruct {
 
	const char *cmd;
 
	ParseCmdProc proc;
 
	long value;
 
	int8 consumes;
 
	byte flags;
 
} CmdStruct;
 

	
 
enum {
 
	C_DONTCOUNT = 1,
 
	C_CASE      = 2,
 
};
 

	
 

	
 
typedef struct Case {
 
	int caseidx;
 
	char *string;
 
	struct Case *next;
 
} Case;
 

	
 
static bool _masterlang;
 
static bool _translated;
 
static const char* _file = "(unknown file)";
 
static int _cur_line;
 
static int _errors, _warnings;
 

	
 
typedef struct LangString {
 
	char *name;            // Name of the string
 
	char *english;         // English text
 
	char *translated;      // Translated text
 
	uint16 hash_next;      // next hash entry
 
	uint16 index;
 
	int line;              // line of string in source-file
 
	Case *english_case;    // cases for english
 
	Case *translated_case; // cases for foreign
 
} LangString;
 

	
 
static LangString *_strings[65536];
 

	
 

	
 
#define HASH_SIZE 32767
 
static uint16 _hash_head[HASH_SIZE];
 

	
 
static byte _put_buf[4096];
 
static int _put_pos;
 
static int _next_string_id;
 

	
 
static uint32 _hash;
 
static char _lang_name[32], _lang_ownname[32], _lang_isocode[16];
 
static byte _lang_pluralform;
 
#define MAX_NUM_GENDER 8
 
static char _genders[MAX_NUM_GENDER][8];
 
static int _numgenders;
 

	
 
// contains the name of all cases.
 
#define MAX_NUM_CASES 50
 
static char _cases[MAX_NUM_CASES][16];
 
static int _numcases;
 

	
 
// for each plural value, this is the number of plural forms.
 
static const byte _plural_form_counts[] = { 2, 1, 2, 3, 3, 3, 3, 3, 4 };
 

	
 
static const char *_cur_ident;
 

	
 
typedef struct CmdPair {
 
	const CmdStruct *a;
 
	char *v;
 
} CmdPair;
 

	
 
typedef struct ParsedCommandStruct {
 
	int np;
 
	CmdPair pairs[32];
 
	const CmdStruct *cmd[32]; // ordered by param #
 
} ParsedCommandStruct;
 

	
 
// Used when generating some advanced commands.
 
static ParsedCommandStruct _cur_pcs;
 
static int _cur_argidx;
 

	
 
static uint HashStr(const char *s)
 
{
 
	uint hash = 0;
 
	for (; *s != '\0'; s++) hash = ROL(hash, 3) ^ *s;
 
	return hash % HASH_SIZE;
 
}
 

	
 
static void HashAdd(const char *s, LangString *ls)
 
{
 
	uint hash = HashStr(s);
 
	ls->hash_next = _hash_head[hash];
 
	_hash_head[hash] = ls->index + 1;
 
}
 

	
 
static LangString *HashFind(const char *s)
 
{
 
	int idx = _hash_head[HashStr(s)];
 

	
 
	while (--idx >= 0) {
 
		LangString* ls = _strings[idx];
 

	
 
		if (strcmp(ls->name, s) == 0) return ls;
 
		idx = ls->hash_next;
 
	}
 
	return NULL;
 
}
 

	
 
#ifdef _MSC_VER
 
# define LINE_NUM_FMT "(%d)"
 
#else
 
# define LINE_NUM_FMT ":%d"
 
#endif
 

	
 
static void CDECL warning(const char *s, ...)
 
{
 
	char buf[1024];
 
	va_list va;
 
	va_start(va, s);
 
	vsnprintf(buf, lengthof(buf), s, va);
 
	va_end(va);
 
	fprintf(stderr, "%s" LINE_NUM_FMT ": warning: %s\n", _file, _cur_line, buf);
 
	_warnings++;
 
}
 

	
 
void CDECL error(const char *s, ...)
 
{
 
	char buf[1024];
 
	va_list va;
 
	va_start(va, s);
 
	vsnprintf(buf, lengthof(buf), s, va);
 
	va_end(va);
 
	fprintf(stderr, "%s" LINE_NUM_FMT ": error: %s\n", _file, _cur_line, buf);
 
	_errors++;
 
}
 

	
 

	
 
static void NORETURN CDECL fatal(const char *s, ...)
 
{
 
	char buf[1024];
 
	va_list va;
 
	va_start(va, s);
 
	vsnprintf(buf, lengthof(buf), s, va);
 
	va_end(va);
 
	fprintf(stderr, "%s" LINE_NUM_FMT ": FATAL: %s\n", _file, _cur_line, buf);
 
	exit(1);
 
}
 

	
 
static void PutByte(byte c)
 
{
 
	if (_put_pos == lengthof(_put_buf)) fatal("Put buffer too small");
 
	_put_buf[_put_pos++] = c;
 
}
 

	
 

	
 
static void PutUtf8(uint32 value)
 
{
 
	if (value < 0x80) {
 
		PutByte(value);
 
	} else if (value < 0x800) {
 
		PutByte(0xC0 + GB(value,  6, 5));
 
		PutByte(0x80 + GB(value,  0, 6));
 
	} else if (value < 0x10000) {
 
		PutByte(0xE0 + GB(value, 12, 4));
 
		PutByte(0x80 + GB(value,  6, 6));
 
		PutByte(0x80 + GB(value,  0, 6));
 
	} else if (value < 0x110000) {
 
		PutByte(0xF0 + GB(value, 18, 3));
 
		PutByte(0x80 + GB(value, 12, 6));
 
		PutByte(0x80 + GB(value,  6, 6));
 
		PutByte(0x80 + GB(value,  0, 6));
 
	} else {
 
		warning("Invalid unicode value U+0x%X", value);
 
	}
 
}
 

	
 

	
 
size_t Utf8Validate(const char *s)
 
{
 
	uint32 c;
 

	
 
	if (!HASBIT(s[0], 7)) {
 
		/* 1 byte */
 
		return 1;
 
	} else if (GB(s[0], 5, 3) == 6 && IsUtf8Part(s[1])) {
 
		/* 2 bytes */
 
		c = GB(s[0], 0, 5) << 6 | GB(s[1], 0, 6);
 
		if (c >= 0x80) return 2;
 
	} else if (GB(s[0], 4, 4) == 14 && IsUtf8Part(s[1]) && IsUtf8Part(s[2])) {
 
		/* 3 bytes */
 
		c = GB(s[0], 0, 4) << 12 | GB(s[1], 0, 6) << 6 | GB(s[2], 0, 6);
 
		if (c >= 0x800) return 3;
 
	} else if (GB(s[0], 3, 5) == 30 && IsUtf8Part(s[1]) && IsUtf8Part(s[2]) && IsUtf8Part(s[3])) {
 
		/* 4 bytes */
 
		c = GB(s[0], 0, 3) << 18 | GB(s[1], 0, 6) << 12 | GB(s[2], 0, 6) << 6 | GB(s[3], 0, 6);
 
		if (c >= 0x10000 && c <= 0x10FFFF) return 4;
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
static void EmitSingleChar(char *buf, int value)
 
{
 
	if (*buf != '\0') warning("Ignoring trailing letters in command");
 
	PutUtf8(value);
 
}
 

	
 

	
 
static void EmitSetX(char *buf, int value)
 
{
 
	char *err;
 
	int x = strtol(buf, &err, 0);
 
	if (*err != 0) fatal("SetX param invalid");
 
	PutUtf8(SCC_SETX);
 
	PutByte((byte)x);
 
}
 

	
 

	
 
static void EmitSetXY(char *buf, int value)
 
{
 
	char *err;
 
	int x;
 
	int y;
 

	
 
	x = strtol(buf, &err, 0);
 
	if (*err != ' ') fatal("SetXY param invalid");
 
	y = strtol(err + 1, &err, 0);
 
	if (*err != 0) fatal("SetXY param invalid");
 

	
 
	PutUtf8(SCC_SETXY);
 
	PutByte((byte)x);
 
	PutByte((byte)y);
 
}
 

	
 
// The plural specifier looks like
 
// {NUM} {PLURAL -1 passenger passengers} then it picks either passenger/passengers depending on the count in NUM
 

	
 
// This is encoded like
 
//  CommandByte <ARG#> <NUM> {Length of each string} {each string}
 

	
 
bool ParseRelNum(char **buf, int *value)
 
{
 
	const char* s = *buf;
 
	char* end;
 
	bool rel = false;
 
	int v;
 

	
 
	while (*s == ' ' || *s == '\t') s++;
 
	if (*s == '+') {
 
		rel = true;
 
		s++;
 
	}
 
	v = strtol(s, &end, 0);
 
	if (end == s) return false;
 
	if (rel || v < 0) {
 
		*value += v;
 
	} else {
 
		*value = v;
 
	}
 
	*buf = end;
 
	return true;
 
}
 

	
 
// Parse out the next word, or NULL
 
char *ParseWord(char **buf)
 
{
 
	char *s = *buf, *r;
 

	
 
	while (*s == ' ' || *s == '\t') s++;
 
	if (*s == '\0') return NULL;
 

	
 
	if (*s == '"') {
 
		r = ++s;
 
		// parse until next " or NUL
 
		for (;;) {
 
			if (*s == '\0') break;
 
			if (*s == '"') {
 
				*s++ = '\0';
 
				break;
 
			}
 
			s++;
 
		}
 
	} else {
 
		// proceed until whitespace or NUL
 
		r = s;
 
		for (;;) {
 
			if (*s == '\0') break;
 
			if (*s == ' ' || *s == '\t') {
 
				*s++ = '\0';
 
				break;
 
			}
 
			s++;
 
		}
 
	}
 
	*buf = s;
 
	return r;
 
}
 

	
 
// Forward declaration
 
static int TranslateArgumentIdx(int arg);
 

	
 
static void EmitWordList(const char* const* words, uint nw)
 
{
 
	uint i;
 
	uint j;
 

	
 
	PutByte(nw);
 
	for (i = 0; i < nw; i++) PutByte(strlen(words[i]));
 
	for (i = 0; i < nw; i++) {
 
		for (j = 0; words[i][j] != '\0'; j++) PutByte(words[i][j]);
 
	}
 
}
 

	
 
static void EmitPlural(char *buf, int value)
 
{
 
	int argidx = _cur_argidx;
 
	const char* words[5];
 
	int nw = 0;
 

	
 
	// Parse out the number, if one exists. Otherwise default to prev arg.
 
	if (!ParseRelNum(&buf, &argidx)) argidx--;
 

	
 
	// Parse each string
 
	for (nw = 0; nw < 5; nw++) {
 
		words[nw] = ParseWord(&buf);
 
		if (words[nw] == NULL) break;
 
	}
 

	
 
	if (nw == 0)
 
		fatal("%s: No plural words", _cur_ident);
 

	
 
	if (_plural_form_counts[_lang_pluralform] != nw) {
 
		if (_translated) {
 
			fatal("%s: Invalid number of plural forms. Expecting %d, found %d.", _cur_ident,
 
				_plural_form_counts[_lang_pluralform], nw);
 
		} else {
 
			warning("'%s' is untranslated. Tweaking english string to allow compilation for plural forms", _cur_ident);
 
			if (nw > _plural_form_counts[_lang_pluralform]) {
 
				nw = _plural_form_counts[_lang_pluralform];
 
			} else {
 
				for (; nw < _plural_form_counts[_lang_pluralform]; nw++) {
 
					words[nw] = words[nw - 1];
 
				}
 
			}
 
		}
 
	}
 

	
 
	PutUtf8(SCC_PLURAL_LIST);
 
	PutByte(TranslateArgumentIdx(argidx));
 
	EmitWordList(words, nw);
 
}
 

	
 

	
 
static void EmitGender(char *buf, int value)
 
{
 
	int argidx = _cur_argidx;
 
	uint nw;
 

	
 
	if (buf[0] == '=') {
 
		buf++;
 

	
 
		// This is a {G=DER} command
 
		for (nw = 0; ; nw++) {
 
			if (nw >= 8) fatal("G argument '%s' invalid", buf);
 
			if (strcmp(buf, _genders[nw]) == 0) break;
 
		}
 
		// now nw contains the gender index
 
		PutUtf8(SCC_GENDER_INDEX);
 
		PutByte(nw);
 
	} else {
 
		const char* words[8];
 

	
 
		// This is a {G 0 foo bar two} command.
 
		// If no relative number exists, default to +0
 
		if (!ParseRelNum(&buf, &argidx)) {}
 

	
 
		for (nw = 0; nw < 8; nw++) {
 
			words[nw] = ParseWord(&buf);
 
			if (words[nw] == NULL) break;
 
		}
 
		if (nw != _numgenders) fatal("Bad # of arguments for gender command");
 
		PutUtf8(SCC_GENDER_LIST);
 
		PutByte(TranslateArgumentIdx(argidx));
 
		EmitWordList(words, nw);
 
	}
 
}
 

	
 

	
 
static const CmdStruct _cmd_structs[] = {
 
	// Update position
 
	{"SETX",  EmitSetX,  SCC_SETX,  0, 0},
 
	{"SETXY", EmitSetXY, SCC_SETXY, 0, 0},
 

	
 
	// Font size
 
	{"TINYFONT", EmitSingleChar, SCC_TINYFONT, 0, 0},
 
	{"BIGFONT",  EmitSingleChar, SCC_BIGFONT,  0, 0},
 

	
 
	// Colors
 
	{"BLUE",    EmitSingleChar, SCC_BLUE,    0, 0},
 
	{"SILVER",  EmitSingleChar, SCC_SILVER,  0, 0},
 
	{"GOLD",    EmitSingleChar, SCC_GOLD,    0, 0},
 
	{"RED",     EmitSingleChar, SCC_RED,     0, 0},
 
	{"PURPLE",  EmitSingleChar, SCC_PURPLE,  0, 0},
 
	{"LTBROWN", EmitSingleChar, SCC_LTBROWN, 0, 0},
 
	{"ORANGE",  EmitSingleChar, SCC_ORANGE,  0, 0},
 
	{"GREEN",   EmitSingleChar, SCC_GREEN,   0, 0},
 
	{"YELLOW",  EmitSingleChar, SCC_YELLOW,  0, 0},
 
	{"DKGREEN", EmitSingleChar, SCC_DKGREEN, 0, 0},
 
	{"CREAM",   EmitSingleChar, SCC_CREAM,   0, 0},
 
	{"BROWN",   EmitSingleChar, SCC_BROWN,   0, 0},
 
	{"WHITE",   EmitSingleChar, SCC_WHITE,   0, 0},
 
	{"LTBLUE",  EmitSingleChar, SCC_LTBLUE,  0, 0},
 
	{"GRAY",    EmitSingleChar, SCC_GRAY,    0, 0},
 
	{"DKBLUE",  EmitSingleChar, SCC_DKBLUE,  0, 0},
 
	{"BLACK",   EmitSingleChar, SCC_BLACK,   0, 0},
 

	
 
	{"CURRCOMPACT",   EmitSingleChar, SCC_CURRENCY_COMPACT,    1, 0}, // compact currency (32 bits)
 
	{"REV",           EmitSingleChar, SCC_REVISION,            0, 0}, // openttd revision string
 
	{"SHORTCARGO",    EmitSingleChar, SCC_CARGO_SHORT,         2, 0}, // short cargo description, only ### tons, or ### litres
 
	{"CURRCOMPACT64", EmitSingleChar, SCC_CURRENCY_COMPACT_64, 2, 0}, // compact currency 64 bits
 

	
 
	// These are special versions of {STRING1}
 
	// The first string includes the second string.
 
	{"COMPANY",    EmitSingleChar, SCC_STRING1, 1, 0},
 
	{"PLAYERNAME", EmitSingleChar, SCC_STRING1, 1, 0},
 
	{"VEHICLE",    EmitSingleChar, SCC_STRING1, 1, 0},
 

	
 
	{"STRING1", EmitSingleChar, SCC_STRING1, 1, C_CASE}, // included string that consumes ONE argument
 
	{"STRING2", EmitSingleChar, SCC_STRING2, 2, C_CASE}, // included string that consumes TWO arguments
 
	{"STRING3", EmitSingleChar, SCC_STRING3, 3, C_CASE}, // included string that consumes THREE arguments
 
	{"STRING4", EmitSingleChar, SCC_STRING4, 4, C_CASE}, // included string that consumes FOUR arguments
 
	{"STRING5", EmitSingleChar, SCC_STRING5, 5, C_CASE}, // included string that consumes FIVE arguments
 

	
 
	{"STATIONFEATURES", EmitSingleChar, SCC_STATION_FEATURES, 1, 0}, // station features string, icons of the features
 
	{"INDUSTRY",        EmitSingleChar, SCC_INDUSTRY_NAME,    1, 0}, // industry, takes an industry #
 
	{"CARGO",           EmitSingleChar, SCC_CARGO,            2, 0},
 
	{"POWER",           EmitSingleChar, SCC_POWER,            1, 0},
 
	{"VOLUME",          EmitSingleChar, SCC_VOLUME,           1, 0},
 
	{"VOLUME_S",        EmitSingleChar, SCC_VOLUME_SHORT,     1, 0},
 
	{"WEIGHT",          EmitSingleChar, SCC_WEIGHT,           1, 0},
 
	{"WEIGHT_S",        EmitSingleChar, SCC_WEIGHT_SHORT,     1, 0},
 
	{"FORCE",           EmitSingleChar, SCC_FORCE,            1, 0},
 
	{"VELOCITY",        EmitSingleChar, SCC_VELOCITY,         1, 0},
 

	
 
	{"P", EmitPlural, 0, 0, C_DONTCOUNT}, // plural specifier
 
	{"G", EmitGender, 0, 0, C_DONTCOUNT}, // gender specifier
 

	
 
	{"DATE_TINY",  EmitSingleChar, SCC_DATE_TINY, 1, 0},
 
	{"DATE_SHORT", EmitSingleChar, SCC_DATE_SHORT, 1, 0},
 
	{"DATE_LONG",  EmitSingleChar, SCC_DATE_LONG, 1, 0},
 

	
 
	{"SKIP", EmitSingleChar, SCC_SKIP, 1, 0},
 

	
 
	{"STRING", EmitSingleChar, SCC_STRING, 1, C_CASE},
 

	
 
	// Numbers
 
	{"COMMA", EmitSingleChar, SCC_COMMA, 1, 0}, // Number with comma
 
	{"NUM",   EmitSingleChar, SCC_NUM,   1, 0}, // Signed number
 

	
 
	{"CURRENCY",   EmitSingleChar, SCC_CURRENCY,    1, 0},
 
	{"CURRENCY64", EmitSingleChar, SCC_CURRENCY_64, 2, 0},
 

	
 
	{"WAYPOINT", EmitSingleChar, SCC_WAYPOINT_NAME, 1, 0}, // waypoint name
 
	{"STATION",  EmitSingleChar, SCC_STATION_NAME,  1, 0},
 
	{"TOWN",     EmitSingleChar, SCC_TOWN_NAME,     1, 0},
 

	
 
	// 0x9D is used for the pseudo command SETCASE
 
	// 0x9E is used for case switching
 

	
 
	{"",               EmitSingleChar, '\n',               0, C_DONTCOUNT},
 
	{"{",              EmitSingleChar, '{',                0, C_DONTCOUNT},
 
	{"UPARROW",        EmitSingleChar, SCC_UPARROW,        0, 0},
 
	{"SMALLUPARROW",   EmitSingleChar, SCC_SMALLUPARROW,   0, 0},
 
	{"SMALLDOWNARROW", EmitSingleChar, SCC_SMALLDOWNARROW, 0, 0},
 
	{"TRAIN",          EmitSingleChar, SCC_TRAIN,          0, 0},
 
	{"LORRY",          EmitSingleChar, SCC_LORRY,          0, 0},
 
	{"BUS",            EmitSingleChar, SCC_BUS,            0, 0},
 
	{"PLANE",          EmitSingleChar, SCC_PLANE,          0, 0},
 
	{"SHIP",           EmitSingleChar, SCC_SHIP,           0, 0},
 
	{"NBSP",           EmitSingleChar, 0xA0,               0, C_DONTCOUNT},
 
	{"CENT",           EmitSingleChar, 0xA2,               0, C_DONTCOUNT},
 
	{"POUNDSIGN",      EmitSingleChar, 0xA3,               0, C_DONTCOUNT},
 
	{"EURO",           EmitSingleChar, 0x20AC,             0, C_DONTCOUNT},
 
	{"YENSIGN",        EmitSingleChar, 0xA5,               0, C_DONTCOUNT},
 
	{"COPYRIGHT",      EmitSingleChar, 0xA9,               0, C_DONTCOUNT},
 
	{"DOWNARROW",      EmitSingleChar, SCC_DOWNARROW,      0, C_DONTCOUNT},
 
	{"CHECKMARK",      EmitSingleChar, SCC_CHECKMARK,      0, C_DONTCOUNT},
 
	{"CROSS",          EmitSingleChar, SCC_CROSS,          0, C_DONTCOUNT},
 
	{"REGISTERED",     EmitSingleChar, 0xAE,               0, C_DONTCOUNT},
 
	{"RIGHTARROW",     EmitSingleChar, SCC_RIGHTARROW,     0, C_DONTCOUNT},
 
	{"SMALLLEFTARROW", EmitSingleChar, SCC_LESSTHAN,       0, C_DONTCOUNT},
 
	{"SMALLRIGHTARROW",EmitSingleChar, SCC_GREATERTHAN,    0, C_DONTCOUNT},
 
};
 

	
 

	
 
static const CmdStruct *FindCmd(const char *s, int len)
 
{
 
	const CmdStruct* cs;
 

	
 
	for (cs = _cmd_structs; cs != endof(_cmd_structs); cs++) {
 
		if (strncmp(cs->cmd, s, len) == 0 && cs->cmd[len] == '\0') return cs;
 
	}
 
	return NULL;
 
}
 

	
 
static uint ResolveCaseName(const char *str, uint len)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < MAX_NUM_CASES; i++) {
 
		if (memcmp(_cases[i], str, len) == 0 && _cases[i][len] == 0) return i + 1;
 
	}
 
	fatal("Invalid case-name '%s'", str);
 
}
 

	
 

	
 
// returns NULL on eof
 
// else returns command struct
 
static const CmdStruct *ParseCommandString(const char **str, char *param, int *argno, int *casei)
 
{
 
	const char *s = *str, *start;
 
	const CmdStruct *cmd;
 
	byte c;
 

	
 
	*argno = -1;
 
	*casei = -1;
 

	
 
	// Scan to the next command, exit if there's no next command.
 
	for (; *s != '{'; s++) {
 
		if (*s == '\0') return NULL;
 
	}
 
	s++; // Skip past the {
 

	
 
	if (*s >= '0' && *s <= '9') {
 
		char *end;
 

	
 
		*argno = strtoul(s, &end, 0);
 
		if (*end != ':') fatal("missing arg #");
 
		s = end + 1;
 
	}
 

	
 
	// parse command name
 
	start = s;
 
	do {
 
		c = *s++;
 
	} while (c != '}' && c != ' ' && c != '=' && c != '.' && c != 0);
 

	
 
	cmd = FindCmd(start, s - start - 1);
 
	if (cmd == NULL) {
 
		error("Undefined command '%.*s'", s - start - 1, start);
 
		return NULL;
 
	}
 

	
 
	if (c == '.') {
 
		const char *casep = s;
 

	
 
		if (!(cmd->flags & C_CASE))
 
			fatal("Command '%s' can't have a case", cmd->cmd);
 

	
 
		do c = *s++; while (c != '}' && c != ' ' && c != '\0');
 
		*casei = ResolveCaseName(casep, s - casep - 1);
 
	}
 

	
 
	if (c == '\0') {
 
		error("Missing } from command '%s'", start);
 
		return NULL;
 
	}
 

	
 

	
 
	if (c != '}') {
 
		if (c == '=') s--;
 
		// copy params
 
		start = s;
 
		for (;;) {
 
			c = *s++;
 
			if (c == '}') break;
 
			if (c == '\0') {
 
				error("Missing } from command '%s'", start);
 
				return NULL;
 
			}
 
			if (s - start == 250) fatal("param command too long");
 
			*param++ = c;
 
		}
 
	}
 
	*param = '\0';
 

	
 
	*str = s;
 

	
 
	return cmd;
 
}
 

	
 

	
 
static void HandlePragma(char *str)
 
{
 
	if (!memcmp(str, "id ", 3)) {
 
		_next_string_id = strtoul(str + 3, NULL, 0);
 
	} else if (!memcmp(str, "name ", 5)) {
 
		ttd_strlcpy(_lang_name, str + 5, sizeof(_lang_name));
 
	} else if (!memcmp(str, "ownname ", 8)) {
 
		ttd_strlcpy(_lang_ownname, str + 8, sizeof(_lang_ownname));
 
	} else if (!memcmp(str, "isocode ", 8)) {
 
		ttd_strlcpy(_lang_isocode, str + 8, sizeof(_lang_isocode));
 
	} else if (!memcmp(str, "plural ", 7)) {
 
		_lang_pluralform = atoi(str + 7);
 
		if (_lang_pluralform >= lengthof(_plural_form_counts))
 
			fatal("Invalid pluralform %d", _lang_pluralform);
 
	} else if (!memcmp(str, "gender ", 7)) {
 
		char* buf = str + 7;
 

	
 
		for (;;) {
 
			const char* s = ParseWord(&buf);
 

	
 
			if (s == NULL) break;
 
			if (_numgenders >= MAX_NUM_GENDER) fatal("Too many genders, max %d", MAX_NUM_GENDER);
 
			ttd_strlcpy(_genders[_numgenders], s, sizeof(_genders[_numgenders]));
 
			_numgenders++;
 
		}
 
	} else if (!memcmp(str, "case ", 5)) {
 
		char* buf = str + 5;
 

	
 
		for (;;) {
 
			const char* s = ParseWord(&buf);
 

	
 
			if (s == NULL) break;
 
			if (_numcases >= MAX_NUM_CASES) fatal("Too many cases, max %d", MAX_NUM_CASES);
 
			ttd_strlcpy(_cases[_numcases], s, sizeof(_cases[_numcases]));
 
			_numcases++;
 
		}
 
	} else {
 
		fatal("unknown pragma '%s'", str);
 
	}
 
}
 

	
 
static void ExtractCommandString(ParsedCommandStruct* p, const char* s, bool warnings)
 
{
 
	char param[100];
 
	int argno;
 
	int argidx = 0;
 
	int casei;
 

	
 
	memset(p, 0, sizeof(*p));
 

	
 
	for (;;) {
 
		// read until next command from a.
 
		const CmdStruct* ar = ParseCommandString(&s, param, &argno, &casei);
 

	
 
		if (ar == NULL) break;
 

	
 
		// Sanity checking
 
		if (argno != -1 && ar->consumes == 0) fatal("Non consumer param can't have a paramindex");
 

	
 
		if (ar->consumes) {
 
			if (argno != -1) argidx = argno;
 
			if (argidx < 0 || argidx >= lengthof(p->cmd)) fatal("invalid param idx %d", argidx);
 
			if (p->cmd[argidx] != NULL && p->cmd[argidx] != ar) fatal("duplicate param idx %d", argidx);
 

	
 
			p->cmd[argidx++] = ar;
 
		} else if (!(ar->flags & C_DONTCOUNT)) { // Ignore some of them
 
			if (p->np >= lengthof(p->pairs)) fatal("too many commands in string, max %d", lengthof(p->pairs));
 
			p->pairs[p->np].a = ar;
 
			p->pairs[p->np].v = param[0] != '\0' ? strdup(param) : "";
 
			p->np++;
 
		}
 
	}
 
}
 

	
 

	
 
static const CmdStruct *TranslateCmdForCompare(const CmdStruct *a)
 
{
 
	if (a == NULL) return NULL;
 

	
 
	if (strcmp(a->cmd, "STRING1") == 0 ||
 
			strcmp(a->cmd, "STRING2") == 0 ||
 
			strcmp(a->cmd, "STRING3") == 0 ||
 
			strcmp(a->cmd, "STRING4") == 0 ||
 
			strcmp(a->cmd, "STRING5") == 0) {
 
		return FindCmd("STRING", 6);
 
	}
 

	
 
	if (strcmp(a->cmd, "SKIP") == 0) return NULL;
 

	
 
	return a;
 
}
 

	
 

	
 
static bool CheckCommandsMatch(char *a, char *b, const char *name)
 
{
 
	ParsedCommandStruct templ;
 
	ParsedCommandStruct lang;
 
	int i,j;
 
	bool result = true;
 

	
 
	ExtractCommandString(&templ, b, true);
 
	ExtractCommandString(&lang, a, true);
 

	
 
	// For each string in templ, see if we find it in lang
 
	if (templ.np != lang.np) {
 
		warning("%s: template string and language string have a different # of commands", name);
 
		result = false;
 
	}
 

	
 
	for (i = 0; i < templ.np; i++) {
 
		// see if we find it in lang, and zero it out
 
		bool found = false;
 
		for (j = 0; j < lang.np; j++) {
 
			if (templ.pairs[i].a == lang.pairs[j].a &&
 
					strcmp(templ.pairs[i].v, lang.pairs[j].v) == 0) {
 
				// it was found in both. zero it out from lang so we don't find it again
 
				lang.pairs[j].a = NULL;
 
				found = true;
 
				break;
 
			}
 
		}
 

	
 
		if (!found) {
 
			warning("%s: command '%s' exists in template file but not in language file", name, templ.pairs[i].a->cmd);
 
			result = false;
 
		}
 
	}
 

	
 
	// if we reach here, all non consumer commands match up.
 
	// Check if the non consumer commands match up also.
 
	for (i = 0; i < lengthof(templ.cmd); i++) {
 
		if (TranslateCmdForCompare(templ.cmd[i]) != TranslateCmdForCompare(lang.cmd[i])) {
 
			warning("%s: Param idx #%d '%s' doesn't match with template command '%s'", name, i,
 
				lang.cmd[i]  == NULL ? "<empty>" : lang.cmd[i]->cmd,
 
				templ.cmd[i] == NULL ? "<empty>" : templ.cmd[i]->cmd);
 
			result = false;
 
		}
 
	}
 

	
 
	return result;
 
}
 

	
 
static void HandleString(char *str, bool master)
 
{
 
	char *s,*t;
 
	LangString *ent;
 
	char *casep;
 

	
 
	if (*str == '#') {
 
		if (str[1] == '#' && str[2] != '#') HandlePragma(str + 2);
 
		return;
 
	}
 

	
 
	// Ignore comments & blank lines
 
	if (*str == ';' || *str == ' ' || *str == '\0') return;
 

	
 
	s = strchr(str, ':');
 
	if (s == NULL) {
 
		error("Line has no ':' delimiter");
 
		return;
 
	}
 

	
 
	// Trim spaces.
 
	// After this str points to the command name, and s points to the command contents
 
	for (t = s; t > str && (t[-1] == ' ' || t[-1] == '\t'); t--);
 
	*t = 0;
 
	s++;
 

	
 
	/* Check string is valid UTF-8 */
 
	{
 
		const char *tmp;
 
		for (tmp = s; *tmp != '\0';) {
 
			size_t len = Utf8Validate(tmp);
 
			if (len == 0) fatal("Invalid UTF-8 sequence in '%s'", s);
 
			tmp += len;
 
		}
 
	}
 

	
 
	// Check if the string has a case..
 
	// The syntax for cases is IDENTNAME.case
 
	casep = strchr(str, '.');
 
	if (casep) *casep++ = 0;
 

	
 
	// Check if this string already exists..
 
	ent = HashFind(str);
 

	
 
	if (master) {
 
		if (ent != NULL && casep == NULL) {
 
			error("String name '%s' is used multiple times", str);
 
			return;
 
		}
 

	
 
		if (ent == NULL && casep != NULL) {
 
			error("Base string name '%s' doesn't exist yet. Define it before defining a case.", str);
 
			return;
 
		}
 

	
 
		if (ent == NULL) {
 
			if (_strings[_next_string_id]) {
 
				error("String ID 0x%X for '%s' already in use by '%s'", ent, str, _strings[_next_string_id]->name);
 
				return;
 
			}
 

	
 
			// Allocate a new LangString
 
			ent = calloc(1, sizeof(*ent));
 
			_strings[_next_string_id] = ent;
 
			ent->index = _next_string_id++;
 
			ent->name = strdup(str);
 
			ent->line = _cur_line;
 

	
 
			HashAdd(str, ent);
 
		}
 

	
 
		if (casep != NULL) {
 
			Case* c = malloc(sizeof(*c));
 

	
 
			c->caseidx = ResolveCaseName(casep, strlen(casep));
 
			c->string = strdup(s);
 
			c->next = ent->english_case;
 
			ent->english_case = c;
 
		} else {
 
			ent->english = strdup(s);
 
		}
 

	
 
	} else {
 
		if (ent == NULL) {
 
			warning("String name '%s' does not exist in master file", str);
 
			return;
 
		}
 

	
 
		if (ent->translated && casep == NULL) {
 
			error("String name '%s' is used multiple times", str);
 
			return;
 
		}
 

	
 
		if (s[0] == ':' && s[1] == '\0' && casep == NULL) {
 
			// Special syntax :: means we should just inherit the master string
 
			ent->translated = strdup(ent->english);
 
		} else {
 
			// make sure that the commands match
 
			if (!CheckCommandsMatch(s, ent->english, str)) return;
 

	
 
			if (casep != NULL) {
 
				Case* c = malloc(sizeof(*c));
 

	
 
				c->caseidx = ResolveCaseName(casep, strlen(casep));
 
				c->string = strdup(s);
 
				c->next = ent->translated_case;
 
				ent->translated_case = c;
 
			} else {
 
				ent->translated = strdup(s);
 
			}
 
		}
 
	}
 
}
 

	
 

	
 
static void rstrip(char *buf)
 
{
 
	int i = strlen(buf);
 
	while (i > 0 && (buf[i - 1] == '\r' || buf[i - 1] == '\n' || buf[i - 1] == ' ')) i--;
 
	buf[i] = '\0';
 
}
 

	
 

	
 
static void ParseFile(const char *file, bool english)
 
{
 
	FILE *in;
 
	char buf[2048];
 

	
 
	_file = file;
 

	
 
	// For each new file we parse, reset the genders.
 
	_numgenders = 0;
 
	// TODO:!! We can't reset the cases. In case the translated strings
 
	// derive some strings from english....
 

	
 

	
 
	in = fopen(file, "r");
 
	if (in == NULL) fatal("Cannot open file");
 
	_cur_line = 1;
 
	while (fgets(buf, sizeof(buf),in) != NULL) {
 
		rstrip(buf);
 
		HandleString(buf, english);
 
		_cur_line++;
 
	}
 
	fclose(in);
 
}
 

	
 

	
 
static uint32 MyHashStr(uint32 hash, const char *s)
 
{
 
	for (; *s != '\0'; s++) {
 
		hash = ROL(hash, 3) ^ *s;
 
		hash = (hash & 1 ? hash >> 1 ^ 0xDEADBEEF : hash >> 1);
 
	}
 
	return hash;
 
}
 

	
 

	
 
// make a hash of the file to get a unique "version number"
 
static void MakeHashOfStrings(void)
 
{
 
	uint32 hash = 0;
 
	uint i;
 

	
 
	for (i = 0; i != lengthof(_strings); i++) {
 
		const LangString* ls = _strings[i];
 

	
 
		if (ls != NULL) {
 
			const CmdStruct* cs;
 
			const char* s;
 
			char buf[256];
 
			int argno;
 
			int casei;
 

	
 
			s = ls->name;
 
			hash ^= i * 0x717239;
 
			hash = (hash & 1 ? hash >> 1 ^ 0xDEADBEEF : hash >> 1);
 
			hash = MyHashStr(hash, s + 1);
 

	
 
			s = ls->english;
 
			while ((cs = ParseCommandString(&s, buf, &argno, &casei)) != NULL) {
 
				if (cs->flags & C_DONTCOUNT) continue;
 

	
 
				hash ^= (cs - _cmd_structs) * 0x1234567;
 
				hash = (hash & 1 ? hash >> 1 ^ 0xF00BAA4 : hash >> 1);
 
			}
 
		}
 
	}
 
	_hash = hash;
 
}
 

	
 

	
 
static uint CountInUse(uint grp)
 
{
 
	int i;
 

	
 
	for (i = 0x800; --i >= 0;) if (_strings[(grp << 11) + i] != NULL) break;
 
	return i + 1;
 
}
 

	
 

	
 
bool CompareFiles(const char *n1, const char *n2)
 
{
 
	FILE *f1, *f2;
 
	char b1[4096];
 
	char b2[4096];
 
	size_t l1, l2;
 

	
 
	f2 = fopen(n2, "rb");
 
	if (f2 == NULL) return false;
 

	
 
	f1 = fopen(n1, "rb");
 
	if (f1 == NULL) fatal("can't open %s", n1);
 

	
 
	do {
 
		l1 = fread(b1, 1, sizeof(b1), f1);
 
		l2 = fread(b2, 1, sizeof(b2), f2);
 

	
 
		if (l1 != l2 || memcmp(b1, b2, l1)) {
 
			fclose(f2);
 
			fclose(f1);
 
			return false;
 
		}
 
	} while (l1);
 

	
 
	fclose(f2);
 
	fclose(f1);
 
	return true;
 
}
 

	
 

	
 
static void WriteStringsH(const char *filename)
 
{
 
	FILE *out;
 
	int i;
 
	int next = -1;
 
	int lastgrp;
 

	
 
	out = fopen("tmp.xxx", "w");
 
	if (out == NULL) fatal("can't open tmp.xxx");
 

	
 
	fprintf(out, "enum {");
 

	
 
	lastgrp = 0;
 

	
 
	for (i = 0; i != lengthof(_strings); i++) {
 
		if (_strings[i] != NULL) {
 
			if (lastgrp != (i >> 11)) {
 
				lastgrp = (i >> 11);
 
				fprintf(out, "};\n\nenum {");
 
			}
 

	
 
			fprintf(out, next == i ? "\t%s,\n" : "\n\t%s = 0x%X,\n", _strings[i]->name, i);
 
			next = i + 1;
 
		}
 
	}
 

	
 
	fprintf(out, "};\n");
 

	
 
	fprintf(out,
 
		"\nenum {\n"
 
		"\tLANGUAGE_PACK_IDENT = 0x474E414C, // Big Endian value for 'LANG' (LE is 0x 4C 41 4E 47)\n"
 
		"\tLANGUAGE_PACK_VERSION = 0x%X,\n"
 
		"};\n", (uint)_hash
 
	);
 

	
 
	fclose(out);
 

	
 
	if (CompareFiles("tmp.xxx", filename)) {
 
		// files are equal. tmp.xxx is not needed
 
		unlink("tmp.xxx");
 
	} else {
 
		// else rename tmp.xxx into filename
 
#if defined(WIN32) || defined(WIN64)
 
		unlink(filename);
 
#endif
 
		if (rename("tmp.xxx", filename) == -1) fatal("rename() failed");
 
	}
 
}
 

	
 
static int TranslateArgumentIdx(int argidx)
 
{
 
	int i, sum;
 

	
 
	if (argidx < 0 || argidx >= lengthof(_cur_pcs.cmd))
 
		fatal("invalid argidx %d", argidx);
 

	
 
	for (i = sum = 0; i < argidx; i++) {
 
		const CmdStruct *cs = _cur_pcs.cmd[i];
 
		sum += (cs != NULL) ? cs->consumes : 1;
 
	}
 

	
 
	return sum;
 
}
 

	
 
static void PutArgidxCommand(void)
 
{
 
	PutUtf8(SCC_ARG_INDEX);
 
	PutByte(TranslateArgumentIdx(_cur_argidx));
 
}
 

	
 

	
 
static void PutCommandString(const char *str)
 
{
 
	const CmdStruct *cs;
 
	char param[256];
 
	int argno;
 
	int casei;
 

	
 
	_cur_argidx = 0;
 

	
 
	while (*str != '\0') {
 
		// Process characters as they are until we encounter a {
 
		if (*str != '{') {
 
			PutByte(*str++);
 
			continue;
 
		}
 
		cs = ParseCommandString(&str, param, &argno, &casei);
 
		if (cs == NULL) break;
 

	
 
		if (casei != -1) {
 
			PutUtf8(SCC_SETCASE); // {SETCASE}
 
			PutByte(casei);
 
		}
 

	
 
		// For params that consume values, we need to handle the argindex properly
 
		if (cs->consumes > 0) {
 
			// Check if we need to output a move-param command
 
			if (argno != -1 && argno != _cur_argidx) {
 
				_cur_argidx = argno;
 
				PutArgidxCommand();
 
			}
 

	
 
			// Output the one from the master string... it's always accurate.
 
			cs = _cur_pcs.cmd[_cur_argidx++];
 
			if (cs == NULL) {
 
				fatal("%s: No argument exists at position %d", _cur_ident, _cur_argidx - 1);
 
			}
 
		}
 

	
 
		cs->proc(param, cs->value);
 
	}
 
}
 

	
 
static void WriteLength(FILE *f, uint length)
 
{
 
	if (length < 0xC0) {
 
		fputc(length, f);
 
	} else if (length < 0x4000) {
 
		fputc((length >> 8) | 0xC0, f);
 
		fputc(length & 0xFF, f);
 
	} else {
 
		fatal("string too long");
 
	}
 
}
 

	
 

	
 
static void WriteLangfile(const char *filename, int show_todo)
 
{
 
	FILE *f;
 
	uint in_use[32];
 
	LanguagePackHeader hdr;
 
	uint i;
 
	uint j;
 

	
 
	f = fopen(filename, "wb");
 
	if (f == NULL) fatal("can't open %s", filename);
 

	
 
	memset(&hdr, 0, sizeof(hdr));
 
	for (i = 0; i != 32; i++) {
 
		uint n = CountInUse(i);
 

	
 
		in_use[i] = n;
 
		hdr.offsets[i] = TO_LE16(n);
 
	}
 

	
 
	// see line 655: fprintf(..."\tLANGUAGE_PACK_IDENT = 0x474E414C,...)
 
	hdr.ident = TO_LE32(0x474E414C); // Big Endian value for 'LANG'
 
	hdr.version = TO_LE32(_hash);
 
	hdr.plural_form = _lang_pluralform;
 
	strcpy(hdr.name, _lang_name);
 
	strcpy(hdr.own_name, _lang_ownname);
 
	strcpy(hdr.isocode, _lang_isocode);
 

	
 
	fwrite(&hdr, sizeof(hdr), 1, f);
 

	
 
	for (i = 0; i != 32; i++) {
 
		for (j = 0; j != in_use[i]; j++) {
 
			const LangString* ls = _strings[(i << 11) + j];
 
			const Case* casep;
 
			const char* cmdp;
 

	
 
			// For undefined strings, just set that it's an empty string
 
			if (ls == NULL) {
 
				WriteLength(f, 0);
 
				continue;
 
			}
 

	
 
			_cur_ident = ls->name;
 
			_cur_line = ls->line;
 

	
 
			// Produce a message if a string doesn't have a translation.
 
			if (show_todo > 0 && ls->translated == NULL) {
 
				if (show_todo == 2) {
 
					warning("'%s' is untranslated", ls->name);
 
				} else {
 
					const char *s = "<TODO> ";
 
					while (*s != '\0') PutByte(*s++);
 
				}
 
			}
 

	
 
			// Extract the strings and stuff from the english command string
 
			ExtractCommandString(&_cur_pcs, ls->english, false);
 

	
 
			if (ls->translated_case != NULL || ls->translated != NULL) {
 
				casep = ls->translated_case;
 
				cmdp = ls->translated;
 
			} else {
 
				casep = ls->english_case;
 
				cmdp = ls->english;
 
			}
 

	
 
			_translated = _masterlang || (cmdp != ls->english);
 

	
 
			if (casep != NULL) {
 
				const Case* c;
 
				uint num;
 

	
 
				// Need to output a case-switch.
 
				// It has this format
 
				// <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
 
				// Each LEN is printed using 2 bytes in big endian order.
 
				PutUtf8(SCC_SWITCH_CASE);
 
				// Count the number of cases
 
				for (num = 0, c = casep; c; c = c->next) num++;
 
				PutByte(num);
 

	
 
				// Write each case
 
				for (c = casep; c != NULL; c = c->next) {
 
					int pos;
 

	
 
					PutByte(c->caseidx);
 
					// Make some space for the 16-bit length
 
					pos = _put_pos;
 
					PutByte(0);
 
					PutByte(0);
 
					// Write string
 
					PutCommandString(c->string);
 
					PutByte(0); // terminate with a zero
 
					// Fill in the length
 
					_put_buf[pos + 0] = GB(_put_pos - (pos + 2), 8, 8);
 
					_put_buf[pos + 1] = GB(_put_pos - (pos + 2), 0, 8);
 
				}
 
			}
 

	
 
			if (cmdp != NULL) PutCommandString(cmdp);
 

	
 
			WriteLength(f, _put_pos);
 
			fwrite(_put_buf, 1, _put_pos, f);
 
			_put_pos = 0;
 
		}
 
	}
 

	
 
	fputc(0, f);
 
	fclose(f);
 
}
 

	
 
/** Multi-OS mkdirectory function */
 
static inline void ottd_mkdir(const char *directory)
 
{
 
#if defined(WIN32) || defined(__WATCOMC__)
 
		mkdir(directory);
 
#else
 
		mkdir(directory, 0755);
 
#endif
 
}
 

	
 
/** Create a path consisting of an already existing path, a possible
 
 * path seperator and the filename. The seperator is only appended if the path
 
 * does not already end with a seperator */
 
static inline char *mkpath(char *buf, size_t buflen, const char *path, const char *file)
 
{
 
	char *p;
 
	ttd_strlcpy(buf, path, buflen); // copy directory into buffer
 

	
 
	p = strchr(buf, '\0'); // add path seperator if necessary
 
	if (p[-1] != PATHSEPCHAR && (size_t)(p - buf) + 1 < buflen) *p++ = PATHSEPCHAR;
 
	ttd_strlcpy(p, file, buflen - (size_t)(p - buf)); // catenate filename at end of buffer
 
	return buf;
 
}
 

	
 
#if defined(__MINGW32__)
 
/**
 
 * On MingW, it is common that both / as \ are accepted in the
 
 * params. To go with those flow, we rewrite all incoming /
 
 * simply to \, so internally we can safely assume \.
 
 */
 
static inline char *replace_pathsep(char *s)
 
{
 
	char *c;
 

	
 
	for (c = s; *c != '\0'; c++) if (*c == '/') *c = '\\';
 
	return s;
 
}
 
#else
 
static inline char *replace_pathsep(char *s) { return s; }
 
#endif
 

	
 
int CDECL main(int argc, char* argv[])
 
{
 
	char pathbuf[256];
 
	const char *src_dir = ".";
 
	const char *dest_dir = NULL;
 

	
 
	int show_todo = 0;
 

	
 
	while (argc > 1 && *argv[1] == '-') {
 
		if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) {
 
			puts("$Revision$");
 
			return 0;
 
		}
 

	
 
		if (strcmp(argv[1], "-t") == 0 || strcmp(argv[1], "--todo") == 0) {
 
			show_todo = 1;
 
			argc--, argv++;
 
			continue;
 
		}
 

	
 
		if (strcmp(argv[1], "-w") == 0 || strcmp(argv[1], "--warning") == 0) {
 
			show_todo = 2;
 
			argc--, argv++;
 
			continue;
 
		}
 

	
 
		if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) {
 
			puts(
 
				"strgen - $Revision$\n"
 
				" -v | --version    print version information and exit\n"
 
				" -t | --todo       replace any untranslated strings with '<TODO>'\n"
 
				" -w | --warning    print a warning for any untranslated strings\n"
 
				" -h | -? | --help  print this help message and exit\n"
 
				" -s | --source_dir search for english.txt in the specified directory\n"
 
				" -d | --dest_dir   put output file in the specified directory, create if needed\n"
 
				" Run without parameters and strgen will search for english.txt and parse it,\n"
 
				" creating strings.h. Passing an argument, strgen will translate that language\n"
 
				" file using english.txt as a reference and output <language>.lng."
 
			);
 
			return 0;
 
		}
 

	
 
		if (argc > 2 && (strcmp(argv[1], "-s") == 0 || strcmp(argv[1], "--source_dir") == 0)) {
 
			src_dir = replace_pathsep(argv[2]);
 
			argc -= 2, argv += 2;
 
			continue;
 
		}
 

	
 
		if (argc > 2 && (strcmp(argv[1], "-d") == 0 || strcmp(argv[1], "--dest_dir") == 0)) {
 
			dest_dir = replace_pathsep(argv[2]);
 
			argc -= 2, argv += 2;
 
			continue;
 
		}
 

	
 
		fprintf(stderr, "Invalid arguments\n");
 
		return 0;
 
	}
 

	
 
	if (dest_dir == NULL) dest_dir = src_dir; // if dest_dir is not specified, it equals src_dir
 

	
 
	/* strgen has two modes of operation. If no (free) arguments are passed
 
	 * strgen generates strings.h to the destination directory. If it is supplied
 
	 * with a (free) parameter the program will translate that language to destination
 
	 * directory. As input english.txt is parsed from the source directory */
 
	if (argc == 1) {
 
		mkpath(pathbuf, lengthof(pathbuf), src_dir, "english.txt");
 

	
 
		/* parse master file */
 
		_masterlang = true;
 
		ParseFile(pathbuf, true);
 
		MakeHashOfStrings();
 
		if (_errors) return 1;
 

	
 
		/* write strings.h */
 
		ottd_mkdir(dest_dir);
 
		mkpath(pathbuf, lengthof(pathbuf), dest_dir, "strings.h");
 
		WriteStringsH(pathbuf);
 
	} else if (argc == 2) {
 
		char *r;
 

	
 
		mkpath(pathbuf, lengthof(pathbuf), src_dir, "english.txt");
 

	
 
		/* parse master file and check if target file is correct */
 
		_masterlang = false;
 
		ParseFile(pathbuf, true);
 
		MakeHashOfStrings();
 
		ParseFile(replace_pathsep(argv[1]), false); // target file
 
		if (_errors) return 1;
 

	
 
		/* get the targetfile, strip any directories and append to destination path */
 
		r = strrchr(argv[1], PATHSEPCHAR);
 
		mkpath(pathbuf, lengthof(pathbuf), dest_dir, (r != NULL) ? &r[1] : argv[1]);
 

	
 
		/* rename the .txt (input-extension) to .lng */
 
		r = strrchr(pathbuf, '.');
 
		if (r == NULL || strcmp(r, ".txt") != 0) r = strchr(pathbuf, '\0');
 
		ttd_strlcpy(r, ".lng", (size_t)(r - pathbuf));
 
		WriteLangfile(pathbuf, show_todo);
 

	
 
		/* if showing warnings, print a summary of the language */
 
		if (show_todo == 2) {
 
			fprintf(stdout, "%d warnings and %d errors for %s\n", _warnings, _errors, pathbuf);
 
		}
 
	} else {
 
		fprintf(stderr, "Invalid arguments\n");
 
	}
 

	
 
	return 0;
 
}
src/string.c
Show inline comments
 
deleted file
src/string.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "string.h"
 
#include "macros.h"
 
#include "table/control_codes.h"
 

	
 
#include <stdarg.h>
 
#include <ctype.h> // required for tolower()
 

	
 
void ttd_strlcat(char *dst, const char *src, size_t size)
 
{
 
	assert(size > 0);
 
	for (; size > 0 && *dst != '\0'; --size, ++dst) {}
 
	assert(size > 0);
 
	while (--size > 0 && *src != '\0') *dst++ = *src++;
 
	*dst = '\0';
 
}
 

	
 

	
 
void ttd_strlcpy(char *dst, const char *src, size_t size)
 
{
 
	assert(size > 0);
 
	while (--size > 0 && *src != '\0') *dst++ = *src++;
 
	*dst = '\0';
 
}
 

	
 

	
 
char* strecat(char* dst, const char* src, const char* last)
 
{
 
	assert(dst <= last);
 
	for (; *dst != '\0'; ++dst)
 
		if (dst == last) return dst;
 
	for (; *src != '\0' && dst != last; ++dst, ++src) *dst = *src;
 
	*dst = '\0';
 
	return strecpy(dst, src, last);
 
}
 

	
 

	
 
char* strecpy(char* dst, const char* src, const char* last)
 
{
 
	assert(dst <= last);
 
	for (; *src != '\0' && dst != last; ++dst, ++src) *dst = *src;
 
	*dst = '\0';
 
#if 1
 
	if (dst == last && *src != '\0') {
 
		error("String too long for destination buffer");
 
	}
 
#endif
 
	return dst;
 
}
 

	
 

	
 
char* CDECL str_fmt(const char* str, ...)
 
{
 
	char buf[4096];
 
	va_list va;
 
	int len;
 
	char* p;
 

	
 
	va_start(va, str);
 
	len = vsnprintf(buf, lengthof(buf), str, va);
 
	va_end(va);
 
	p = malloc(len + 1);
 
	if (p != NULL) memcpy(p, buf, len + 1);
 
	return p;
 
}
 

	
 

	
 
void str_validate(char *str)
 
{
 
	char *dst = str;
 
	WChar c;
 
	size_t len;
 

	
 
	for (len = Utf8Decode(&c, str); c != '\0'; len = Utf8Decode(&c, str)) {
 
		if (IsPrintable(c) && (c < SCC_SPRITE_START || c > SCC_SPRITE_END ||
 
			IsValidChar(c - SCC_SPRITE_START, CS_ALPHANUMERAL))) {
 
			/* Copy the character back. Even if dst is current the same as str
 
			 * (i.e. no characters have been changed) this is quicker than
 
			 * moving the pointers ahead by len */
 
			do {
 
				*dst++ = *str++;
 
			} while (--len != 0);
 
		} else {
 
			/* Replace the undesirable character with a question mark */
 
			str += len;
 
			*dst++ = '?';
 
		}
 
	}
 

	
 
	*dst = '\0';
 
}
 

	
 

	
 
void str_strip_colours(char *str)
 
{
 
	char *dst = str;
 
	WChar c;
 
	size_t len;
 

	
 
	for (len = Utf8Decode(&c, str); c != '\0'; len = Utf8Decode(&c, str)) {
 
		if (c < SCC_BLUE || c > SCC_BLACK) {
 
			/* Copy the character back. Even if dst is current the same as str
 
			 * (i.e. no characters have been changed) this is quicker than
 
			 * moving the pointers ahead by len */
 
			do {
 
				*dst++ = *str++;
 
			} while (--len != 0);
 
		} else {
 
			/* Just skip (strip) the colour codes */
 
			str += len;
 
		}
 
	}
 
	*dst = '\0';
 
}
 

	
 
/** Convert a given ASCII string to lowercase.
 
 * NOTE: only support ASCII characters, no UTF8 fancy. As currently
 
 * the function is only used to lowercase data-filenames if they are
 
 * not found, this is sufficient. If more, or general functionality is
 
 * needed, look to r7271 where it was removed because it was broken when
 
 * using certain locales: eg in Turkish the uppercase 'I' was converted to
 
 * '?', so just revert to the old functionality */
 
void strtolower(char *str)
 
{
 
	for (; *str != '\0'; str++) *str = tolower(*str);
 
}
 

	
 
/**
 
 * Only allow certain keys. You can define the filter to be used. This makes
 
 *  sure no invalid keys can get into an editbox, like BELL.
 
 * @param key character to be checked
 
 * @param afilter the filter to use
 
 * @return true or false depending if the character is printable/valid or not
 
 */
 
bool IsValidChar(WChar key, CharSetFilter afilter)
 
{
 
	switch (afilter) {
 
		case CS_ALPHANUMERAL: return IsPrintable(key);
 
		case CS_NUMERAL:      return (key >= '0' && key <= '9');
 
		case CS_ALPHA:        return IsPrintable(key) && !(key >= '0' && key <= '9');
 
	}
 

	
 
	return false;
 
}
 

	
 
#ifdef WIN32
 
int CDECL snprintf(char *str, size_t size, const char *format, ...)
 
{
 
	va_list ap;
 
	int ret;
 

	
 
	va_start(ap, format);
 
	ret = vsnprintf(str, size, format, ap);
 
	va_end(ap);
 
	return ret;
 
}
 

	
 
#ifdef _MSC_VER
 
int CDECL vsnprintf(char *str, size_t size, const char *format, va_list ap)
 
{
 
	int ret;
 
	ret = _vsnprintf(str, size, format, ap);
 
	if (ret < 0) str[size - 1] = '\0';
 
	return ret;
 
}
 
#endif /* _MSC_VER */
 

	
 
#endif /* WIN32 */
 

	
 

	
 
/* UTF-8 handling routines */
 

	
 

	
 
/* Decode and consume the next UTF-8 encoded character
 
 * @param c Buffer to place decoded character.
 
 * @param s Character stream to retrieve character from.
 
 * @return Number of characters in the sequence.
 
 */
 
size_t Utf8Decode(WChar *c, const char *s)
 
{
 
	assert(c != NULL);
 

	
 
	if (!HASBIT(s[0], 7)) {
 
		/* Single byte character: 0xxxxxxx */
 
		*c = s[0];
 
		return 1;
 
	} else if (GB(s[0], 5, 3) == 6) {
 
		if (IsUtf8Part(s[1])) {
 
			/* Double byte character: 110xxxxx 10xxxxxx */
 
			*c = GB(s[0], 0, 5) << 6 | GB(s[1], 0, 6);
 
			if (*c >= 0x80) return 2;
 
		}
 
	} else if (GB(s[0], 4, 4) == 14) {
 
		if (IsUtf8Part(s[1]) && IsUtf8Part(s[2])) {
 
			/* Triple byte character: 1110xxxx 10xxxxxx 10xxxxxx */
 
			*c = GB(s[0], 0, 4) << 12 | GB(s[1], 0, 6) << 6 | GB(s[2], 0, 6);
 
			if (*c >= 0x800) return 3;
 
		}
 
	} else if (GB(s[0], 3, 5) == 30) {
 
		if (IsUtf8Part(s[1]) && IsUtf8Part(s[2]) && IsUtf8Part(s[3])) {
 
			/* 4 byte character: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
 
			*c = GB(s[0], 0, 3) << 18 | GB(s[1], 0, 6) << 12 | GB(s[2], 0, 6) << 6 | GB(s[3], 0, 6);
 
			if (*c >= 0x10000 && *c <= 0x10FFFF) return 4;
 
		}
 
	}
 

	
 
	//DEBUG(misc, 1, "[utf8] invalid UTF-8 sequence");
 
	*c = '?';
 
	return 1;
 
}
 

	
 

	
 
/* Encode a unicode character and place it in the buffer
 
 * @param buf Buffer to place character.
 
 * @param c   Unicode character to encode.
 
 * @return Number of characters in the encoded sequence.
 
 */
 
size_t Utf8Encode(char *buf, WChar c)
 
{
 
	if (c < 0x80) {
 
		*buf = c;
 
		return 1;
 
	} else if (c < 0x800) {
 
		*buf++ = 0xC0 + GB(c,  6, 5);
 
		*buf   = 0x80 + GB(c,  0, 6);
 
		return 2;
 
	} else if (c < 0x10000) {
 
		*buf++ = 0xE0 + GB(c, 12, 4);
 
		*buf++ = 0x80 + GB(c,  6, 6);
 
		*buf   = 0x80 + GB(c,  0, 6);
 
		return 3;
 
	} else if (c < 0x110000) {
 
		*buf++ = 0xF0 + GB(c, 18, 3);
 
		*buf++ = 0x80 + GB(c, 12, 6);
 
		*buf++ = 0x80 + GB(c,  6, 6);
 
		*buf   = 0x80 + GB(c,  0, 6);
 
		return 4;
 
	}
 

	
 
	//DEBUG(misc, 1, "[utf8] can't UTF-8 encode value 0x%X", c);
 
	*buf = '?';
 
	return 1;
 
}
src/strings.c
Show inline comments
 
deleted file
src/strings.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "currency.h"
 
#include "functions.h"
 
#include "string.h"
 
#include "strings.h"
 
#include "table/strings.h"
 
#include "namegen.h"
 
#include "station.h"
 
#include "town.h"
 
#include "vehicle.h"
 
#include "news.h"
 
#include "screenshot.h"
 
#include "waypoint.h"
 
#include "industry.h"
 
#include "variables.h"
 
#include "newgrf_text.h"
 
#include "table/landscape_const.h"
 
#include "table/control_codes.h"
 
#include "music.h"
 
#include "date.h"
 
#include "industry.h"
 

	
 
#ifdef WIN32
 
/* for opendir/readdir/closedir */
 
# include "fios.h"
 
#else
 
# include <sys/types.h>
 
# include <dirent.h>
 
#endif /* WIN32 */
 

	
 
char _userstring[128];
 

	
 
static char *StationGetSpecialString(char *buff, int x, const char* last);
 
static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char* last);
 
static char *GetSpecialPlayerNameString(char *buff, int ind, const int32 *argv, const char* last);
 

	
 
static char *FormatString(char *buff, const char *str, const int32 *argv, uint casei, const char* last);
 

	
 
typedef struct LanguagePack {
 
	uint32 ident;
 
	uint32 version;     // 32-bits of auto generated version info which is basically a hash of strings.h
 
	char name[32];      // the international name of this language
 
	char own_name[32];  // the localized name of this language
 
	char isocode[16];   // the ISO code for the language (not country code)
 
	uint16 offsets[32]; // the offsets
 
	byte plural_form;   // how to compute plural forms
 
	byte pad[3];        // pad header to be a multiple of 4
 
	char data[VARARRAY_SIZE];
 
} LanguagePack;
 

	
 
static char **_langpack_offs;
 
static LanguagePack *_langpack;
 
static uint _langtab_num[32]; // Offset into langpack offs
 
static uint _langtab_start[32]; // Offset into langpack offs
 

	
 
static const StringID _cargo_string_list[NUM_LANDSCAPE][NUM_CARGO] = {
 
	{ /* LT_NORMAL */
 
		STR_PASSENGERS,
 
		STR_TONS,
 
		STR_BAGS,
 
		STR_LITERS,
 
		STR_ITEMS,
 
		STR_CRATES,
 
		STR_TONS,
 
		STR_TONS,
 
		STR_TONS,
 
		STR_TONS,
 
		STR_BAGS,
 
		STR_RES_OTHER
 
	},
 

	
 
	{ /* LT_HILLY */
 
		STR_PASSENGERS,
 
		STR_TONS,
 
		STR_BAGS,
 
		STR_LITERS,
 
		STR_ITEMS,
 
		STR_CRATES,
 
		STR_TONS,
 
		STR_TONS,
 
		STR_RES_OTHER,
 
		STR_TONS,
 
		STR_BAGS,
 
		STR_TONS
 
	},
 

	
 
	{ /* LT_DESERT */
 
		STR_PASSENGERS,
 
		STR_LITERS,
 
		STR_BAGS,
 
		STR_LITERS,
 
		STR_TONS,
 
		STR_CRATES,
 
		STR_TONS,
 
		STR_TONS,
 
		STR_TONS,
 
		STR_LITERS,
 
		STR_BAGS,
 
		STR_TONS
 
	},
 

	
 
	{ /* LT_CANDY */
 
		STR_PASSENGERS,
 
		STR_TONS,
 
		STR_BAGS,
 
		STR_NOTHING,
 
		STR_NOTHING,
 
		STR_TONS,
 
		STR_TONS,
 
		STR_LITERS,
 
		STR_TONS,
 
		STR_NOTHING,
 
		STR_LITERS,
 
		STR_NOTHING
 
	}
 
};
 

	
 

	
 
// Read an int64 from the argv array.
 
static inline int64 GetInt64(const int32 **argv)
 
{
 
	int64 result;
 

	
 
	assert(argv);
 
	result = (uint32)(*argv)[0] + ((uint64)(uint32)(*argv)[1] << 32);
 
	(*argv)+=2;
 
	return result;
 
}
 

	
 
// Read an int32 from the argv array.
 
static inline int32 GetInt32(const int32 **argv)
 
{
 
	assert(argv);
 
	return *(*argv)++;
 
}
 

	
 
// Read an array from the argv array.
 
static inline const int32 *GetArgvPtr(const int32 **argv, int n)
 
{
 
	const int32 *result;
 
	assert(*argv);
 
	result = *argv;
 
	(*argv) += n;
 
	return result;
 
}
 

	
 

	
 
#define NUM_BOUND_STRINGS 8
 

	
 
// Array to hold the bound strings.
 
static const char *_bound_strings[NUM_BOUND_STRINGS];
 

	
 
// This index is used to implement a "round-robin" allocating of
 
// slots for BindCString. NUM_BOUND_STRINGS slots are reserved.
 
// Which means that after NUM_BOUND_STRINGS calls to BindCString,
 
// the indices will be reused.
 
static int _bind_index;
 

	
 
static const char *GetStringPtr(StringID string)
 
{
 
	return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)];
 
}
 

	
 
// The highest 8 bits of string contain the "case index".
 
// These 8 bits will only be set when FormatString wants to print
 
// the string in a different case. No one else except FormatString
 
// should set those bits, therefore string CANNOT be StringID, but uint32.
 
static char *GetStringWithArgs(char *buffr, uint string, const int32 *argv, const char* last)
 
{
 
	uint index = GB(string,  0, 11);
 
	uint tab   = GB(string, 11,  5);
 
	char buff[512];
 

	
 
	if (GB(string, 0, 16) == 0) error("!invalid string id 0 in GetString");
 

	
 
	switch (tab) {
 
		case 4:
 
			if (index >= 0xC0)
 
				return GetSpecialTownNameString(buffr, index - 0xC0, GetInt32(&argv), last);
 
			break;
 

	
 
		case 14:
 
			if (index >= 0xE4)
 
				return GetSpecialPlayerNameString(buffr, index - 0xE4, argv, last);
 
			break;
 

	
 
		// User defined name
 
		case 15:
 
			return GetName(buffr, index, last);
 

	
 
		case 26:
 
			/* Include string within newgrf text (format code 81) */
 
			if (HASBIT(index, 10)) {
 
				StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
 
				return GetStringWithArgs(buffr, string, argv, last);
 
			}
 
			break;
 

	
 
		case 28:
 
			GetGRFString(buff, index, lastof(buff));
 
			return FormatString(buffr, buff, argv, 0, last);
 

	
 
		case 29:
 
			GetGRFString(buff, index + 0x800, lastof(buff));
 
			return FormatString(buffr, buff, argv, 0, last);
 

	
 
		case 30:
 
			GetGRFString(buff, index + 0x1000, lastof(buff));
 
			return FormatString(buffr, buff, argv, 0, last);
 

	
 
		case 31:
 
			// dynamic strings. These are NOT to be passed through the formatter,
 
			// but passed through verbatim.
 
			if (index < (STR_SPEC_USERSTRING & 0x7FF)) {
 
				return strecpy(buffr, _bound_strings[index], last);
 
			}
 

	
 
			return FormatString(buffr, _userstring, NULL, 0, last);
 
	}
 

	
 
	if (index >= _langtab_num[tab]) {
 
		error(
 
			"!String 0x%X is invalid. "
 
			"Probably because an old version of the .lng file.\n", string
 
		);
 
	}
 

	
 
	return FormatString(buffr, GetStringPtr(GB(string, 0, 16)), argv, GB(string, 24, 8), last);
 
}
 

	
 
char *GetString(char *buffr, StringID string, const char* last)
 
{
 
	return GetStringWithArgs(buffr, string, (int32*)_decode_parameters, last);
 
}
 

	
 

	
 
char *InlineString(char *buf, StringID string)
 
{
 
	buf += Utf8Encode(buf, SCC_STRING_ID);
 
	buf += Utf8Encode(buf, string);
 
	return buf;
 
}
 

	
 

	
 
// This function takes a C-string and allocates a temporary string ID.
 
// The duration of the bound string is valid only until the next GetString,
 
// so be careful.
 
StringID BindCString(const char *str)
 
{
 
	int idx = (++_bind_index) & (NUM_BOUND_STRINGS - 1);
 
	_bound_strings[idx] = str;
 
	return idx + STR_SPEC_DYNSTRING;
 
}
 

	
 
// This function is used to "bind" a C string to a OpenTTD dparam slot.
 
void SetDParamStr(uint n, const char *str)
 
{
 
	SetDParam(n, BindCString(str));
 
}
 

	
 
void InjectDParam(int amount)
 
{
 
	memmove(_decode_parameters + amount, _decode_parameters, sizeof(_decode_parameters) - amount * sizeof(uint32));
 
}
 

	
 
static const uint32 _divisor_table[] = {
 
	1000000000,
 
	100000000,
 
	10000000,
 
	1000000,
 

	
 
	100000,
 
	10000,
 
	1000,
 
	100,
 
	10,
 
	1
 
};
 

	
 
// TODO
 
static char *FormatCommaNumber(char *buff, int32 number, const char* last)
 
{
 
	uint32 quot,divisor;
 
	int i;
 
	uint32 tot;
 
	uint32 num;
 

	
 
	if (number < 0) {
 
		*buff++ = '-';
 
		number = -number;
 
	}
 

	
 
	num = number;
 

	
 
	tot = 0;
 
	for (i = 0; i != 10; i++) {
 
		divisor = _divisor_table[i];
 
		quot = 0;
 
		if (num >= divisor) {
 
			quot = num / _divisor_table[i];
 
			num = num % _divisor_table[i];
 
		}
 
		if (tot |= quot || i == 9) {
 
			*buff++ = '0' + quot;
 
			if (i == 0 || i == 3 || i == 6) *buff++ = ',';
 
		}
 
	}
 

	
 
	*buff = '\0';
 

	
 
	return buff;
 
}
 

	
 
// TODO
 
static char *FormatNoCommaNumber(char *buff, int32 number, const char* last)
 
{
 
	uint32 quot,divisor;
 
	int i;
 
	uint32 tot;
 
	uint32 num;
 

	
 
	if (number < 0) {
 
		buff = strecpy(buff, "-", last);
 
		number = -number;
 
	}
 

	
 
	num = number;
 

	
 
	tot = 0;
 
	for (i = 0; i != 10; i++) {
 
		divisor = _divisor_table[i];
 
		quot = 0;
 
		if (num >= divisor) {
 
			quot = num / _divisor_table[i];
 
			num = num % _divisor_table[i];
 
		}
 
		if (tot |= quot || i == 9) {
 
			*buff++ = '0' + quot;
 
		}
 
	}
 

	
 
	*buff = '\0';
 

	
 
	return buff;
 
}
 

	
 

	
 
static char *FormatYmdString(char *buff, Date date, const char* last)
 
{
 
	YearMonthDay ymd;
 

	
 
	ConvertDateToYMD(date, &ymd);
 

	
 
	buff = strecpy(buff, GetStringPtr(ymd.day + STR_01AC_1ST - 1), last);
 
	buff = strecpy(buff, " ", last);
 
	buff = strecpy(buff, GetStringPtr(STR_0162_JAN + ymd.month), last);
 
	buff = strecpy(buff, " ", last);
 

	
 
	return FormatNoCommaNumber(buff, ymd.year, last);
 
}
 

	
 
static char *FormatMonthAndYear(char *buff, Date date, const char* last)
 
{
 
	YearMonthDay ymd;
 

	
 
	ConvertDateToYMD(date, &ymd);
 

	
 
	buff = strecpy(buff, GetStringPtr(STR_MONTH_JAN + ymd.month), last);
 
	buff = strecpy(buff, " ", last);
 

	
 
	return FormatNoCommaNumber(buff, ymd.year, last);
 
}
 

	
 
static char *FormatTinyDate(char *buff, Date date, const char* last)
 
{
 
	YearMonthDay ymd;
 

	
 
	ConvertDateToYMD(date, &ymd);
 
	buff += snprintf(
 
		buff, last - buff + 1,
 
		" %02i-%02i-%04i", ymd.day, ymd.month + 1, ymd.year
 
	);
 

	
 
	return buff;
 
}
 

	
 
static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, int64 number, bool compact, const char* last)
 
{
 
	const char* multiplier = "";
 
	char buf[40];
 
	char* p;
 
	int j;
 

	
 
	// multiply by exchange rate
 
	number *= spec->rate;
 

	
 
	// convert from negative
 
	if (number < 0) {
 
		buff = strecpy(buff, "-", last);
 
		number = -number;
 
	}
 

	
 
	/* Add prefix part, folowing symbol_pos specification.
 
	 * Here, it can can be either 0 (prefix) or 2 (both prefix anf suffix).
 
	 * The only remaining value is 1 (suffix), so everything that is not 1 */
 
	if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
 

	
 
	// for huge numbers, compact the number into k or M
 
	if (compact) {
 
		if (number >= 1000000000) {
 
			number = (number + 500000) / 1000000;
 
			multiplier = "M";
 
		} else if (number >= 1000000) {
 
			number = (number + 500) / 1000;
 
			multiplier = "k";
 
		}
 
	}
 

	
 
	// convert to ascii number and add commas
 
	p = endof(buf);
 
	*--p = '\0';
 
	j = 4;
 
	do {
 
		if (--j == 0) {
 
			*--p = spec->separator;
 
			j = 3;
 
		}
 
		*--p = '0' + number % 10;
 
	} while ((number /= 10) != 0);
 
	buff = strecpy(buff, p, last);
 

	
 
	buff = strecpy(buff, multiplier, last);
 

	
 
	/* Add suffix part, folowing symbol_pos specification.
 
	 * Here, it can can be either 1 (suffix) or 2 (both prefix anf suffix).
 
	 * The only remaining value is 1 (prefix), so everything that is not 0 */
 
	if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
 

	
 
	return buff;
 
}
 

	
 
static int DeterminePluralForm(int32 n)
 
{
 
	// The absolute value determines plurality
 
	if (n < 0) n = -n;
 

	
 
	switch (_langpack->plural_form) {
 
	// Two forms, singular used for one only
 
	// Used in:
 
	//   Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
 
	//   Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto
 
	case 0:
 
	default:
 
		return n != 1;
 

	
 
	// Only one form
 
	// Used in:
 
	//   Hungarian, Japanese, Korean, Turkish
 
	case 1:
 
		return 0;
 

	
 
	// Two forms, singular used for zero and one
 
	// Used in:
 
	//   French, Brazilian Portuguese
 
	case 2:
 
		return n > 1;
 

	
 
	// Three forms, special case for zero
 
	// Used in:
 
	//   Latvian
 
	case 3:
 
		return n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;
 

	
 
	// Three forms, special case for one and two
 
	// Used in:
 
	//   Gaelige (Irish)
 
	case 4:
 
		return n==1 ? 0 : n==2 ? 1 : 2;
 

	
 
	// Three forms, special case for numbers ending in 1[2-9]
 
	// Used in:
 
	//   Lithuanian
 
	case 5:
 
		return n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2;
 

	
 
	// Three forms, special cases for numbers ending in 1 and 2, 3, 4, except those ending in 1[1-4]
 
	// Used in:
 
	//   Croatian, Czech, Russian, Slovak, Ukrainian
 
	case 6:
 
		return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
 

	
 
	// Three forms, special case for one and some numbers ending in 2, 3, or 4
 
	// Used in:
 
	//   Polish
 
	case 7:
 
		return n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
 

	
 
	// Four forms, special case for one and all numbers ending in 02, 03, or 04
 
	// Used in:
 
	//   Slovenian
 
	case 8:
 
		return n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;
 
	}
 
}
 

	
 
static const char *ParseStringChoice(const char *b, uint form, char *dst, int *dstlen)
 
{
 
	//<NUM> {Length of each string} {each string}
 
	uint n = (byte)*b++;
 
	uint pos,i, mylen=0,mypos=0;
 

	
 
	for (i = pos = 0; i != n; i++) {
 
		uint len = (byte)*b++;
 
		if (i == form) {
 
			mypos = pos;
 
			mylen = len;
 
		}
 
		pos += len;
 
	}
 
	*dstlen = mylen;
 
	memcpy(dst, b + mypos, mylen);
 
	return b + pos;
 
}
 

	
 
typedef struct Units {
 
	int s_m;           ///< Multiplier for velocity
 
	int s_s;           ///< Shift for velocity
 
	StringID velocity; ///< String for velocity
 
	int p_m;           ///< Multiplier for power
 
	int p_s;           ///< Shift for power
 
	StringID power;    ///< String for velocity
 
	int w_m;           ///< Multiplier for weight
 
	int w_s;           ///< Shift for weight
 
	StringID s_weight; ///< Short string for weight
 
	StringID l_weight; ///< Long string for weight
 
	int v_m;           ///< Multiplier for volume
 
	int v_s;           ///< Shift for volume
 
	StringID s_volume; ///< Short string for volume
 
	StringID l_volume; ///< Long string for volume
 
	int f_m;           ///< Multiplier for force
 
	int f_s;           ///< Shift for force
 
	StringID force;    ///< String for force
 
} Units;
 

	
 
/* Unit conversions */
 
static const Units units[] = {
 
	{ // Imperial (Original, mph, hp, metric ton, litre, kN)
 
		  10,  4, STR_UNITS_VELOCITY_IMPERIAL,
 
		   1,  0, STR_UNITS_POWER_IMPERIAL,
 
		   1,  0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
 
		1000,  0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
 
		   1,  0, STR_UNITS_FORCE_SI,
 
	},
 
	{ // Metric (km/h, hp, metric ton, litre, kN)
 
		   1,  0, STR_UNITS_VELOCITY_METRIC,
 
		   1,  0, STR_UNITS_POWER_METRIC,
 
		   1,  0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
 
		1000,  0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
 
		   1,  0, STR_UNITS_FORCE_SI,
 
	},
 
	{ // SI (m/s, kilowatt, kilogram, cubic metres, kilonewton)
 
		 284, 10, STR_UNITS_VELOCITY_SI,
 
		 764, 10, STR_UNITS_POWER_SI,
 
		1000,  0, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
 
		   1,  0, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
 
		   1,  0, STR_UNITS_FORCE_SI,
 
	},
 
};
 

	
 
static char* FormatString(char* buff, const char* str, const int32* argv, uint casei, const char* last)
 
{
 
	extern const char _openttd_revision[];
 
	WChar b;
 
	const int32 *argv_orig = argv;
 
	uint modifier = 0;
 

	
 
	while ((b = Utf8Consume(&str)) != '\0') {
 
		switch (b) {
 
			case SCC_SETX: // {SETX}
 
				if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
 
					buff += Utf8Encode(buff, SCC_SETX);
 
					*buff++ = *str++;
 
				}
 
				break;
 

	
 
			case SCC_SETXY: // {SETXY}
 
				if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
 
					buff += Utf8Encode(buff, SCC_SETXY);
 
					*buff++ = *str++;
 
					*buff++ = *str++;
 
				}
 
				break;
 

	
 
			case SCC_STRING_ID: // {STRINL}
 
				buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, last);
 
				break;
 

	
 
			case SCC_DATE_LONG: // {DATE_LONG}
 
				buff = FormatYmdString(buff, GetInt32(&argv), last);
 
				break;
 

	
 
			case SCC_DATE_SHORT: // {DATE_SHORT}
 
				buff = FormatMonthAndYear(buff, GetInt32(&argv), last);
 
				break;
 

	
 
			case SCC_VELOCITY: {// {VELOCITY}
 
				int32 args[1];
 
				assert(_opt_ptr->units < lengthof(units));
 
				args[0] = GetInt32(&argv) * units[_opt_ptr->units].s_m >> units[_opt_ptr->units].s_s;
 
				buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].velocity), args, modifier >> 24, last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_CURRENCY_COMPACT: /* {CURRCOMPACT} */
 
				buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), true, last);
 
				break;
 

	
 
			case SCC_REVISION: /* {REV} */
 
				buff = strecpy(buff, _openttd_revision, last);
 
				break;
 

	
 
			case SCC_CARGO_SHORT: { /* {SHORTCARGO} */
 
				// Short description of cargotypes. Layout:
 
				// 8-bit = cargo type
 
				// 16-bit = cargo count
 
				StringID cargo_str = _cargo_types_base_values[_opt_ptr->landscape].units_volume[GetInt32(&argv)];
 
				switch (cargo_str) {
 
					case STR_TONS: {
 
						int32 args[1];
 
						assert(_opt_ptr->units < lengthof(units));
 
						args[0] = GetInt32(&argv) * units[_opt_ptr->units].w_m >> units[_opt_ptr->units].w_s;
 
						buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].l_weight), args, modifier >> 24, last);
 
						modifier = 0;
 
						break;
 
					}
 

	
 
					case STR_LITERS: {
 
						int32 args[1];
 
						assert(_opt_ptr->units < lengthof(units));
 
						args[0] = GetInt32(&argv) * units[_opt_ptr->units].v_m >> units[_opt_ptr->units].v_s;
 
						buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].l_volume), args, modifier >> 24, last);
 
						modifier = 0;
 
						break;
 
					}
 

	
 
					default:
 
						buff = FormatCommaNumber(buff, GetInt32(&argv), last);
 
						buff = strecpy(buff, " ", last);
 
						buff = strecpy(buff, GetStringPtr(cargo_str), last);
 
						break;
 
				}
 
			} break;
 

	
 
			case SCC_CURRENCY_COMPACT_64: { /* {CURRCOMPACT64} */
 
				// 64 bit compact currency-unit
 
				buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), true, last);
 
				break;
 
			}
 

	
 
			case SCC_STRING1: { /* {STRING1} */
 
				// String that consumes ONE argument
 
				uint str = modifier + GetInt32(&argv);
 
				buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1), last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_STRING2: { /* {STRING2} */
 
				// String that consumes TWO arguments
 
				uint str = modifier + GetInt32(&argv);
 
				buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2), last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_STRING3: { /* {STRING3} */
 
				// String that consumes THREE arguments
 
				uint str = modifier + GetInt32(&argv);
 
				buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3), last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_STRING4: { /* {STRING4} */
 
				// String that consumes FOUR arguments
 
				uint str = modifier + GetInt32(&argv);
 
				buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4), last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_STRING5: { /* {STRING5} */
 
				// String that consumes FIVE arguments
 
				uint str = modifier + GetInt32(&argv);
 
				buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 5), last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_STATION_FEATURES: { /* {STATIONFEATURES} */
 
				buff = StationGetSpecialString(buff, GetInt32(&argv), last);
 
				break;
 
			}
 

	
 
			case SCC_INDUSTRY_NAME: { /* {INDUSTRY} */
 
				const Industry* i = GetIndustry(GetInt32(&argv));
 
				int32 args[2];
 

	
 
				// industry not valid anymore?
 
				if (!IsValidIndustry(i)) break;
 

	
 
				// First print the town name and the industry type name
 
				// The string STR_INDUSTRY_PATTERN controls the formatting
 
				args[0] = i->town->index;
 
				args[1] = GetIndustrySpec(i->type)->name;
 
				buff = FormatString(buff, GetStringPtr(STR_INDUSTRY_FORMAT), args, modifier >> 24, last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_VOLUME: { // {VOLUME}
 
				int32 args[1];
 
				assert(_opt_ptr->units < lengthof(units));
 
				args[0] = GetInt32(&argv) * units[_opt_ptr->units].v_m >> units[_opt_ptr->units].v_s;
 
				buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].l_volume), args, modifier >> 24, last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_GENDER_LIST: { // {G 0 Der Die Das}
 
				const char* s = GetStringPtr(argv_orig[(byte)*str++]); // contains the string that determines gender.
 
				int len;
 
				int gender = 0;
 
				if (s != NULL && Utf8Consume(&s) == SCC_GENDER_INDEX) gender = (byte)s[0];
 
				str = ParseStringChoice(str, gender, buff, &len);
 
				buff += len;
 
				break;
 
			}
 

	
 
			case SCC_DATE_TINY: { // {DATE_TINY}
 
				buff = FormatTinyDate(buff, GetInt32(&argv), last);
 
				break;
 
			}
 

	
 
			case SCC_CARGO: { // {CARGO}
 
				// Layout now is:
 
				//   8bit   - cargo type
 
				//   16-bit - cargo count
 
				CargoID cargo = GetInt32(&argv);
 
				StringID cargo_str = (cargo == CT_INVALID) ? STR_8838_N_A : _cargoc.names_long[cargo];
 
				buff = GetStringWithArgs(buff, cargo_str, argv++, last);
 
				break;
 
			}
 

	
 
			case SCC_POWER: { // {POWER}
 
				int32 args[1];
 
				assert(_opt_ptr->units < lengthof(units));
 
				args[0] = GetInt32(&argv) * units[_opt_ptr->units].p_m >> units[_opt_ptr->units].p_s;
 
				buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].power), args, modifier >> 24, last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_VOLUME_SHORT: { // {VOLUME_S}
 
				int32 args[1];
 
				assert(_opt_ptr->units < lengthof(units));
 
				args[0] = GetInt32(&argv) * units[_opt_ptr->units].v_m >> units[_opt_ptr->units].v_s;
 
				buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].s_volume), args, modifier >> 24, last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_WEIGHT: { // {WEIGHT}
 
				int32 args[1];
 
				assert(_opt_ptr->units < lengthof(units));
 
				args[0] = GetInt32(&argv) * units[_opt_ptr->units].w_m >> units[_opt_ptr->units].w_s;
 
				buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].l_weight), args, modifier >> 24, last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_WEIGHT_SHORT: { // {WEIGHT_S}
 
				int32 args[1];
 
				assert(_opt_ptr->units < lengthof(units));
 
				args[0] = GetInt32(&argv) * units[_opt_ptr->units].w_m >> units[_opt_ptr->units].w_s;
 
				buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].s_weight), args, modifier >> 24, last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_FORCE: { // {FORCE}
 
				int32 args[1];
 
				assert(_opt_ptr->units < lengthof(units));
 
				args[0] = GetInt32(&argv) * units[_opt_ptr->units].f_m >> units[_opt_ptr->units].f_s;
 
				buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].force), args, modifier >> 24, last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_SKIP: // {SKIP}
 
				argv++;
 
				break;
 

	
 
			// This sets up the gender for the string.
 
			// We just ignore this one. It's used in {G 0 Der Die Das} to determine the case.
 
			case SCC_GENDER_INDEX: // {GENDER 0}
 
				str++;
 
				break;
 

	
 
			case SCC_STRING: {// {STRING}
 
				uint str = modifier + GetInt32(&argv);
 
				// WARNING. It's prohibited for the included string to consume any arguments.
 
				// For included strings that consume argument, you should use STRING1, STRING2 etc.
 
				// To debug stuff you can set argv to NULL and it will tell you
 
				buff = GetStringWithArgs(buff, str, argv, last);
 
				modifier = 0;
 
				break;
 
			}
 

	
 
			case SCC_COMMA: // {COMMA}
 
				buff = FormatCommaNumber(buff, GetInt32(&argv), last);
 
				break;
 

	
 
			case SCC_ARG_INDEX: // Move argument pointer
 
				argv = argv_orig + (byte)*str++;
 
				break;
 

	
 
			case SCC_PLURAL_LIST: { // {P}
 
				int32 v = argv_orig[(byte)*str++]; // contains the number that determines plural
 
				int len;
 
				str = ParseStringChoice(str, DeterminePluralForm(v), buff, &len);
 
				buff += len;
 
				break;
 
			}
 

	
 
			case SCC_NUM: // {NUM}
 
				buff = FormatNoCommaNumber(buff, GetInt32(&argv), last);
 
				break;
 

	
 
			case SCC_CURRENCY: // {CURRENCY}
 
				buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), false, last);
 
				break;
 

	
 
			case SCC_WAYPOINT_NAME: { // {WAYPOINT}
 
				int32 temp[2];
 
				Waypoint *wp = GetWaypoint(GetInt32(&argv));
 
				StringID str;
 
				if (wp->string != STR_NULL) {
 
					str = wp->string;
 
				} else {
 
					temp[0] = wp->town_index;
 
					temp[1] = wp->town_cn + 1;
 
					str = wp->town_cn == 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL;
 
				}
 
				buff = GetStringWithArgs(buff, str, temp, last);
 
				break;
 
			}
 

	
 
			case SCC_STATION_NAME: { // {STATION}
 
				const Station* st = GetStation(GetInt32(&argv));
 

	
 
				if (!IsValidStation(st)) { // station doesn't exist anymore
 
					buff = GetStringWithArgs(buff, STR_UNKNOWN_DESTINATION, NULL, last);
 
				} else {
 
					int32 temp[2];
 
					temp[0] = st->town->townnametype;
 
					temp[1] = st->town->townnameparts;
 
					buff = GetStringWithArgs(buff, st->string_id, temp, last);
 
				}
 
				break;
 
			}
 

	
 
			case SCC_TOWN_NAME: { // {TOWN}
 
				const Town* t = GetTown(GetInt32(&argv));
 
				int32 temp[1];
 

	
 
				assert(IsValidTown(t));
 

	
 
				temp[0] = t->townnameparts;
 
				buff = GetStringWithArgs(buff, t->townnametype, temp, last);
 
				break;
 
			}
 

	
 
			case SCC_CURRENCY_64: { // {CURRENCY64}
 
				buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
 
				break;
 
			}
 

	
 
			case SCC_SETCASE: { // {SETCASE}
 
				// This is a pseudo command, it's outputted when someone does {STRING.ack}
 
				// The modifier is added to all subsequent GetStringWithArgs that accept the modifier.
 
				modifier = (byte)*str++ << 24;
 
				break;
 
			}
 

	
 
			case SCC_SWITCH_CASE: { // {Used to implement case switching}
 
				// <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
 
				// Each LEN is printed using 2 bytes in big endian order.
 
				uint num = (byte)*str++;
 
				while (num) {
 
					if ((byte)str[0] == casei) {
 
						// Found the case, adjust str pointer and continue
 
						str += 3;
 
						break;
 
					}
 
					// Otherwise skip to the next case
 
					str += 3 + (str[1] << 8) + str[2];
 
					num--;
 
				}
 
				break;
 
			}
 

	
 
			default:
 
				if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
 
				break;
 
		}
 
	}
 
	*buff = '\0';
 
	return buff;
 
}
 

	
 

	
 
static char *StationGetSpecialString(char *buff, int x, const char* last)
 
{
 
	if ((x & 0x01) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
 
	if ((x & 0x02) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
 
	if ((x & 0x04) && (buff + Utf8CharLen(SCC_BUS)   < last)) buff += Utf8Encode(buff, SCC_BUS);
 
	if ((x & 0x08) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
 
	if ((x & 0x10) && (buff + Utf8CharLen(SCC_SHIP)  < last)) buff += Utf8Encode(buff, SCC_SHIP);
 
	*buff = '\0';
 
	return buff;
 
}
 

	
 
static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char* last)
 
{
 
	char name[512];
 

	
 
	_town_name_generators[ind](name, seed, lastof(name));
 
	return strecpy(buff, name, last);
 
}
 

	
 
static const char* const _silly_company_names[] = {
 
	"Bloggs Brothers",
 
	"Tiny Transport Ltd.",
 
	"Express Travel",
 
	"Comfy-Coach & Co.",
 
	"Crush & Bump Ltd.",
 
	"Broken & Late Ltd.",
 
	"Sam Speedy & Son",
 
	"Supersonic Travel",
 
	"Mike's Motors",
 
	"Lightning International",
 
	"Pannik & Loozit Ltd.",
 
	"Inter-City Transport",
 
	"Getout & Pushit Ltd."
 
};
 

	
 
static const char* const _surname_list[] = {
 
	"Adams",
 
	"Allan",
 
	"Baker",
 
	"Bigwig",
 
	"Black",
 
	"Bloggs",
 
	"Brown",
 
	"Campbell",
 
	"Gordon",
 
	"Hamilton",
 
	"Hawthorn",
 
	"Higgins",
 
	"Green",
 
	"Gribble",
 
	"Jones",
 
	"McAlpine",
 
	"MacDonald",
 
	"McIntosh",
 
	"Muir",
 
	"Murphy",
 
	"Nelson",
 
	"O'Donnell",
 
	"Parker",
 
	"Phillips",
 
	"Pilkington",
 
	"Quigley",
 
	"Sharkey",
 
	"Thomson",
 
	"Watkins"
 
};
 

	
 
static const char* const _silly_surname_list[] = {
 
	"Grumpy",
 
	"Dozy",
 
	"Speedy",
 
	"Nosey",
 
	"Dribble",
 
	"Mushroom",
 
	"Cabbage",
 
	"Sniffle",
 
	"Fishy",
 
	"Swindle",
 
	"Sneaky",
 
	"Nutkins"
 
};
 

	
 
static const char _initial_name_letters[] = {
 
	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
 
	'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
 
};
 

	
 
static char *GenAndCoName(char *buff, uint32 arg, const char* last)
 
{
 
	const char* const* base;
 
	uint num;
 

	
 
	if (_opt_ptr->landscape == LT_CANDY) {
 
		base = _silly_surname_list;
 
		num  = lengthof(_silly_surname_list);
 
	} else {
 
		base = _surname_list;
 
		num  = lengthof(_surname_list);
 
	}
 

	
 
	buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
 
	buff = strecpy(buff, " & Co.", last);
 

	
 
	return buff;
 
}
 

	
 
static char *GenPresidentName(char *buff, uint32 x, const char* last)
 
{
 
	char initial[] = "?. ";
 
	const char* const* base;
 
	uint num;
 
	uint i;
 

	
 
	initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
 
	buff = strecpy(buff, initial, last);
 

	
 
	i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
 
	if (i < sizeof(_initial_name_letters)) {
 
		initial[0] = _initial_name_letters[i];
 
		buff = strecpy(buff, initial, last);
 
	}
 

	
 
	if (_opt_ptr->landscape == LT_CANDY) {
 
		base = _silly_surname_list;
 
		num  = lengthof(_silly_surname_list);
 
	} else {
 
		base = _surname_list;
 
		num  = lengthof(_surname_list);
 
	}
 

	
 
	buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
 

	
 
	return buff;
 
}
 

	
 
static char *GetSpecialPlayerNameString(char *buff, int ind, const int32 *argv, const char* last)
 
{
 
	switch (ind) {
 
		case 1: // not used
 
			return strecpy(buff, _silly_company_names[GetInt32(&argv) & 0xFFFF], last);
 

	
 
		case 2: // used for Foobar & Co company names
 
			return GenAndCoName(buff, GetInt32(&argv), last);
 

	
 
		case 3: // President name
 
			return GenPresidentName(buff, GetInt32(&argv), last);
 

	
 
		case 4: // song names
 
			return strecpy(buff, origin_songs_specs[GetInt32(&argv) - 1].song_name, last);
 
	}
 

	
 
	// town name?
 
	if (IS_INT_INSIDE(ind - 6, 0, SPECSTR_TOWNNAME_LAST-SPECSTR_TOWNNAME_START + 1)) {
 
		buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv), last);
 
		return strecpy(buff, " Transport", last);
 
	}
 

	
 
	// language name?
 
	if (IS_INT_INSIDE(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
 
		int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
 
		return strecpy(buff,
 
			i == _dynlang.curr ? _langpack->own_name : _dynlang.ent[i].name, last);
 
	}
 

	
 
	// resolution size?
 
	if (IS_INT_INSIDE(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
 
		int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
 
		buff += snprintf(
 
			buff, last - buff + 1, "%dx%d", _resolutions[i][0], _resolutions[i][1]
 
		);
 
		return buff;
 
	}
 

	
 
	// screenshot format name?
 
	if (IS_INT_INSIDE(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
 
		int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
 
		return strecpy(buff, GetScreenshotFormatDesc(i), last);
 
	}
 

	
 
	assert(0);
 
	return NULL;
 
}
 

	
 
// remap a string ID from the old format to the new format
 
StringID RemapOldStringID(StringID s)
 
{
 
	switch (s) {
 
		case 0x0006: return STR_SV_EMPTY;
 
		case 0x7000: return STR_SV_UNNAMED;
 
		case 0x70E4: return SPECSTR_PLAYERNAME_ENGLISH;
 
		case 0x70E9: return SPECSTR_PLAYERNAME_ENGLISH;
 
		case 0x8864: return STR_SV_TRAIN_NAME;
 
		case 0x902B: return STR_SV_ROADVEH_NAME;
 
		case 0x9830: return STR_SV_SHIP_NAME;
 
		case 0xA02F: return STR_SV_AIRCRAFT_NAME;
 

	
 
		default:
 
			if (IS_INT_INSIDE(s, 0x300F, 0x3030)) {
 
				return s - 0x300F + STR_SV_STNAME;
 
			} else {
 
				return s;
 
			}
 
	}
 
}
 

	
 
bool ReadLanguagePack(int lang_index)
 
{
 
	int tot_count, i;
 
	LanguagePack *lang_pack;
 
	size_t len;
 
	char **langpack_offs;
 
	char *s;
 

	
 
	{
 
		char *lang = str_fmt("%s%s", _paths.lang_dir, _dynlang.ent[lang_index].file);
 
		lang_pack = ReadFileToMem(lang, &len, 200000);
 
		free(lang);
 
	}
 
	if (lang_pack == NULL) return false;
 
	if (len < sizeof(LanguagePack) ||
 
			lang_pack->ident != TO_LE32(LANGUAGE_PACK_IDENT) ||
 
			lang_pack->version != TO_LE32(LANGUAGE_PACK_VERSION)) {
 
		free(lang_pack);
 
		return false;
 
	}
 

	
 
#if defined(TTD_BIG_ENDIAN)
 
	for (i = 0; i != 32; i++) {
 
		lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
 
	}
 
#endif
 

	
 
	tot_count = 0;
 
	for (i = 0; i != 32; i++) {
 
		uint num = lang_pack->offsets[i];
 
		_langtab_start[i] = tot_count;
 
		_langtab_num[i] = num;
 
		tot_count += num;
 
	}
 

	
 
	// Allocate offsets
 
	langpack_offs = malloc(tot_count * sizeof(*langpack_offs));
 

	
 
	// Fill offsets
 
	s = lang_pack->data;
 
	for (i = 0; i != tot_count; i++) {
 
		len = (byte)*s;
 
		*s++ = '\0'; // zero terminate the string before.
 
		if (len >= 0xC0) len = ((len & 0x3F) << 8) + (byte)*s++;
 
		langpack_offs[i] = s;
 
		s += len;
 
	}
 

	
 
	free(_langpack);
 
	_langpack = lang_pack;
 

	
 
	free(_langpack_offs);
 
	_langpack_offs = langpack_offs;
 

	
 
	ttd_strlcpy(_dynlang.curr_file, _dynlang.ent[lang_index].file, sizeof(_dynlang.curr_file));
 

	
 
	_dynlang.curr = lang_index;
 
	SetCurrentGrfLangID(_langpack->isocode);
 
	return true;
 
}
 

	
 
/** Determine the current charset based on the environment
 
 * First check some default values, after this one we passed ourselves
 
 * and if none exist return the value for $LANG
 
 * @param environment variable to check conditionally if default ones are not
 
 *        set. Pass NULL if you don't want additional checks.
 
 * @return return string containing current charset, or NULL if not-determinable */
 
const char *GetCurrentLocale(const char *param)
 
{
 
	const char *env;
 

	
 
	env = getenv("LANGUAGE");
 
	if (env != NULL) return env;
 

	
 
	env = getenv("LC_ALL");
 
	if (env != NULL) return env;
 

	
 
	if (param != NULL) {
 
		env = getenv(param);
 
		if (env != NULL) return env;
 
	}
 

	
 
	return getenv("LANG");
 
}
 

	
 
static int CDECL LanguageCompareFunc(const void *a, const void *b)
 
{
 
	return strcmp(*(const char* const *)a, *(const char* const *)b);
 
}
 

	
 
static int GetLanguageList(char **languages, int max)
 
{
 
	DIR *dir;
 
	struct dirent *dirent;
 
	int num = 0;
 

	
 
	dir = opendir(_paths.lang_dir);
 
	if (dir != NULL) {
 
		while ((dirent = readdir(dir)) != NULL) {
 
			const char *d_name = FS2OTTD(dirent->d_name);
 
			char *t = strrchr(d_name, '.');
 

	
 
			if (t != NULL && strcmp(t, ".lng") == 0) {
 
				languages[num++] = strdup(d_name);
 
				if (num == max) break;
 
			}
 
		}
 
		closedir(dir);
 
	}
 

	
 
	qsort(languages, num, sizeof(char*), LanguageCompareFunc);
 
	return num;
 
}
 

	
 
// make a list of the available language packs. put the data in _dynlang struct.
 
void InitializeLanguagePacks(void)
 
{
 
	DynamicLanguages *dl = &_dynlang;
 
	int i;
 
	int n;
 
	int m;
 
	int def;
 
	int def2;
 
	int fallback;
 
	LanguagePack hdr;
 
	FILE *in;
 
	char *files[MAX_LANG];
 
	const char* lang;
 

	
 
	lang = GetCurrentLocale("LC_MESSAGES");
 
	if (lang == NULL) lang = "en_GB";
 

	
 
	n = GetLanguageList(files, lengthof(files));
 

	
 
	def = -1;
 
	def2 = -1;
 
	fallback = 0;
 

	
 
	// go through the language files and make sure that they are valid.
 
	for (i = m = 0; i != n; i++) {
 
		size_t j;
 

	
 
		char *s = str_fmt("%s%s", _paths.lang_dir, files[i]);
 
		in = fopen(s, "rb");
 
		free(s);
 
		if (in == NULL ||
 
				(j = fread(&hdr, sizeof(hdr), 1, in), fclose(in), j) != 1 ||
 
				hdr.ident != TO_LE32(LANGUAGE_PACK_IDENT) ||
 
				hdr.version != TO_LE32(LANGUAGE_PACK_VERSION)) {
 
			free(files[i]);
 
			continue;
 
		}
 

	
 
		dl->ent[m].file = files[i];
 
		dl->ent[m].name = strdup(hdr.name);
 

	
 
		if (strcmp(hdr.isocode, "en_GB")  == 0) fallback = m;
 
		if (strncmp(hdr.isocode, lang, 2) == 0) def2 = m;
 
		if (strncmp(hdr.isocode, lang, 5) == 0) def = m;
 

	
 
		m++;
 
	}
 
	if (def == -1) def = (def2 != -1 ? def2 : fallback);
 

	
 
	if (m == 0)
 
		error(n == 0 ? "No available language packs" : "Invalid version of language packs");
 

	
 
	dl->num = m;
 
	for (i = 0; i != dl->num; i++) dl->dropdown[i] = SPECSTR_LANGUAGE_START + i;
 
	dl->dropdown[i] = INVALID_STRING_ID;
 

	
 
	for (i = 0; i != dl->num; i++)
 
		if (strcmp(dl->ent[i].file, dl->curr_file) == 0) {
 
			def = i;
 
			break;
 
		}
 

	
 
	if (!ReadLanguagePack(def))
 
		error("can't read language pack '%s'", dl->ent[def].file);
 
}
src/subsidy_gui.c
Show inline comments
 
deleted file
src/subsidy_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "window.h"
 
#include "station.h"
 
#include "industry.h"
 
#include "town.h"
 
#include "player.h"
 
#include "gfx.h"
 
#include "economy.h"
 
#include "variables.h"
 
#include "date.h"
 

	
 
static void HandleSubsidyClick(int y)
 
{
 
	const Subsidy *s;
 
	uint num;
 
	int offs;
 
	TileIndex xy;
 

	
 
	if (y < 0) return;
 

	
 
	num = 0;
 
	for (s = _subsidies; s != endof(_subsidies); s++) {
 
		if (s->cargo_type != CT_INVALID && s->age < 12) {
 
			y -= 10;
 
			if (y < 0) goto handle_click;
 
			num++;
 
		}
 
	}
 

	
 
	if (num == 0) {
 
		y -= 10;
 
		if (y < 0) return;
 
	}
 

	
 
	y -= 11;
 
	if (y < 0) return;
 

	
 
	for (s = _subsidies; s != endof(_subsidies); s++) {
 
		if (s->cargo_type != CT_INVALID && s->age >= 12) {
 
			y -= 10;
 
			if (y < 0) goto handle_click;
 
		}
 
	}
 
	return;
 

	
 
handle_click:
 

	
 
	/* determine from coordinate for subsidy and try to scroll to it */
 
	offs = s->from;
 
	if (s->age >= 12) {
 
		xy = GetStation(offs)->xy;
 
	} else if (s->cargo_type == CT_PASSENGERS || s->cargo_type == CT_MAIL) {
 
		xy = GetTown(offs)->xy;
 
	} else {
 
		xy = GetIndustry(offs)->xy;
 

	
 
	}
 
	if (!ScrollMainWindowToTile(xy)) {
 
		/* otherwise determine to coordinate for subsidy and scroll to it */
 
		offs = s->to;
 
		if (s->age >= 12) {
 
			xy = GetStation(offs)->xy;
 
		} else if (s->cargo_type == CT_PASSENGERS || s->cargo_type == CT_MAIL || s->cargo_type == CT_GOODS || s->cargo_type == CT_FOOD) {
 
			xy = GetTown(offs)->xy;
 
		} else {
 
			xy = GetIndustry(offs)->xy;
 
		}
 
		ScrollMainWindowToTile(xy);
 
	}
 
}
 

	
 
static void DrawSubsidiesWindow(const Window *w)
 
{
 
	YearMonthDay ymd;
 
	const Subsidy *s;
 
	uint num;
 
	int x;
 
	int y;
 

	
 
	DrawWindowWidgets(w);
 

	
 
	ConvertDateToYMD(_date, &ymd);
 

	
 
	y = 15;
 
	x = 1;
 
	DrawString(x, y, STR_2026_SUBSIDIES_ON_OFFER_FOR, 0);
 
	y += 10;
 
	num = 0;
 

	
 
	for (s = _subsidies; s != endof(_subsidies); s++) {
 
		if (s->cargo_type != CT_INVALID && s->age < 12) {
 
			int x2;
 

	
 
			SetupSubsidyDecodeParam(s, 1);
 
			x2 = DrawString(x + 2, y, STR_2027_FROM_TO, 0);
 

	
 
			SetDParam(0, _date - ymd.day + 384 - s->age * 32);
 
			DrawString(x2, y, STR_2028_BY, 0);
 
			y += 10;
 
			num++;
 
		}
 
	}
 

	
 
	if (num == 0) {
 
		DrawString(x + 2, y, STR_202A_NONE, 0);
 
		y += 10;
 
	}
 

	
 
	DrawString(x, y + 1, STR_202B_SERVICES_ALREADY_SUBSIDISED, 0);
 
	y += 10;
 
	num = 0;
 

	
 
	for (s = _subsidies; s != endof(_subsidies); s++) {
 
		if (s->cargo_type != CT_INVALID && s->age >= 12) {
 
			const Player *p;
 
			int xt;
 

	
 
			SetupSubsidyDecodeParam(s, 1);
 

	
 
			p = GetPlayer(GetStation(s->to)->owner);
 
			SetDParam(3, p->name_1);
 
			SetDParam(4, p->name_2);
 

	
 
			xt = DrawString(x + 2, y, STR_202C_FROM_TO, 0);
 

	
 
			SetDParam(0, _date - ymd.day + 768 - s->age * 32);
 
			DrawString(xt, y, STR_202D_UNTIL, 0);
 
			y += 10;
 
			num++;
 
		}
 
	}
 

	
 
	if (num == 0) DrawString(x + 2, y, STR_202A_NONE, 0);
 
}
 

	
 
static void SubsidiesListWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: DrawSubsidiesWindow(w); break;
 

	
 
		case WE_CLICK:
 
			switch (e->we.click.widget) {
 
				case 3:
 
					HandleSubsidyClick(e->we.click.pt.y - 25);
 
					break;
 
			}
 
		break;
 
	}
 
}
 

	
 
static const Widget _subsidies_list_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE, 13,   0,  10,   0,  13, STR_00C5,           STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE, 13,  11, 617,   0,  13, STR_2025_SUBSIDIES, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE, 13, 618, 629,   0,  13, STR_NULL,           STR_STICKY_BUTTON},
 
{      WWT_PANEL,   RESIZE_NONE, 13,   0, 629,  14, 126, 0x0,                STR_01FD_CLICK_ON_SERVICE_TO_CENTER},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _subsidies_list_desc = {
 
	WDP_AUTO, WDP_AUTO, 630, 127,
 
	WC_SUBSIDIES_LIST,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
 
	_subsidies_list_widgets,
 
	SubsidiesListWndProc
 
};
 

	
 

	
 
void ShowSubsidiesList(void)
 
{
 
	AllocateWindowDescFront(&_subsidies_list_desc, 0);
 
}
src/terraform_gui.c
Show inline comments
 
deleted file
src/terraform_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "clear_map.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "player.h"
 
#include "tile.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "sound.h"
 
#include "command.h"
 
#include "vehicle.h"
 
#include "signs.h"
 
#include "variables.h"
 

	
 
void CcTerraform(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_1F_SPLAT, tile);
 
	} else {
 
		SetRedErrorSquare(_terraform_err_tile);
 
	}
 
}
 

	
 

	
 
/** Scenario editor command that generates desert areas */
 
static void GenerateDesertArea(TileIndex end, TileIndex start)
 
{
 
	int size_x, size_y;
 
	int sx = TileX(start);
 
	int sy = TileY(start);
 
	int ex = TileX(end);
 
	int ey = TileY(end);
 

	
 
	if (_game_mode != GM_EDITOR) return;
 

	
 
	if (ex < sx) intswap(ex, sx);
 
	if (ey < sy) intswap(ey, sy);
 
	size_x = (ex - sx) + 1;
 
	size_y = (ey - sy) + 1;
 

	
 
	_generating_world = true;
 
	BEGIN_TILE_LOOP(tile, size_x, size_y, TileXY(sx, sy)) {
 
		if (GetTileType(tile) != MP_WATER) {
 
			SetTropicZone(tile, (_ctrl_pressed) ? TROPICZONE_INVALID : TROPICZONE_DESERT);
 
			DoCommandP(tile, 0, 0, NULL, CMD_LANDSCAPE_CLEAR);
 
			MarkTileDirtyByTile(tile);
 
		}
 
	} END_TILE_LOOP(tile, size_x, size_y, 0);
 
	_generating_world = false;
 
}
 

	
 
/** Scenario editor command that generates rocky areas */
 
static void GenerateRockyArea(TileIndex end, TileIndex start)
 
{
 
	int size_x, size_y;
 
	bool success = false;
 
	int sx = TileX(start);
 
	int sy = TileY(start);
 
	int ex = TileX(end);
 
	int ey = TileY(end);
 

	
 
	if (_game_mode != GM_EDITOR) return;
 

	
 
	if (ex < sx) intswap(ex, sx);
 
	if (ey < sy) intswap(ey, sy);
 
	size_x = (ex - sx) + 1;
 
	size_y = (ey - sy) + 1;
 

	
 
	BEGIN_TILE_LOOP(tile, size_x, size_y, TileXY(sx, sy)) {
 
		switch (GetTileType(tile)) {
 
			case MP_CLEAR:
 
			case MP_TREES:
 
				MakeClear(tile, CLEAR_ROCKS, 3);
 
				break;
 

	
 
			default: continue;
 
		}
 
		MarkTileDirtyByTile(tile);
 
		success = true;
 
	} END_TILE_LOOP(tile, size_x, size_y, 0);
 

	
 
	if (success) SndPlayTileFx(SND_1F_SPLAT, end);
 
}
 

	
 
/**
 
 * A central place to handle all X_AND_Y dragged GUI functions.
 
 * @param e @WindowEvent variable holding in its higher bits (excluding the lower
 
 * 4, since that defined the X_Y drag) the type of action to be performed
 
 * @return Returns true if the action was found and handled, and false otherwise. This
 
 * allows for additional implements that are more local. For example X_Y drag
 
 * of convertrail which belongs in rail_gui.c and not terraform_gui.c
 
 **/
 
bool GUIPlaceProcDragXY(const WindowEvent *e)
 
{
 
	TileIndex start_tile = e->we.place.starttile;
 
	TileIndex end_tile = e->we.place.tile;
 

	
 
	switch (e->we.place.userdata >> 4) {
 
	case GUI_PlaceProc_DemolishArea >> 4:
 
		DoCommandP(end_tile, start_tile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA));
 
		break;
 
	case GUI_PlaceProc_LevelArea >> 4:
 
		DoCommandP(end_tile, start_tile, 0, CcPlaySound10, CMD_LEVEL_LAND | CMD_AUTO);
 
		break;
 
	case GUI_PlaceProc_RockyArea >> 4:
 
		GenerateRockyArea(end_tile, start_tile);
 
		break;
 
	case GUI_PlaceProc_DesertArea >> 4:
 
		GenerateDesertArea(end_tile, start_tile);
 
		break;
 
	case GUI_PlaceProc_WaterArea >> 4:
 
		DoCommandP(end_tile, start_tile, _ctrl_pressed, CcBuildCanal, CMD_BUILD_CANAL | CMD_AUTO | CMD_MSG(STR_CANT_BUILD_CANALS));
 
		break;
 
	default: return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
typedef void OnButtonClick(Window *w);
 

	
 
static const uint16 _terraform_keycodes[] = {
 
	'Q',
 
	'W',
 
	'E',
 
	'D',
 
	'U',
 
	'I',
 
	'O',
 
};
 

	
 
void PlaceProc_DemolishArea(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_X_AND_Y | GUI_PlaceProc_DemolishArea);
 
}
 

	
 
static void PlaceProc_RaiseLand(TileIndex tile)
 
{
 
	DoCommandP(
 
		tile, SLOPE_N, 1, CcTerraform,
 
		CMD_TERRAFORM_LAND | CMD_AUTO | CMD_MSG(STR_0808_CAN_T_RAISE_LAND_HERE)
 
	);
 
}
 

	
 
static void PlaceProc_LowerLand(TileIndex tile)
 
{
 
	DoCommandP(
 
		tile, SLOPE_N, 0, CcTerraform,
 
		CMD_TERRAFORM_LAND | CMD_AUTO | CMD_MSG(STR_0809_CAN_T_LOWER_LAND_HERE)
 
	);
 
}
 

	
 
void PlaceProc_LevelLand(TileIndex tile)
 
{
 
	VpStartPlaceSizing(tile, VPM_X_AND_Y | GUI_PlaceProc_LevelArea);
 
}
 

	
 
static void TerraformClick_Lower(Window *w)
 
{
 
	HandlePlacePushButton(w, 4, ANIMCURSOR_LOWERLAND, 2, PlaceProc_LowerLand);
 
}
 

	
 
static void TerraformClick_Raise(Window *w)
 
{
 
	HandlePlacePushButton(w, 5, ANIMCURSOR_RAISELAND, 2, PlaceProc_RaiseLand);
 
}
 

	
 
static void TerraformClick_Level(Window *w)
 
{
 
	HandlePlacePushButton(w, 6, SPR_CURSOR_LEVEL_LAND, 2, PlaceProc_LevelLand);
 
}
 

	
 
static void TerraformClick_Dynamite(Window *w)
 
{
 
	HandlePlacePushButton(w, 7, ANIMCURSOR_DEMOLISH , 1, PlaceProc_DemolishArea);
 
}
 

	
 
static void TerraformClick_BuyLand(Window *w)
 
{
 
	HandlePlacePushButton(w, 8, SPR_CURSOR_BUY_LAND, 1, PlaceProc_BuyLand);
 
}
 

	
 
static void TerraformClick_Trees(Window *w)
 
{
 
	/* This button is NOT a place-push-button, so don't treat it as such */
 
	ShowBuildTreesToolbar();
 
}
 

	
 
static void TerraformClick_PlaceSign(Window *w)
 
{
 
	HandlePlacePushButton(w, 10, SPR_CURSOR_SIGN, 1, PlaceProc_Sign);
 
}
 

	
 
static OnButtonClick * const _terraform_button_proc[] = {
 
	TerraformClick_Lower,
 
	TerraformClick_Raise,
 
	TerraformClick_Level,
 
	TerraformClick_Dynamite,
 
	TerraformClick_BuyLand,
 
	TerraformClick_Trees,
 
	TerraformClick_PlaceSign,
 
};
 

	
 
static void TerraformToolbWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		if (e->we.click.widget >= 4) _terraform_button_proc[e->we.click.widget - 4](w);
 
		break;
 

	
 
	case WE_KEYPRESS: {
 
		uint i;
 

	
 
		for (i = 0; i != lengthof(_terraform_keycodes); i++) {
 
			if (e->we.keypress.keycode == _terraform_keycodes[i]) {
 
				e->we.keypress.cont = false;
 
				_terraform_button_proc[i](w);
 
				break;
 
			}
 
		}
 
		break;
 
	}
 

	
 
	case WE_PLACE_OBJ:
 
		_place_proc(e->we.place.tile);
 
		return;
 

	
 
	case WE_PLACE_DRAG:
 
		VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata & 0xF);
 
		break;
 

	
 
	case WE_PLACE_MOUSEUP:
 
		if (e->we.place.pt.x != -1 &&
 
				(e->we.place.userdata & 0xF) == VPM_X_AND_Y) { // dragged actions
 
			GUIPlaceProcDragXY(e);
 
		}
 
		break;
 

	
 
	case WE_ABORT_PLACE_OBJ:
 
		RaiseWindowButtons(w);
 
		break;
 
	}
 
}
 

	
 
static const Widget _terraform_widgets[] = {
 
{ WWT_CLOSEBOX,   RESIZE_NONE,     7,   0,  10,   0,  13, STR_00C5,                STR_018B_CLOSE_WINDOW},
 
{  WWT_CAPTION,   RESIZE_NONE,     7,  11, 145,   0,  13, STR_LANDSCAPING_TOOLBAR, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{WWT_STICKYBOX,   RESIZE_NONE,     7, 146, 157,   0,  13, STR_NULL,                STR_STICKY_BUTTON},
 

	
 
{    WWT_PANEL,   RESIZE_NONE,     7,  66,  69,  14,  35, 0x0,                    STR_NULL},
 
{   WWT_IMGBTN,   RESIZE_NONE,     7,   0,  21,  14,  35, SPR_IMG_TERRAFORM_DOWN,  STR_018E_LOWER_A_CORNER_OF_LAND},
 
{   WWT_IMGBTN,   RESIZE_NONE,     7,  22,  43,  14,  35, SPR_IMG_TERRAFORM_UP,    STR_018F_RAISE_A_CORNER_OF_LAND},
 
{   WWT_IMGBTN,   RESIZE_NONE,     7,  44,  65,  14,  35, SPR_IMG_LEVEL_LAND,      STR_LEVEL_LAND_TOOLTIP},
 
{   WWT_IMGBTN,   RESIZE_NONE,     7,  70,  91,  14,  35, SPR_IMG_DYNAMITE,        STR_018D_DEMOLISH_BUILDINGS_ETC},
 
{   WWT_IMGBTN,   RESIZE_NONE,     7,  92, 113,  14,  35, SPR_IMG_BUY_LAND,        STR_0329_PURCHASE_LAND_FOR_FUTURE},
 
{   WWT_IMGBTN,   RESIZE_NONE,     7, 114, 135,  14,  35, SPR_IMG_PLANTTREES,      STR_0185_PLANT_TREES_PLACE_SIGNS},
 
{   WWT_IMGBTN,   RESIZE_NONE,     7, 136, 157,  14,  35, SPR_IMG_PLACE_SIGN,      STR_0289_PLACE_SIGN},
 

	
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _terraform_desc = {
 
	WDP_ALIGN_TBR, 22+36, 158, 36,
 
	WC_SCEN_LAND_GEN, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
 
	_terraform_widgets,
 
	TerraformToolbWndProc
 
};
 

	
 
void ShowTerraformToolbar(void)
 
{
 
	if (!IsValidPlayer(_current_player)) return;
 
	AllocateWindowDescFront(&_terraform_desc, 0);
 
}
src/texteff.c
Show inline comments
 
deleted file
src/texteff.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "macros.h"
 
#include "strings.h"
 
#include "gfx.h"
 
#include "viewport.h"
 
#include "saveload.h"
 
#include "hal.h"
 
#include "console.h"
 
#include "string.h"
 
#include "variables.h"
 
#include "table/sprites.h"
 
#include <stdarg.h> /* va_list */
 
#include "date.h"
 

	
 
enum {
 
	MAX_TEXTMESSAGE_LENGTH = 150,
 
	MAX_TEXT_MESSAGES      =  30,
 
	MAX_CHAT_MESSAGES      =  10,
 
	MAX_ANIMATED_TILES     = 256,
 
};
 

	
 
typedef struct TextEffect {
 
	StringID string_id;
 
	int32 x;
 
	int32 y;
 
	int32 right;
 
	int32 bottom;
 
	uint16 duration;
 
	uint32 params_1;
 
	uint32 params_2;
 
} TextEffect;
 

	
 

	
 
typedef struct TextMessage {
 
	char message[MAX_TEXTMESSAGE_LENGTH];
 
	uint16 color;
 
	Date end_date;
 
} TextMessage;
 

	
 
static TextEffect _text_effect_list[MAX_TEXT_MESSAGES];
 
static TextMessage _textmsg_list[MAX_CHAT_MESSAGES];
 
TileIndex _animated_tile_list[MAX_ANIMATED_TILES];
 

	
 
static bool _textmessage_dirty = false;
 
static bool _textmessage_visible = false;
 

	
 
/* The chatbox grows from the bottom so the coordinates are pixels from
 
 * the left and pixels from the bottom. The height is the maximum height */
 
static const Oblong _textmsg_box = {10, 30, 500, 150};
 
static Pixel _textmessage_backup[150 * 500]; // (height * width)
 

	
 
extern void memcpy_pitch(void *dst, void *src, int w, int h, int srcpitch, int dstpitch);
 

	
 
static inline uint GetTextMessageCount(void)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
 
		if (_textmsg_list[i].message[0] == '\0') break;
 
	}
 

	
 
	return i;
 
}
 

	
 
/* Add a text message to the 'chat window' to be shown
 
 * @param color The colour this message is to be shown in
 
 * @param duration The duration of the chat message in game-days
 
 * @param message message itself in printf() style */
 
void CDECL AddTextMessage(uint16 color, uint8 duration, const char *message, ...)
 
{
 
	char buf[MAX_TEXTMESSAGE_LENGTH];
 
	const char *bufp;
 
	va_list va;
 
	uint msg_count;
 
	uint16 lines;
 

	
 
	va_start(va, message);
 
	vsnprintf(buf, lengthof(buf), message, va);
 
	va_end(va);
 

	
 
	/* Force linebreaks for strings that are too long */
 
	lines = GB(FormatStringLinebreaks(buf, _textmsg_box.width - 8), 0, 16) + 1;
 
	if (lines >= MAX_CHAT_MESSAGES) return;
 

	
 
	msg_count = GetTextMessageCount();
 
	/* We want to add more chat messages than there is free space for, remove 'old' */
 
	if (lines > MAX_CHAT_MESSAGES - msg_count) {
 
		int i = lines - (MAX_CHAT_MESSAGES - msg_count);
 
		memmove(&_textmsg_list[0], &_textmsg_list[i], sizeof(_textmsg_list[0]) * (msg_count - i));
 
		msg_count = MAX_CHAT_MESSAGES - lines;
 
	}
 

	
 
	for (bufp = buf; lines != 0; lines--) {
 
		TextMessage *tmsg = &_textmsg_list[msg_count++];
 
		ttd_strlcpy(tmsg->message, bufp, sizeof(tmsg->message));
 

	
 
		/* The default colour for a message is player colour. Replace this with
 
		 * white for any additional lines */
 
		tmsg->color = (bufp == buf && color & IS_PALETTE_COLOR) ? color : (0x1D - 15) | IS_PALETTE_COLOR;
 
		tmsg->end_date = _date + duration;
 

	
 
		bufp += strlen(bufp) + 1; // jump to 'next line' in the formatted string
 
	}
 

	
 
	_textmessage_dirty = true;
 
}
 

	
 
void InitTextMessage(void)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
 
		_textmsg_list[i].message[0] = '\0';
 
	}
 
}
 

	
 
/* Hide the textbox */
 
void UndrawTextMessage(void)
 
{
 
	if (_textmessage_visible) {
 
		/* Sometimes we also need to hide the cursor
 
		 *   This is because both textmessage and the cursor take a shot of the
 
		 *   screen before drawing.
 
		 *   Now the textmessage takes his shot and paints his data before the cursor
 
		 *   does, so in the shot of the cursor is the screen-data of the textmessage
 
		 *   included when the cursor hangs somewhere over the textmessage. To
 
		 *   avoid wrong repaints, we undraw the cursor in that case, and everything
 
		 *   looks nicely ;)
 
		 * (and now hope this story above makes sense to you ;))
 
		 */
 

	
 
		if (_cursor.visible) {
 
			if (_cursor.draw_pos.x + _cursor.draw_size.x >= _textmsg_box.x &&
 
				_cursor.draw_pos.x <= _textmsg_box.x + _textmsg_box.width &&
 
				_cursor.draw_pos.y + _cursor.draw_size.y >= _screen.height - _textmsg_box.y - _textmsg_box.height &&
 
				_cursor.draw_pos.y <= _screen.height - _textmsg_box.y) {
 
				UndrawMouseCursor();
 
			}
 
		}
 

	
 
		_textmessage_visible = false;
 
		/* Put our 'shot' back to the screen */
 
		memcpy_pitch(
 
			_screen.dst_ptr + _textmsg_box.x + (_screen.height - _textmsg_box.y - _textmsg_box.height) * _screen.pitch,
 
			_textmessage_backup,
 
			_textmsg_box.width, _textmsg_box.height, _textmsg_box.width, _screen.pitch);
 

	
 
		/* And make sure it is updated next time */
 
		_video_driver->make_dirty(_textmsg_box.x, _screen.height - _textmsg_box.y - _textmsg_box.height, _textmsg_box.width, _textmsg_box.height);
 

	
 
		_textmessage_dirty = true;
 
	}
 
}
 

	
 
/* Check if a message is expired every day */
 
void TextMessageDailyLoop(void)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
 
		TextMessage *tmsg = &_textmsg_list[i];
 
		if (tmsg->message[0] == '\0') continue;
 

	
 
		/* Message has expired, remove from the list */
 
		if (tmsg->end_date < _date) {
 
			/* Move the remaining messages over the current message */
 
			if (i != MAX_CHAT_MESSAGES - 1) memmove(tmsg, tmsg + 1, sizeof(*tmsg) * (MAX_CHAT_MESSAGES - i - 1));
 

	
 
			/* Mark the last item as empty */
 
			_textmsg_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0';
 
			_textmessage_dirty = true;
 

	
 
			/* Go one item back, because we moved the array 1 to the left */
 
			i--;
 
		}
 
	}
 
}
 

	
 
/* Draw the textmessage-box */
 
void DrawTextMessage(void)
 
{
 
	uint y, count;
 

	
 
	if (!_textmessage_dirty) return;
 

	
 
	/* First undraw if needed */
 
	UndrawTextMessage();
 

	
 
	if (_iconsole_mode == ICONSOLE_FULL) return;
 

	
 
	/* Check if we have anything to draw at all */
 
	count = GetTextMessageCount();
 
	if (count == 0) return;
 

	
 
	/* Make a copy of the screen as it is before painting (for undraw) */
 
	memcpy_pitch(
 
		_textmessage_backup,
 
		_screen.dst_ptr + _textmsg_box.x + (_screen.height - _textmsg_box.y - _textmsg_box.height) * _screen.pitch,
 
		_textmsg_box.width, _textmsg_box.height, _screen.pitch, _textmsg_box.width);
 

	
 
	_cur_dpi = &_screen; // switch to _screen painting
 

	
 
	/* Paint a half-transparent box behind the text messages */
 
	GfxFillRect(
 
			_textmsg_box.x,
 
			_screen.height - _textmsg_box.y - count * 13 - 2,
 
			_textmsg_box.x + _textmsg_box.width - 1,
 
			_screen.height - _textmsg_box.y - 2,
 
			0x322 | USE_COLORTABLE // black, but with some alpha for background
 
		);
 

	
 
	/* Paint the messages starting with the lowest at the bottom */
 
	for (y = 13; count-- != 0; y += 13) {
 
		DoDrawString(_textmsg_list[count].message, _textmsg_box.x + 3, _screen.height - _textmsg_box.y - y + 1, _textmsg_list[count].color);
 
 	}
 

	
 
	/* Make sure the data is updated next flush */
 
	_video_driver->make_dirty(_textmsg_box.x, _screen.height - _textmsg_box.y - _textmsg_box.height, _textmsg_box.width, _textmsg_box.height);
 

	
 
	_textmessage_visible = true;
 
	_textmessage_dirty = false;
 
}
 

	
 
static void MarkTextEffectAreaDirty(TextEffect *te)
 
{
 
	MarkAllViewportsDirty(
 
		te->x,
 
		te->y - 1,
 
		(te->right - te->x)*2 + te->x + 1,
 
		(te->bottom - (te->y - 1)) * 2 + (te->y - 1) + 1
 
	);
 
}
 

	
 
void AddTextEffect(StringID msg, int x, int y, uint16 duration)
 
{
 
	TextEffect *te;
 
	int w;
 
	char buffer[100];
 

	
 
	if (_game_mode == GM_MENU) return;
 

	
 
	for (te = _text_effect_list; te->string_id != INVALID_STRING_ID; ) {
 
		if (++te == endof(_text_effect_list)) return;
 
	}
 

	
 
	te->string_id = msg;
 
	te->duration = duration;
 
	te->y = y - 5;
 
	te->bottom = y + 5;
 
	te->params_1 = GetDParam(0);
 
	te->params_2 = GetDParam(4);
 

	
 
	GetString(buffer, msg, lastof(buffer));
 
	w = GetStringBoundingBox(buffer).width;
 

	
 
	te->x = x - (w >> 1);
 
	te->right = x + (w >> 1) - 1;
 
	MarkTextEffectAreaDirty(te);
 
}
 

	
 
static void MoveTextEffect(TextEffect *te)
 
{
 
	if (te->duration < 8) {
 
		te->string_id = INVALID_STRING_ID;
 
	} else {
 
		te->duration -= 8;
 
		te->y--;
 
		te->bottom--;
 
	}
 
	MarkTextEffectAreaDirty(te);
 
}
 

	
 
void MoveAllTextEffects(void)
 
{
 
	TextEffect *te;
 

	
 
	for (te = _text_effect_list; te != endof(_text_effect_list); te++) {
 
		if (te->string_id != INVALID_STRING_ID) MoveTextEffect(te);
 
	}
 
}
 

	
 
void InitTextEffects(void)
 
{
 
	TextEffect *te;
 

	
 
	for (te = _text_effect_list; te != endof(_text_effect_list); te++) {
 
		te->string_id = INVALID_STRING_ID;
 
	}
 
}
 

	
 
void DrawTextEffects(DrawPixelInfo *dpi)
 
{
 
	const TextEffect* te;
 

	
 
	switch (dpi->zoom) {
 
		case 0:
 
			for (te = _text_effect_list; te != endof(_text_effect_list); te++) {
 
				if (te->string_id != INVALID_STRING_ID &&
 
						dpi->left <= te->right &&
 
						dpi->top  <= te->bottom &&
 
						dpi->left + dpi->width  > te->x &&
 
						dpi->top  + dpi->height > te->y) {
 
					AddStringToDraw(te->x, te->y, te->string_id, te->params_1, te->params_2);
 
				}
 
			}
 
			break;
 

	
 
		case 1:
 
			for (te = _text_effect_list; te != endof(_text_effect_list); te++) {
 
				if (te->string_id != INVALID_STRING_ID &&
 
						dpi->left <= te->right  * 2 - te->x &&
 
						dpi->top  <= te->bottom * 2 - te->y &&
 
						dpi->left + dpi->width  > te->x &&
 
						dpi->top  + dpi->height > te->y) {
 
					AddStringToDraw(te->x, te->y, (StringID)(te->string_id-1), te->params_1, te->params_2);
 
				}
 
			}
 
			break;
 
	}
 
}
 

	
 
void DeleteAnimatedTile(TileIndex tile)
 
{
 
	TileIndex *ti;
 

	
 
	for (ti = _animated_tile_list; ti != endof(_animated_tile_list); ti++) {
 
		if (tile == *ti) {
 
			/* remove the hole */
 
			memmove(ti, ti + 1, (lastof(_animated_tile_list) - ti) * sizeof(*ti));
 
			/* and clear last item */
 
			*lastof(_animated_tile_list) = 0;
 
			MarkTileDirtyByTile(tile);
 
			return;
 
		}
 
	}
 
}
 

	
 
bool AddAnimatedTile(TileIndex tile)
 
{
 
	TileIndex *ti;
 

	
 
	for (ti = _animated_tile_list; ti != endof(_animated_tile_list); ti++) {
 
		if (tile == *ti || *ti == 0) {
 
			*ti = tile;
 
			MarkTileDirtyByTile(tile);
 
			return true;
 
		}
 
	}
 

	
 
	return false;
 
}
 

	
 
void AnimateAnimatedTiles(void)
 
{
 
	const TileIndex* ti;
 

	
 
	for (ti = _animated_tile_list; ti != endof(_animated_tile_list) && *ti != 0; ti++) {
 
		AnimateTile(*ti);
 
	}
 
}
 

	
 
void InitializeAnimatedTiles(void)
 
{
 
	memset(_animated_tile_list, 0, sizeof(_animated_tile_list));
 
}
 

	
 
static void SaveLoad_ANIT(void)
 
{
 
	/* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */
 
	if (CheckSavegameVersion(6)) {
 
		SlArray(_animated_tile_list, lengthof(_animated_tile_list), SLE_FILE_U16 | SLE_VAR_U32);
 
	} else {
 
		SlArray(_animated_tile_list, lengthof(_animated_tile_list), SLE_UINT32);
 
	}
 
}
 

	
 

	
 
const ChunkHandler _animated_tile_chunk_handlers[] = {
 
	{ 'ANIT', SaveLoad_ANIT, SaveLoad_ANIT, CH_RIFF | CH_LAST},
 
};
src/tgp.c
Show inline comments
 
deleted file
src/tgp.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include <math.h>
 
#include "openttd.h"
 
#include "clear_map.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "table/strings.h"
 
#include "clear_map.h"
 
#include "tile.h"
 
#include "variables.h"
 
#include "void_map.h"
 
#include "tgp.h"
 
#include "console.h"
 
#include "genworld.h"
 

	
 
/*
 
 * OTTD Perlin Noise Landscape Generator, aka TerraGenesis Perlin
 
 *
 
 * Quickie guide to Perlin Noise
 
 * Perlin noise is a predictable pseudo random number sequence. By generating
 
 * it in 2 dimensions, it becomes a useful random map, that for a given seed
 
 * and starting X & Y is entirely predictable. On the face of it, that may not
 
 * be useful. However, it means that if you want to replay a map in a different
 
 * terrain, or just vary the sea level, you just re-run the generator with the
 
 * same seed. The seed is an int32, and is randomised on each run of New Game.
 
 * The Scenario Generator does not randomise the value, so that you can
 
 * experiment with one terrain until you are happy, or click "Random" for a new
 
 * random seed.
 
 *
 
 * Perlin Noise is a series of "octaves" of random noise added together. By
 
 * reducing the amplitude of the noise with each octave, the first octave of
 
 * noise defines the main terrain sweep, the next the ripples on that, and the
 
 * next the ripples on that. I use 6 octaves, with the amplitude controlled by
 
 * a power ratio, usually known as a persistence or p value. This I vary by the
 
 * smoothness selection, as can be seen in the table below. The closer to 1,
 
 * the more of that octave is added. Each octave is however raised to the power
 
 * of its position in the list, so the last entry in the "smooth" row, 0.35, is
 
 * raised to the power of 6, so can only add 0.001838...  of the amplitude to
 
 * the running total.
 
 *
 
 * In other words; the first p value sets the general shape of the terrain, the
 
 * second sets the major variations to that, ... until finally the smallest
 
 * bumps are added.
 
 *
 
 * Usefully, this routine is totally scaleable; so when 32bpp comes along, the
 
 * terrain can be as bumpy as you like! It is also infinitely expandable; a
 
 * single random seed terrain continues in X & Y as far as you care to
 
 * calculate. In theory, we could use just one seed value, but randomly select
 
 * where in the Perlin XY space we use for the terrain. Personally I prefer
 
 * using a simple (0, 0) to (X, Y), with a varying seed.
 
 *
 
 *
 
 * Other things i have had to do: mountainous wasnt mountainous enough, and
 
 * since we only have 0..15 heights available, I add a second generated map
 
 * (with a modified seed), onto the original. This generally raises the
 
 * terrain, which then needs scaling back down. Overall effect is a general
 
 * uplift.
 
 *
 
 * However, the values on the top of mountains are then almost guaranteed to go
 
 * too high, so large flat plateaus appeared at height 15. To counter this, I
 
 * scale all heights above 12 to proportion up to 15. It still makes the
 
 * mountains have flatish tops, rather than craggy peaks, but at least they
 
 * arent smooth as glass.
 
 *
 
 *
 
 * For a full discussion of Perlin Noise, please visit:
 
 * http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
 
 *
 
 *
 
 * Evolution II
 
 *
 
 * The algorithm as described in the above link suggests to compute each tile height
 
 * as composition of several noise waves. Some of them are computed directly by
 
 * noise(x, y) function, some are calculated using linear approximation. Our
 
 * first implementation of perlin_noise_2D() used 4 noise(x, y) calls plus
 
 * 3 linear interpolations. It was called 6 times for each tile. This was a bit
 
 * CPU expensive.
 
 *
 
 * The following implementation uses optimized algorithm that should produce
 
 * the same quality result with much less computations, but more memory accesses.
 
 * The overal speedup should be 300% to 800% depending on CPU and memory speed.
 
 *
 
 * I will try to explain it on the example below:
 
 *
 
 * Have a map of 4 x 4 tiles, our simplifiead noise generator produces only two
 
 * values -1 and +1, use 3 octaves with wave lenght 1, 2 and 4, with amplitudes
 
 * 3, 2, 1. Original algorithm produces:
 
 *
 
 * h00 = lerp(lerp(-3, 3, 0/4), lerp(3, -3, 0/4), 0/4) + lerp(lerp(-2,  2, 0/2), lerp( 2, -2, 0/2), 0/2) + -1 = lerp(-3.0,  3.0, 0/4) + lerp(-2,  2, 0/2) + -1 = -3.0  + -2 + -1 = -6.0
 
 * h01 = lerp(lerp(-3, 3, 1/4), lerp(3, -3, 1/4), 0/4) + lerp(lerp(-2,  2, 1/2), lerp( 2, -2, 1/2), 0/2) +  1 = lerp(-1.5,  1.5, 0/4) + lerp( 0,  0, 0/2) +  1 = -1.5  +  0 +  1 = -0.5
 
 * h02 = lerp(lerp(-3, 3, 2/4), lerp(3, -3, 2/4), 0/4) + lerp(lerp( 2, -2, 0/2), lerp(-2,  2, 0/2), 0/2) + -1 = lerp(   0,    0, 0/4) + lerp( 2, -2, 0/2) + -1 =    0  +  2 + -1 =  1.0
 
 * h03 = lerp(lerp(-3, 3, 3/4), lerp(3, -3, 3/4), 0/4) + lerp(lerp( 2, -2, 1/2), lerp(-2,  2, 1/2), 0/2) +  1 = lerp( 1.5, -1.5, 0/4) + lerp( 0,  0, 0/2) +  1 =  1.5  +  0 +  1 =  2.5
 
 *
 
 * h10 = lerp(lerp(-3, 3, 0/4), lerp(3, -3, 0/4), 1/4) + lerp(lerp(-2,  2, 0/2), lerp( 2, -2, 0/2), 1/2) +  1 = lerp(-3.0,  3.0, 1/4) + lerp(-2,  2, 1/2) +  1 = -1.5  +  0 +  1 = -0.5
 
 * h11 = lerp(lerp(-3, 3, 1/4), lerp(3, -3, 1/4), 1/4) + lerp(lerp(-2,  2, 1/2), lerp( 2, -2, 1/2), 1/2) + -1 = lerp(-1.5,  1.5, 1/4) + lerp( 0,  0, 1/2) + -1 = -0.75 +  0 + -1 = -1.75
 
 * h12 = lerp(lerp(-3, 3, 2/4), lerp(3, -3, 2/4), 1/4) + lerp(lerp( 2, -2, 0/2), lerp(-2,  2, 0/2), 1/2) +  1 = lerp(   0,    0, 1/4) + lerp( 2, -2, 1/2) +  1 =    0  +  0 +  1 =  1.0
 
 * h13 = lerp(lerp(-3, 3, 3/4), lerp(3, -3, 3/4), 1/4) + lerp(lerp( 2, -2, 1/2), lerp(-2,  2, 1/2), 1/2) + -1 = lerp( 1.5, -1.5, 1/4) + lerp( 0,  0, 1/2) + -1 =  0.75 +  0 + -1 = -0.25
 
 *
 
 *
 
 * Optimization 1:
 
 *
 
 * 1) we need to allocate a bit more tiles: (size_x + 1) * (size_y + 1) = (5 * 5):
 
 *
 
 * 2) setup corner values using amplitude 3
 
 * {    -3.0        X          X          X          3.0   }
 
 * {     X          X          X          X          X     }
 
 * {     X          X          X          X          X     }
 
 * {     X          X          X          X          X     }
 
 * {     3.0        X          X          X         -3.0   }
 
 *
 
 * 3a) interpolate values in the middle
 
 * {    -3.0        X          0.0        X          3.0   }
 
 * {     X          X          X          X          X     }
 
 * {     0.0        X          0.0        X          0.0   }
 
 * {     X          X          X          X          X     }
 
 * {     3.0        X          0.0        X         -3.0   }
 
 *
 
 * 3b) add patches with amplitude 2 to them
 
 * {    -5.0        X          2.0        X          1.0   }
 
 * {     X          X          X          X          X     }
 
 * {     2.0        X         -2.0        X          2.0   }
 
 * {     X          X          X          X          X     }
 
 * {     1.0        X          2.0        X         -5.0   }
 
 *
 
 * 4a) interpolate values in the middle
 
 * {    -5.0       -1.5        2.0        1.5        1.0   }
 
 * {    -1.5       -0.75       0.0        0.75       1.5   }
 
 * {     2.0        0.0       -2.0        0.0        2.0   }
 
 * {     1.5        0.75       0.0       -0.75      -1.5   }
 
 * {     1.0        1.5        2.0       -1.5       -5.0   }
 
 *
 
 * 4b) add patches with amplitude 1 to them
 
 * {    -6.0       -0.5        1.0        2.5        0.0   }
 
 * {    -0.5       -1.75       1.0       -0.25       2.5   }
 
 * {     1.0        1.0       -3.0        1.0        1.0   }
 
 * {     2.5       -0.25       1.0       -1.75      -0.5   }
 
 * {     0.0        2.5        1.0       -0.5       -6.0   }
 
 *
 
 *
 
 *
 
 * Optimization 2:
 
 *
 
 * As you can see above, each noise function was called just once. Therefore
 
 * we don't need to use noise function that calculates the noise from x, y and
 
 * some prime. The same quality result we can obtain using standard Random()
 
 * function instead.
 
 *
 
 */
 

	
 
#ifndef M_PI_2
 
#define M_PI_2 1.57079632679489661923
 
#define M_PI   3.14159265358979323846
 
#endif /* M_PI_2 */
 

	
 
/** Fixed point type for heights */
 
typedef int16 height_t;
 
static const int height_decimal_bits = 4;
 
static const height_t _invalid_height = -32768;
 

	
 
/** Fixed point array for amplitudes (and percent values) */
 
typedef int amplitude_t;
 
static const int amplitude_decimal_bits = 10;
 

	
 
/** Height map - allocated array of heights (MapSizeX() + 1) x (MapSizeY() + 1) */
 
typedef struct HeightMap
 
{
 
	height_t *h;         //! array of heights
 
	uint     dim_x;      //! height map size_x MapSizeX() + 1
 
	uint     total_size; //! height map total size
 
	uint     size_x;     //! MapSizeX()
 
	uint     size_y;     //! MapSizeY()
 
} HeightMap;
 

	
 
/** Global height map instance */
 
static HeightMap _height_map = {NULL, 0, 0, 0, 0};
 

	
 
/** Height map accessors */
 
#define HeightMapXY(x, y) _height_map.h[(x) + (y) * _height_map.dim_x]
 

	
 
/** Conversion: int to height_t */
 
#define I2H(i) ((i) << height_decimal_bits)
 
/** Conversion: height_t to int */
 
#define H2I(i) ((i) >> height_decimal_bits)
 

	
 
/** Conversion: int to amplitude_t */
 
#define I2A(i) ((i) << amplitude_decimal_bits)
 
/** Conversion: amplitude_t to int */
 
#define A2I(i) ((i) >> amplitude_decimal_bits)
 

	
 
/** Conversion: amplitude_t to height_t */
 
#define A2H(a) ((height_decimal_bits < amplitude_decimal_bits) \
 
	? ((a) >> (amplitude_decimal_bits - height_decimal_bits)) \
 
	: ((a) << (height_decimal_bits - amplitude_decimal_bits)))
 

	
 
/** Walk through all items of _height_map.h */
 
#define FOR_ALL_TILES_IN_HEIGHT(h) for (h = _height_map.h; h < &_height_map.h[_height_map.total_size]; h++)
 

	
 
/** Noise amplitudes (multiplied by 1024)
 
 * - indexed by "smoothness setting" and log2(frequency) */
 
static const amplitude_t _amplitudes_by_smoothness_and_frequency[4][12] = {
 
	// Very smooth
 
	{1000,  350,  123,   43,   15,    1,     1,    0,    0,    0,    0,    0},
 
	// Smooth
 
	{1000, 1000,  403,  200,   64,    8,     1,    0,    0,    0,    0,    0},
 
	// Rough
 
	{1000, 1200,  800,  500,  200,   16,     4,    0,    0,    0,    0,    0},
 
	// Very Rough
 
	{1500, 1000, 1200, 1000,  500,   32,    20,    0,    0,    0,    0,    0},
 
};
 

	
 
/** Desired water percentage (100% == 1024) - indexed by _opt.diff.quantity_sea_lakes */
 
static const amplitude_t _water_percent[4] = {20, 80, 250, 400};
 

	
 
/** Desired maximum height - indexed by _opt.diff.terrain_type */
 
static const int8 _max_height[4] = {
 
	6,       // Very flat
 
	9,       // Flat
 
	12,      // Hilly
 
	15       // Mountainous
 
};
 

	
 
/** Check if a X/Y set are within the map. */
 
static inline bool IsValidXY(uint x, uint y)
 
{
 
	return ((int)x) >= 0 && x < _height_map.size_x && ((int)y) >= 0 && y < _height_map.size_y;
 
}
 

	
 

	
 
/** Allocate array of (MapSizeX()+1)*(MapSizeY()+1) heights and init the _height_map structure members */
 
static inline bool AllocHeightMap(void)
 
{
 
	height_t *h;
 

	
 
	_height_map.size_x = MapSizeX();
 
	_height_map.size_y = MapSizeY();
 

	
 
	/* Allocate memory block for height map row pointers */
 
	_height_map.total_size = (_height_map.size_x + 1) * (_height_map.size_y + 1);
 
	_height_map.dim_x = _height_map.size_x + 1;
 
	_height_map.h = calloc(_height_map.total_size, sizeof(*_height_map.h));
 
	if (_height_map.h == NULL) return false;
 

	
 
	/* Iterate through height map initialize values */
 
	FOR_ALL_TILES_IN_HEIGHT(h) *h = _invalid_height;
 

	
 
	return true;
 
}
 

	
 
/** Free height map */
 
static inline void FreeHeightMap(void)
 
{
 
	if (_height_map.h == NULL) return;
 
	free(_height_map.h);
 
	_height_map.h = NULL;
 
}
 

	
 
/** RandomHeight() generator */
 
static inline height_t RandomHeight(amplitude_t rMax)
 
{
 
	amplitude_t ra = (Random() << 16) | (Random() & 0x0000FFFF);
 
	height_t rh;
 
	/* Scale the amplitude for better resolution */
 
	rMax *= 16;
 
	/* Spread height into range -rMax..+rMax */
 
	rh = A2H(ra % (2 * rMax + 1) - rMax);
 
	return rh;
 
}
 

	
 
/** One interpolation and noise round */
 
static bool ApplyNoise(uint log_frequency, amplitude_t amplitude)
 
{
 
	uint size_min = min(_height_map.size_x, _height_map.size_y);
 
	uint step = size_min >> log_frequency;
 
	uint x, y;
 

	
 
	assert(_height_map.h != NULL);
 

	
 
	/* Are we finished? */
 
	if (step == 0) return false;
 

	
 
	if (log_frequency == 0) {
 
		/* This is first round, we need to establish base heights with step = size_min */
 
		for (y = 0; y <= _height_map.size_y; y += step) {
 
			for (x = 0; x <= _height_map.size_x; x += step) {
 
				height_t height = (amplitude > 0) ? RandomHeight(amplitude) : 0;
 
				HeightMapXY(x, y) = height;
 
			}
 
		}
 
		return true;
 
	}
 

	
 
	/* It is regular iteration round.
 
	 * Interpolate height values at odd x, even y tiles */
 
	for (y = 0; y <= _height_map.size_y; y += 2 * step) {
 
		for (x = 0; x < _height_map.size_x; x += 2 * step) {
 
			height_t h00 = HeightMapXY(x + 0 * step, y);
 
			height_t h02 = HeightMapXY(x + 2 * step, y);
 
			height_t h01 = (h00 + h02) / 2;
 
			HeightMapXY(x + 1 * step, y) = h01;
 
		}
 
	}
 

	
 
	/* Interpolate height values at odd y tiles */
 
	for (y = 0; y < _height_map.size_y; y += 2 * step) {
 
		for (x = 0; x <= _height_map.size_x; x += step) {
 
			height_t h00 = HeightMapXY(x, y + 0 * step);
 
			height_t h20 = HeightMapXY(x, y + 2 * step);
 
			height_t h10 = (h00 + h20) / 2;
 
			HeightMapXY(x, y + 1 * step) = h10;
 
		}
 
	}
 

	
 
	for (y = 0; y <= _height_map.size_y; y += step) {
 
		for (x = 0; x <= _height_map.size_x; x += step) {
 
			HeightMapXY(x, y) += RandomHeight(amplitude);
 
		}
 
	}
 
	return (step > 1);
 
}
 

	
 
/** Base Perlin noise generator - fills height map with raw Perlin noise */
 
static void HeightMapGenerate(void)
 
{
 
	uint size_min = min(_height_map.size_x, _height_map.size_y);
 
	uint iteration_round = 0;
 
	amplitude_t amplitude;
 
	bool continue_iteration;
 
	uint log_size_min, log_frequency_min;
 
	int log_frequency;
 

	
 
	/* Find first power of two that fits */
 
	for (log_size_min = 6; (1U << log_size_min) < size_min; log_size_min++) { }
 
	log_frequency_min = log_size_min - 6;
 

	
 
	do {
 
		log_frequency = iteration_round - log_frequency_min;
 
		if (log_frequency >= 0) {
 
			amplitude = _amplitudes_by_smoothness_and_frequency[_patches.tgen_smoothness][log_frequency];
 
		} else {
 
			amplitude = 0;
 
		}
 
		continue_iteration = ApplyNoise(iteration_round, amplitude);
 
		iteration_round++;
 
	} while(continue_iteration);
 
}
 

	
 
/** Returns min, max and average height from height map */
 
static void HeightMapGetMinMaxAvg(height_t *min_ptr, height_t *max_ptr, height_t *avg_ptr)
 
{
 
	height_t h_min, h_max, h_avg, *h;
 
	int64 h_accu = 0;
 
	h_min = h_max = HeightMapXY(0, 0);
 

	
 
	/* Get h_min, h_max and accumulate heights into h_accu */
 
	FOR_ALL_TILES_IN_HEIGHT(h) {
 
		if (*h < h_min) h_min = *h;
 
		if (*h > h_max) h_max = *h;
 
		h_accu += *h;
 
	}
 

	
 
	/* Get average height */
 
	h_avg = (height_t)(h_accu / (_height_map.size_x * _height_map.size_y));
 

	
 
	/* Return required results */
 
	if (min_ptr != NULL) *min_ptr = h_min;
 
	if (max_ptr != NULL) *max_ptr = h_max;
 
	if (avg_ptr != NULL) *avg_ptr = h_avg;
 
}
 

	
 
/** Dill histogram and return pointer to its base point - to the count of zero heights */
 
static int *HeightMapMakeHistogram(height_t h_min, height_t h_max, int *hist_buf)
 
{
 
	int *hist = hist_buf - h_min;
 
	height_t *h;
 

	
 
	/* Fill histogram */
 
	FOR_ALL_TILES_IN_HEIGHT(h) {
 
		assert(*h >= h_min);
 
		assert(*h <= h_max);
 
		hist[*h]++;
 
	}
 
	return hist;
 
}
 

	
 
/** Applies sine wave redistribution onto height map */
 
static void HeightMapSineTransform(height_t h_min, height_t h_max)
 
{
 
	height_t *h;
 

	
 
	FOR_ALL_TILES_IN_HEIGHT(h) {
 
		double fheight;
 

	
 
		if (*h < h_min) continue;
 

	
 
		/* Transform height into 0..1 space */
 
		fheight = (double)(*h - h_min) / (double)(h_max - h_min);
 
		/* Apply sine transform depending on landscape type */
 
		switch(_opt.landscape) {
 
			case LT_CANDY:
 
			case LT_NORMAL:
 
				/* Move and scale 0..1 into -1..+1 */
 
				fheight = 2 * fheight - 1;
 
				/* Sine transform */
 
				fheight = sin(fheight * M_PI_2);
 
				/* Transform it back from -1..1 into 0..1 space */
 
				fheight = 0.5 * (fheight + 1);
 
				break;
 

	
 
			case LT_HILLY:
 
				{
 
					/* Arctic terrain needs special height distribution.
 
					 * Redistribute heights to have more tiles at highest (75%..100%) range */
 
					double sine_upper_limit = 0.75;
 
					double linear_compression = 2;
 
					if (fheight >= sine_upper_limit) {
 
						/* Over the limit we do linear compression up */
 
						fheight = 1.0 - (1.0 - fheight) / linear_compression;
 
					} else {
 
						double m = 1.0 - (1.0 - sine_upper_limit) / linear_compression;
 
						/* Get 0..sine_upper_limit into -1..1 */
 
						fheight = 2.0 * fheight / sine_upper_limit - 1.0;
 
						/* Sine wave transform */
 
						fheight = sin(fheight * M_PI_2);
 
						/* Get -1..1 back to 0..(1 - (1 - sine_upper_limit) / linear_compression) == 0.0..m */
 
						fheight = 0.5 * (fheight + 1.0) * m;
 
					}
 
				}
 
				break;
 

	
 
			case LT_DESERT:
 
				{
 
					/* Desert terrain needs special height distribution.
 
					 * Half of tiles should be at lowest (0..25%) heights */
 
					double sine_lower_limit = 0.5;
 
					double linear_compression = 2;
 
					if (fheight <= sine_lower_limit) {
 
						/* Under the limit we do linear compression down */
 
						fheight = fheight / linear_compression;
 
					} else {
 
						double m = sine_lower_limit / linear_compression;
 
						/* Get sine_lower_limit..1 into -1..1 */
 
						fheight = 2.0 * ((fheight - sine_lower_limit) / (1.0 - sine_lower_limit)) - 1.0;
 
						/* Sine wave transform */
 
						fheight = sin(fheight * M_PI_2);
 
						/* Get -1..1 back to (sine_lower_limit / linear_compression)..1.0 */
 
						fheight = 0.5 * ((1.0 - m) * fheight + (1.0 + m));
 
					}
 
				}
 
				break;
 

	
 
			default:
 
				NOT_REACHED();
 
				break;
 
		}
 
		/* Transform it back into h_min..h_max space */
 
		*h = fheight * (h_max - h_min) + h_min;
 
		if (*h < 0) *h = I2H(0);
 
		if (*h >= h_max) *h = h_max - 1;
 
	}
 
}
 

	
 
/** Adjusts heights in height map to contain required amount of water tiles */
 
static void HeightMapAdjustWaterLevel(amplitude_t water_percent, height_t h_max_new)
 
{
 
	height_t h_min, h_max, h_avg, h_water_level;
 
	int water_tiles, desired_water_tiles;
 
	height_t *h;
 
	int *hist_buf, *hist;
 

	
 
	HeightMapGetMinMaxAvg(&h_min, &h_max, &h_avg);
 

	
 
	/* Allocate histogram buffer and clear its cells */
 
	hist_buf = calloc(h_max - h_min + 1, sizeof(*hist_buf));
 
	/* Fill histogram */
 
	hist = HeightMapMakeHistogram(h_min, h_max, hist_buf);
 

	
 
	/* How many water tiles do we want? */
 
	desired_water_tiles = (int)(((int64)water_percent) * (int64)(_height_map.size_x * _height_map.size_y)) >> amplitude_decimal_bits;
 

	
 
	/* Raise water_level and accumulate values from histogram until we reach required number of water tiles */
 
	for (h_water_level = h_min, water_tiles = 0; h_water_level < h_max; h_water_level++) {
 
		water_tiles += hist[h_water_level];
 
		if (water_tiles >= desired_water_tiles) break;
 
	}
 

	
 
	/* We now have the proper water level value.
 
	 * Transform the height map into new (normalized) height map:
 
	 *   values from range: h_min..h_water_level will become negative so it will be clamped to 0
 
	 *   values from range: h_water_level..h_max are transformed into 0..h_max_new
 
	 * , where h_max_new is 4, 8, 12 or 16 depending on terrain type (very flat, flat, hilly, mountains)
 
	 */
 
	FOR_ALL_TILES_IN_HEIGHT(h) {
 
		/* Transform height from range h_water_level..h_max into 0..h_max_new range */
 
		*h = (height_t)(((int)h_max_new) * (*h - h_water_level) / (h_max - h_water_level)) + I2H(1);
 
		/* Make sure all values are in the proper range (0..h_max_new) */
 
		if (*h < 0) *h = I2H(0);
 
		if (*h >= h_max_new) *h = h_max_new - 1;
 
	}
 

	
 
	free(hist_buf);
 
}
 

	
 
static double perlin_coast_noise_2D(const double x, const double y, const double p, const int prime);
 

	
 
/**
 
 * This routine sculpts in from the edge a random amount, again a Perlin
 
 * sequence, to avoid the rigid flat-edge slopes that were present before. The
 
 * Perlin noise map doesnt know where we are going to slice across, and so we
 
 * often cut straight through high terrain. the smoothing routine makes it
 
 * legal, gradually increasing up from the edge to the original terrain height.
 
 * By cutting parts of this away, it gives a far more irregular edge to the
 
 * map-edge. Sometimes it works beautifully with the existing sea & lakes, and
 
 * creates a very realistic coastline. Other times the variation is less, and
 
 * the map-edge shows its cliff-like roots.
 
 *
 
 * This routine may be extended to randomly sculpt the height of the terrain
 
 * near the edge. This will have the coast edge at low level (1-3), rising in
 
 * smoothed steps inland to about 15 tiles in. This should make it look as
 
 * though the map has been built for the map size, rather than a slice through
 
 * a larger map.
 
 *
 
 * Please note that all the small numbers; 53, 101, 167, etc. are small primes
 
 * to help give the perlin noise a bit more of a random feel.
 
 */
 
static void HeightMapCoastLines(void)
 
{
 
	int smallest_size = min(_patches.map_x, _patches.map_y);
 
	const int margin = 4;
 
	uint y, x;
 
	uint max_x;
 
	uint max_y;
 

	
 
	/* Lower to sea level */
 
	for (y = 0; y <= _height_map.size_y; y++) {
 
		/* Top right */
 
		max_x = myabs((perlin_coast_noise_2D(_height_map.size_y - y, y, 0.9, 53) + 0.25) * 5 + (perlin_coast_noise_2D(y, y, 0.35, 179) + 1) * 12);
 
		max_x = max((smallest_size * smallest_size / 16) + max_x, (smallest_size * smallest_size / 16) + margin - max_x);
 
		if (smallest_size < 8 && max_x > 5) max_x /= 1.5;
 
		for (x = 0; x < max_x; x++) {
 
			HeightMapXY(x, y) = 0;
 
		}
 

	
 
		/* Bottom left */
 
		max_x = myabs((perlin_coast_noise_2D(_height_map.size_y - y, y, 0.85, 101) + 0.3) * 6 + (perlin_coast_noise_2D(y, y, 0.45,  67) + 0.75) * 8);
 
		max_x = max((smallest_size * smallest_size / 16) + max_x, (smallest_size * smallest_size / 16) + margin - max_x);
 
		if (smallest_size < 8 && max_x > 5) max_x /= 1.5;
 
		for (x = _height_map.size_x; x > (_height_map.size_x - 1 - max_x); x--) {
 
			HeightMapXY(x, y) = 0;
 
		}
 
	}
 

	
 
	/* Lower to sea level */
 
	for (x = 0; x <= _height_map.size_x; x++) {
 
		/* Top left */
 
		max_y = myabs((perlin_coast_noise_2D(x, _height_map.size_y / 2, 0.9, 167) + 0.4) * 5 + (perlin_coast_noise_2D(x, _height_map.size_y / 3, 0.4, 211) + 0.7) * 9);
 
		max_y = max((smallest_size * smallest_size / 16) + max_y, (smallest_size * smallest_size / 16) + margin - max_y);
 
		if (smallest_size < 8 && max_y > 5) max_y /= 1.5;
 
		for (y = 0; y < max_y; y++) {
 
			HeightMapXY(x, y) = 0;
 
		}
 

	
 

	
 
		/* Bottom right */
 
		max_y = myabs((perlin_coast_noise_2D(x, _height_map.size_y / 3, 0.85, 71) + 0.25) * 6 + (perlin_coast_noise_2D(x, _height_map.size_y / 3, 0.35, 193) + 0.75) * 12);
 
		max_y = max((smallest_size * smallest_size / 16) + max_y, (smallest_size * smallest_size / 16) + margin - max_y);
 
		if (smallest_size < 8 && max_y > 5) max_y /= 1.5;
 
		for (y = _height_map.size_y; y > (_height_map.size_y - 1 - max_y); y--) {
 
			HeightMapXY(x, y) = 0;
 
		}
 
	}
 
}
 

	
 
/** Start at given point, move in given direction, find and Smooth coast in that direction */
 
static void HeightMapSmoothCoastInDirection(int org_x, int org_y, int dir_x, int dir_y)
 
{
 
	const int max_coast_dist_from_edge = 35;
 
	const int max_coast_Smooth_depth = 35;
 

	
 
	int x, y;
 
	int ed; // coast distance from edge
 
	int depth;
 

	
 
	height_t h_prev = 16;
 
	height_t h;
 

	
 
	assert(IsValidXY(org_x, org_y));
 

	
 
	/* Search for the coast (first non-water tile) */
 
	for (x = org_x, y = org_y, ed = 0; IsValidXY(x, y) && ed < max_coast_dist_from_edge; x += dir_x, y += dir_y, ed++) {
 
		/* Coast found? */
 
		if (HeightMapXY(x, y) > 15) break;
 

	
 
		/* Coast found in the neighborhood? */
 
		if (IsValidXY(x + dir_y, y + dir_x) && HeightMapXY(x + dir_y, y + dir_x) > 0) break;
 

	
 
		/* Coast found in the neighborhood on the other side */
 
		if (IsValidXY(x - dir_y, y - dir_x) && HeightMapXY(x - dir_y, y - dir_x) > 0) break;
 
	}
 

	
 
	/* Coast found or max_coast_dist_from_edge has been reached.
 
	 * Soften the coast slope */
 
	for (depth = 0; IsValidXY(x, y) && depth <= max_coast_Smooth_depth; depth++, x += dir_x, y += dir_y) {
 
		h = HeightMapXY(x, y);
 
		h = min(h, h_prev + (4 + depth)); // coast softening formula
 
		HeightMapXY(x, y) = h;
 
		h_prev = h;
 
	}
 
}
 

	
 
/** Smooth coasts by modulating height of tiles close to map edges with cosine of distance from edge */
 
static void HeightMapSmoothCoasts(void)
 
{
 
	uint x, y;
 
	/* First Smooth NW and SE coasts (y close to 0 and y close to size_y) */
 
	for (x = 0; x < _height_map.size_x; x++) {
 
		HeightMapSmoothCoastInDirection(x, 0, 0, 1);
 
		HeightMapSmoothCoastInDirection(x, _height_map.size_y - 1, 0, -1);
 
	}
 
	/* First Smooth NE and SW coasts (x close to 0 and x close to size_x) */
 
	for (y = 0; y < _height_map.size_y; y++) {
 
		HeightMapSmoothCoastInDirection(0, y, 1, 0);
 
		HeightMapSmoothCoastInDirection(_height_map.size_x - 1, y, -1, 0);
 
	}
 
}
 

	
 
/**
 
 * This routine provides the essential cleanup necessary before OTTD can
 
 * display the terrain. When generated, the terrain heights can jump more than
 
 * one level between tiles. This routine smooths out those differences so that
 
 * the most it can change is one level. When OTTD can support cliffs, this
 
 * routine may not be necessary.
 
 */
 
static void HeightMapSmoothSlopes(height_t dh_max)
 
{
 
	int x, y;
 
	for (y = 1; y <= (int)_height_map.size_y; y++) {
 
		for (x = 1; x <= (int)_height_map.size_x; x++) {
 
			height_t h_max = min(HeightMapXY(x - 1, y), HeightMapXY(x, y - 1)) + dh_max;
 
			if (HeightMapXY(x, y) > h_max) HeightMapXY(x, y) = h_max;
 
		}
 
	}
 
	for (y = _height_map.size_y - 1; y >= 0; y--) {
 
		for (x = _height_map.size_x - 1; x >= 0; x--) {
 
			height_t h_max = min(HeightMapXY(x + 1, y), HeightMapXY(x, y + 1)) + dh_max;
 
			if (HeightMapXY(x, y) > h_max) HeightMapXY(x, y) = h_max;
 
		}
 
	}
 
}
 

	
 
/** Height map terraform post processing:
 
 *  - water level adjusting
 
 *  - coast Smoothing
 
 *  - slope Smoothing
 
 *  - height histogram redistribution by sine wave transform */
 
static void HeightMapNormalize(void)
 
{
 
	const amplitude_t water_percent = _water_percent[_opt.diff.quantity_sea_lakes];
 
	const height_t h_max_new = I2H(_max_height[_opt.diff.terrain_type]);
 
	const height_t roughness = 7 + 3 * _patches.tgen_smoothness;
 

	
 
	HeightMapAdjustWaterLevel(water_percent, h_max_new);
 

	
 
	HeightMapCoastLines();
 
	HeightMapSmoothSlopes(roughness);
 

	
 
	HeightMapSmoothCoasts();
 
	HeightMapSmoothSlopes(roughness);
 

	
 
	HeightMapSineTransform(12, h_max_new);
 
	HeightMapSmoothSlopes(16);
 
}
 

	
 
static inline int perlin_landXY(uint x, uint y)
 
{
 
	return HeightMapXY(x, y);
 
}
 

	
 

	
 
/* The following decimals are the octave power modifiers for the Perlin noise */
 
static const double _perlin_p_values[][7] = {    // perlin frequency per power
 
	{ 0.35, 0.35, 0.35, 0.35, 0.35, 0.25, 0.539 }, // Very smooth
 
	{ 0.45, 0.55, 0.45, 0.45, 0.35, 0.25, 0.89  }, // Smooth
 
	{ 0.85, 0.80, 0.70, 0.45, 0.45, 0.35, 1.825 }, // Rough 1.825
 
	{ 0.95, 0.85, 0.80, 0.55, 0.55, 0.45, 2.245 }  // Very Rough 2.25
 
};
 

	
 
/**
 
 * The Perlin Noise calculation using large primes
 
 * The initial number is adjusted by two values; the generation_seed, and the
 
 * passed parameter; prime.
 
 * prime is used to allow the perlin noise generator to create useful random
 
 * numbers from slightly different series.
 
 */
 
static double int_noise(const long x, const long y, const int prime)
 
{
 
	long n = x + y * prime + _patches.generation_seed;
 

	
 
	n = (n << 13) ^ n;
 

	
 
	/* Pseudo-random number generator, using several large primes */
 
	return 1.0 - (double)((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0;
 
}
 

	
 

	
 
/**
 
 * Hj. Malthaner's routine included 2 different noise smoothing methods.
 
 * We now use the "raw" int_noise one.
 
 * However, it may be useful to move to the other routine in future.
 
 * So it is included too.
 
 */
 
static double smoothed_noise(const int x, const int y, const int prime)
 
{
 
#if 0
 
	/* A hilly world (four corner smooth) */
 
	const double sides = int_noise(x - 1, y) + int_noise(x + 1, y) + int_noise(x, y - 1) + int_noise(x, y + 1);
 
	const double center  =  int_noise(x, y);
 
	return (sides + sides + center * 4) / 8.0;
 
#endif
 

	
 
	/* This gives very hilly world */
 
	return int_noise(x, y, prime);
 
}
 

	
 

	
 
/**
 
 * This routine determines the interpolated value between a and b
 
 */
 
static inline double linear_interpolate(const double a, const double b, const double x)
 
{
 
	return a + x * (b - a);
 
}
 

	
 

	
 
/**
 
 * This routine returns the smoothed interpolated noise for an x and y, using
 
 * the values from the surrounding positions.
 
 */
 
static double interpolated_noise(const double x, const double y, const int prime)
 
{
 
	const int integer_X = (int)x;
 
	const int integer_Y = (int)y;
 

	
 
	const double fractional_X = x - (double)integer_X;
 
	const double fractional_Y = y - (double)integer_Y;
 

	
 
	const double v1 = smoothed_noise(integer_X,     integer_Y,     prime);
 
	const double v2 = smoothed_noise(integer_X + 1, integer_Y,     prime);
 
	const double v3 = smoothed_noise(integer_X,     integer_Y + 1, prime);
 
	const double v4 = smoothed_noise(integer_X + 1, integer_Y + 1, prime);
 

	
 
	const double i1 = linear_interpolate(v1, v2, fractional_X);
 
	const double i2 = linear_interpolate(v3, v4, fractional_X);
 

	
 
	return linear_interpolate(i1, i2, fractional_Y);
 
}
 

	
 

	
 
/**
 
 * This is a similar function to the main perlin noise calculation, but uses
 
 * the value p passed as a parameter rather than selected from the predefined
 
 * sequences. as you can guess by its title, i use this to create the indented
 
 * coastline, which is just another perlin sequence.
 
 */
 
static double perlin_coast_noise_2D(const double x, const double y, const double p, const int prime)
 
{
 
	double total = 0.0;
 
	int i;
 

	
 
	for (i = 0; i < 6; i++) {
 
		const double frequency = (double)(1 << i);
 
		const double amplitude = pow(p, (double)i);
 

	
 
		total += interpolated_noise((x * frequency) / 64.0, (y * frequency) / 64.0, prime) * amplitude;
 
	}
 

	
 
	return total;
 
}
 

	
 

	
 
/** A small helper function */
 
static void TgenSetTileHeight(TileIndex tile, int height)
 
{
 
	SetTileHeight(tile, height);
 
	MakeClear(tile, CLEAR_GRASS, 3);
 
}
 

	
 
/**
 
 * The main new land generator using Perlin noise. Desert landscape is handled
 
 * different to all others to give a desert valley between two high mountains.
 
 * Clearly if a low height terrain (flat/very flat) is chosen, then the tropic
 
 * areas wont be high enough, and there will be very little tropic on the map.
 
 * Thus Tropic works best on Hilly or Mountainous.
 
 */
 
void GenerateTerrainPerlin(void)
 
{
 
	uint x, y;
 

	
 
	if (!AllocHeightMap()) return;
 
	GenerateWorldSetAbortCallback(FreeHeightMap);
 

	
 
	HeightMapGenerate();
 

	
 
	IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 

	
 
	HeightMapNormalize();
 

	
 
	IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 

	
 
	/* Transfer height map into OTTD map */
 
	for (y = 2; y < _height_map.size_y - 2; y++) {
 
		for (x = 2; x < _height_map.size_x - 2; x++) {
 
			int height = H2I(HeightMapXY(x, y));
 
			if (height < 0) height = 0;
 
			if (height > 15) height = 15;
 
			TgenSetTileHeight(TileXY(x, y), height);
 
		}
 
	}
 

	
 
	IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 

	
 
	/* Recreate void tiles at the border in case they have been affected by generation */
 
	for (y = 0; y < _height_map.size_y - 1; y++) MakeVoid(_height_map.size_x * y + _height_map.size_x - 1);
 
	for (x = 0; x < _height_map.size_x;     x++) MakeVoid(_height_map.size_x * y + x);
 

	
 
	FreeHeightMap();
 
	GenerateWorldSetAbortCallback(NULL);
 
}
src/thread.c
Show inline comments
 
deleted file
src/thread.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "thread.h"
 
#include <stdlib.h>
 

	
 
#if defined(__AMIGA__) || defined(__MORPHOS__) || defined(NO_THREADS)
 
OTTDThread *OTTDCreateThread(OTTDThreadFunc function, void *arg) { return NULL; }
 
void *OTTDJoinThread(OTTDThread *t) { return NULL; }
 
void OTTDExitThread(void) { NOT_REACHED(); };
 

	
 
#elif defined(__OS2__)
 

	
 
#define INCL_DOS
 
#include <os2.h>
 
#include <process.h>
 

	
 
struct OTTDThread {
 
	TID thread;
 
	OTTDThreadFunc func;
 
	void* arg;
 
	void* ret;
 
};
 

	
 
static void Proxy(void* arg)
 
{
 
	OTTDThread* t = arg;
 
	t->ret = t->func(t->arg);
 
}
 

	
 
OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
 
{
 
	OTTDThread* t = malloc(sizeof(*t));
 

	
 
	if (t == NULL) return NULL;
 

	
 
	t->func = function;
 
	t->arg  = arg;
 
	t->thread = _beginthread(Proxy, NULL, 32768, t);
 
	if (t->thread != -1) {
 
		return t;
 
	} else {
 
		free(t);
 
		return NULL;
 
	}
 
}
 

	
 
void* OTTDJoinThread(OTTDThread* t)
 
{
 
	void* ret;
 

	
 
	if (t == NULL) return NULL;
 

	
 
	DosWaitThread(&t->thread, DCWW_WAIT);
 
	ret = t->ret;
 
	free(t);
 
	return ret;
 
}
 

	
 
void OTTDExitThread(void)
 
{
 
	_endthread();
 
}
 

	
 
#elif defined(UNIX)
 

	
 
#include <pthread.h>
 

	
 
struct OTTDThread {
 
	pthread_t thread;
 
};
 

	
 
OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
 
{
 
	OTTDThread* t = malloc(sizeof(*t));
 

	
 
	if (t == NULL) return NULL;
 

	
 
	if (pthread_create(&t->thread, NULL, function, arg) == 0) {
 
		return t;
 
	} else {
 
		free(t);
 
		return NULL;
 
	}
 
}
 

	
 
void* OTTDJoinThread(OTTDThread* t)
 
{
 
	void* ret;
 

	
 
	if (t == NULL) return NULL;
 

	
 
	pthread_join(t->thread, &ret);
 
	free(t);
 
	return ret;
 
}
 

	
 
void OTTDExitThread(void)
 
{
 
	pthread_exit(NULL);
 
}
 

	
 
#elif defined(WIN32)
 

	
 
#include <windows.h>
 

	
 
struct OTTDThread {
 
	HANDLE thread;
 
	OTTDThreadFunc func;
 
	void* arg;
 
	void* ret;
 
};
 

	
 
static DWORD WINAPI Proxy(LPVOID arg)
 
{
 
	OTTDThread* t = arg;
 
	t->ret = t->func(t->arg);
 
	return 0;
 
}
 

	
 
OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg)
 
{
 
	OTTDThread* t = malloc(sizeof(*t));
 
	DWORD dwThreadId;
 

	
 
	if (t == NULL) return NULL;
 

	
 
	t->func = function;
 
	t->arg  = arg;
 
	t->thread = CreateThread(NULL, 0, Proxy, t, 0, &dwThreadId);
 

	
 
	if (t->thread != NULL) {
 
		return t;
 
	} else {
 
		free(t);
 
		return NULL;
 
	}
 
}
 

	
 
void* OTTDJoinThread(OTTDThread* t)
 
{
 
	void* ret;
 

	
 
	if (t == NULL) return NULL;
 

	
 
	WaitForSingleObject(t->thread, INFINITE);
 
	CloseHandle(t->thread);
 
	ret = t->ret;
 
	free(t);
 
	return ret;
 
}
 

	
 
void OTTDExitThread(void)
 
{
 
	ExitThread(0);
 
}
 
#endif
src/tile.c
Show inline comments
 
deleted file
src/tile.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "tile.h"
 

	
 
Slope GetTileSlope(TileIndex tile, uint *h)
 
{
 
	uint a;
 
	uint b;
 
	uint c;
 
	uint d;
 
	uint min;
 
	uint r;
 

	
 
	assert(tile < MapSize());
 

	
 
	if (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY()) {
 
		if (h != NULL) *h = 0;
 
		return 0;
 
	}
 

	
 
	min = a = TileHeight(tile);
 
	b = TileHeight(tile + TileDiffXY(1, 0));
 
	if (min >= b) min = b;
 
	c = TileHeight(tile + TileDiffXY(0, 1));
 
	if (min >= c) min = c;
 
	d = TileHeight(tile + TileDiffXY(1, 1));
 
	if (min >= d) min = d;
 

	
 
	r = SLOPE_FLAT;
 
	if ((a -= min) != 0) r += (--a << 4) + SLOPE_N;
 
	if ((c -= min) != 0) r += (--c << 4) + SLOPE_E;
 
	if ((d -= min) != 0) r += (--d << 4) + SLOPE_S;
 
	if ((b -= min) != 0) r += (--b << 4) + SLOPE_W;
 

	
 
	if (h != NULL) *h = min * TILE_HEIGHT;
 

	
 
	return r;
 
}
 

	
 
uint GetTileZ(TileIndex tile)
 
{
 
	uint h;
 
	GetTileSlope(tile, &h);
 
	return h;
 
}
 

	
 

	
 
uint GetTileMaxZ(TileIndex t)
 
{
 
	uint max;
 
	uint h;
 

	
 
	h = TileHeight(t);
 
	max = h;
 
	h = TileHeight(t + TileDiffXY(1, 0));
 
	if (h > max) max = h;
 
	h = TileHeight(t + TileDiffXY(0, 1));
 
	if (h > max) max = h;
 
	h = TileHeight(t + TileDiffXY(1, 1));
 
	if (h > max) max = h;
 
	return max * 8;
 
}
src/town_cmd.c
Show inline comments
 
deleted file
src/town_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "strings.h"
 
#include "road_map.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "town_map.h"
 
#include "tunnel_map.h"
 
#include "viewport.h"
 
#include "town.h"
 
#include "command.h"
 
#include "gfx.h"
 
#include "industry.h"
 
#include "station.h"
 
#include "player.h"
 
#include "news.h"
 
#include "saveload.h"
 
#include "economy.h"
 
#include "gui.h"
 
#include "unmovable_map.h"
 
#include "water_map.h"
 
#include "variables.h"
 
#include "bridge.h"
 
#include "bridge_map.h"
 
#include "date.h"
 
#include "table/town_land.h"
 
#include "genworld.h"
 

	
 
/**
 
 * Called if a new block is added to the town-pool
 
 */
 
static void TownPoolNewBlock(uint start_item)
 
{
 
	Town *t;
 

	
 
	/* 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 (t = GetTown(start_item); t != NULL; t = (t->index + 1U < GetTownPoolSize()) ? GetTown(t->index + 1U) : NULL) t->index = start_item++;
 
}
 

	
 
/* Initialize the town-pool */
 
DEFINE_OLD_POOL(Town, Town, TownPoolNewBlock, NULL)
 

	
 
void DestroyTown(Town *t)
 
{
 
	Industry *i;
 
	TileIndex tile;
 

	
 
	/* Delete town authority window
 
	 * and remove from list of sorted towns */
 
	DeleteWindowById(WC_TOWN_VIEW, t->index);
 
	_town_sort_dirty = true;
 
	_total_towns--;
 

	
 
	/* Delete all industries belonging to the town */
 
	FOR_ALL_INDUSTRIES(i) if (i->town == t) DeleteIndustry(i);
 

	
 
	/* Go through all tiles and delete those belonging to the town */
 
	for (tile = 0; tile < MapSize(); ++tile) {
 
		switch (GetTileType(tile)) {
 
			case MP_HOUSE:
 
				if (GetTownByTile(tile) == t) DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				break;
 

	
 
			case MP_STREET:
 
			case MP_TUNNELBRIDGE:
 
				if (IsTileOwner(tile, OWNER_TOWN) &&
 
						ClosestTownFromTile(tile, (uint)-1) == t)
 
					DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				break;
 

	
 
			default:
 
				break;
 
		}
 
	}
 

	
 
	DeleteName(t->townnametype);
 
	DeleteSubsidyWithTown(t->index);
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
// Local
 
static int _grow_town_result;
 

	
 
static bool BuildTownHouse(Town *t, TileIndex tile);
 
static void ClearTownHouse(Town *t, TileIndex tile);
 
static void DoBuildTownHouse(Town *t, TileIndex tile);
 

	
 
static void TownDrawHouseLift(const TileInfo *ti)
 
{
 
	AddChildSpriteScreen(SPR_LIFT, 14, 60 - GetLiftPosition(ti->tile));
 
}
 

	
 
typedef void TownDrawTileProc(const TileInfo *ti);
 
static TownDrawTileProc * const _town_draw_tile_procs[1] = {
 
	TownDrawHouseLift
 
};
 

	
 

	
 
static void DrawTile_Town(TileInfo *ti)
 
{
 
	const DrawBuildingsTileStruct *dcts;
 
	uint32 image;
 

	
 
	/* Retrieve pointer to the draw town tile struct */
 
	{
 
		/* this "randomizes" on the (up to) 4 variants of a building */
 
		uint variant;
 
		variant  = ti->x >> 4;
 
		variant ^= ti->x >> 6;
 
		variant ^= ti->y >> 4;
 
		variant -= ti->y >> 6;
 
		variant &= 3;
 
		dcts = &_town_draw_tile_data[GetHouseType(ti->tile) << 4 | variant << 2 | GetHouseBuildingStage(ti->tile)];
 
	}
 

	
 
	if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh);
 
	DrawGroundSprite(dcts->ground);
 

	
 
	/* Add a house on top of the ground? */
 
	image = dcts->building;
 
	if (image != 0) {
 
		if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 

	
 
		AddSortableSpriteToDraw(image,
 
			ti->x + dcts->subtile_x,
 
			ti->y + dcts->subtile_y,
 
			dcts->width + 1,
 
			dcts->height + 1,
 
			dcts->dz,
 
			ti->z
 
		);
 

	
 
		if (_display_opt & DO_TRANS_BUILDINGS) return;
 
	}
 

	
 
	{
 
		int proc = dcts->draw_proc - 1;
 

	
 
		if (proc >= 0) _town_draw_tile_procs[proc](ti);
 
	}
 
}
 

	
 
static uint GetSlopeZ_Town(TileIndex tile, uint x, uint y)
 
{
 
	return GetTileMaxZ(tile);
 
}
 

	
 
static Slope GetSlopeTileh_Town(TileIndex tile, Slope tileh)
 
{
 
	return SLOPE_FLAT;
 
}
 

	
 
static void AnimateTile_Town(TileIndex tile)
 
{
 
	int pos, dest;
 

	
 
	if (_tick_counter & 3) return;
 

	
 
	// If the house is not one with a lift anymore, then stop this animating.
 
	// Not exactly sure when this happens, but probably when a house changes.
 
	// Before this was just a return...so it'd leak animated tiles..
 
	// That bug seems to have been here since day 1??
 
	if (!(_housetype_extra_flags[GetHouseType(tile)] & 0x20)) {
 
		DeleteAnimatedTile(tile);
 
		return;
 
	}
 

	
 
	if (!IsLiftMoving(tile)) {
 
		int i;
 

	
 
		/** Building has 6 floors, number 0 .. 6, where 1 is illegal.
 
		 *  This is due to the fact that the first floor is, in the graphics,
 
		 *  the height of 2 'normal' floors.
 
		 *  Furthermore, there are 6 lift positions from floor N (incl) to floor N + 1 (excl) */
 
		do {
 
			i = (Random() & 7) - 1;
 
		} while (i < 0 || i == 1 || i * 6 == GetLiftPosition(tile));
 

	
 
		SetLiftDestination(tile, i);
 
	}
 

	
 
	pos = GetLiftPosition(tile);
 
	dest = GetLiftDestination(tile) * 6;
 
	pos += (pos < dest) ? 1 : -1;
 
	SetLiftPosition(tile, pos);
 

	
 
	if (pos == dest) HaltLift(tile);
 

	
 
	MarkTileDirtyByTile(tile);
 
}
 

	
 
static void UpdateTownRadius(Town *t);
 

	
 
static bool IsCloseToTown(TileIndex tile, uint dist)
 
{
 
	const Town* t;
 

	
 
	FOR_ALL_TOWNS(t) {
 
		if (DistanceManhattan(tile, t->xy) < dist) return true;
 
	}
 
	return false;
 
}
 

	
 
static void MarkTownSignDirty(Town *t)
 
{
 
	MarkAllViewportsDirty(
 
		t->sign.left-6,
 
		t->sign.top-3,
 
		t->sign.left+t->sign.width_1*4+12,
 
		t->sign.top + 45
 
	);
 
}
 

	
 
void UpdateTownVirtCoord(Town *t)
 
{
 
	Point pt;
 

	
 
	MarkTownSignDirty(t);
 
	pt = RemapCoords2(TileX(t->xy) * TILE_SIZE, TileY(t->xy) * TILE_SIZE);
 
	SetDParam(0, t->index);
 
	SetDParam(1, t->population);
 
	UpdateViewportSignPos(&t->sign, pt.x, pt.y - 24,
 
		_patches.population_in_label ? STR_TOWN_LABEL_POP : STR_TOWN_LABEL);
 
	MarkTownSignDirty(t);
 
}
 

	
 
static void ChangePopulation(Town *t, int mod)
 
{
 
	t->population += mod;
 
	InvalidateWindow(WC_TOWN_VIEW, t->index);
 
	UpdateTownVirtCoord(t);
 

	
 
	if (_town_sort_order & 2) _town_sort_dirty = true;
 
}
 

	
 
uint32 GetWorldPopulation(void)
 
{
 
	uint32 pop;
 
	const Town* t;
 

	
 
	pop = 0;
 
	FOR_ALL_TOWNS(t) pop += t->population;
 
	return pop;
 
}
 

	
 
static void MakeSingleHouseBigger(TileIndex tile)
 
{
 
	assert(IsTileType(tile, MP_HOUSE));
 

	
 
	if (LiftHasDestination(tile)) return;
 

	
 
	IncHouseConstructionTick(tile);
 
	if (GetHouseConstructionTick(tile) != 0) return;
 

	
 
	IncHouseBuildingStage(tile);  /*increase construction stage of one more step*/
 

	
 
	if (GetHouseBuildingStage(tile) == TOWN_HOUSE_COMPLETED){
 
		/*Now, construction is completed.  Can add population of building to the town*/
 
		ChangePopulation(GetTownByTile(tile), _housetype_population[GetHouseType(tile)]);
 
	}
 
	MarkTileDirtyByTile(tile);
 
}
 

	
 
static void MakeTownHouseBigger(TileIndex tile)
 
{
 
	uint flags = _house_more_flags[GetHouseType(tile)];
 
	if (flags & 8) MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 0));
 
	if (flags & 4) MakeSingleHouseBigger(TILE_ADDXY(tile, 0, 1));
 
	if (flags & 2) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 0));
 
	if (flags & 1) MakeSingleHouseBigger(TILE_ADDXY(tile, 1, 1));
 
}
 

	
 
static void TileLoop_Town(TileIndex tile)
 
{
 
	int house;
 
	Town *t;
 
	uint32 r;
 

	
 
	if (GetHouseBuildingStage(tile) != TOWN_HOUSE_COMPLETED) {
 
		/*Construction is not completed. See if we can go further in construction*/
 
		MakeTownHouseBigger(tile);
 
		return;
 
	}
 

	
 
	house = GetHouseType(tile);
 
	if ((_housetype_extra_flags[house] & 0x20) && !LiftHasDestination(tile) && CHANCE16(1, 2) && AddAnimatedTile(tile)) BeginLiftMovement(tile);
 

	
 
	t = GetTownByTile(tile);
 

	
 
	r = Random();
 

	
 
	if (GB(r, 0, 8) < _housetype_population[house]) {
 
		uint amt = GB(r, 0, 8) / 8 + 1;
 
		uint moved;
 

	
 
		if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
 
		t->new_max_pass += amt;
 
		moved = MoveGoodsToStation(tile, 1, 1, CT_PASSENGERS, amt);
 
		t->new_act_pass += moved;
 
	}
 

	
 
	if (GB(r, 8, 8) < _housetype_mailamount[house] ) {
 
		uint amt = GB(r, 8, 8) / 8 + 1;
 
		uint moved;
 

	
 
		if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
 
		t->new_max_mail += amt;
 
		moved = MoveGoodsToStation(tile, 1, 1, CT_MAIL, amt);
 
		t->new_act_mail += moved;
 
	}
 

	
 
	if (_house_more_flags[house] & 8 && HASBIT(t->flags12, TOWN_IS_FUNDED) && --t->time_until_rebuild == 0) {
 
		t->time_until_rebuild = GB(r, 16, 6) + 130;
 

	
 
		_current_player = OWNER_TOWN;
 

	
 
		ClearTownHouse(t, tile);
 

	
 
		// rebuild with another house?
 
		if (GB(r, 24, 8) >= 12) DoBuildTownHouse(t, tile);
 

	
 
		_current_player = OWNER_NONE;
 
	}
 
}
 

	
 
static void ClickTile_Town(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static int32 ClearTile_Town(TileIndex tile, byte flags)
 
{
 
	int house, rating;
 
	int32 cost;
 
	Town *t;
 

	
 
	// safety checks
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 
	if (flags&DC_AUTO && !(flags&DC_AI_BUILDING)) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);
 

	
 
	house = GetHouseType(tile);
 
	cost = _price.remove_house * _housetype_remove_cost[house] >> 8;
 

	
 
	rating = _housetype_remove_ratingmod[house];
 
	_cleared_town_rating += rating;
 
	_cleared_town = t = GetTownByTile(tile);
 

	
 
	if (IsValidPlayer(_current_player)) {
 
		if (rating > t->ratings[_current_player] && !(flags & DC_NO_TOWN_RATING) && !_cheats.magic_bulldozer.value) {
 
			SetDParam(0, t->index);
 
			return_cmd_error(STR_2009_LOCAL_AUTHORITY_REFUSES);
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		ChangeTownRating(t, -rating, RATING_HOUSE_MINIMUM);
 
		ClearTownHouse(t, tile);
 
	}
 

	
 
	return cost;
 
}
 

	
 
static void GetAcceptedCargo_Town(TileIndex tile, AcceptedCargo ac)
 
{
 
	byte type = GetHouseType(tile);
 

	
 
	ac[CT_PASSENGERS] = _housetype_cargo_passengers[type];
 
	ac[CT_MAIL]       = _housetype_cargo_mail[type];
 
	ac[CT_GOODS]      = _housetype_cargo_goods[type];
 
	ac[CT_FOOD]       = _housetype_cargo_food[type];
 
}
 

	
 
static void GetTileDesc_Town(TileIndex tile, TileDesc *td)
 
{
 
	td->str = _town_tile_names[GetHouseType(tile)];
 
	if (GetHouseBuildingStage(tile) != TOWN_HOUSE_COMPLETED) {
 
		SetDParamX(td->dparam, 0, td->str);
 
		td->str = STR_2058_UNDER_CONSTRUCTION;
 
	}
 

	
 
	td->owner = OWNER_TOWN;
 
}
 

	
 
static uint32 GetTileTrackStatus_Town(TileIndex tile, TransportType mode)
 
{
 
	/* not used */
 
	return 0;
 
}
 

	
 
static void ChangeTileOwner_Town(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	/* not used */
 
}
 

	
 

	
 
static const TileIndexDiffC _roadblock_tileadd[] = {
 
	{ 0, -1},
 
	{ 1,  0},
 
	{ 0,  1},
 
	{-1,  0},
 

	
 
	// Store the first 3 elements again.
 
	// Lets us rotate without using &3.
 
	{ 0, -1},
 
	{ 1,  0},
 
	{ 0,  1}
 
};
 

	
 

	
 
static bool GrowTown(Town *t);
 

	
 
static void TownTickHandler(Town *t)
 
{
 
	if (HASBIT(t->flags12, TOWN_IS_FUNDED)) {
 
		int i = t->grow_counter - 1;
 
		if (i < 0) {
 
			if (GrowTown(t)) {
 
				i = t->growth_rate;
 
			} else {
 
				i = 0;
 
			}
 
		}
 
		t->grow_counter = i;
 
	}
 

	
 
	UpdateTownRadius(t);
 
}
 

	
 
void OnTick_Town(void)
 
{
 
	if (_game_mode == GM_EDITOR) return;
 

	
 
	/* Make sure each town's tickhandler invocation frequency is about the
 
	 * same - TOWN_GROWTH_FREQUENCY - independent on the number of towns. */
 
	for (_cur_town_iter += GetMaxTownIndex() + 1;
 
	     _cur_town_iter >= TOWN_GROWTH_FREQUENCY;
 
	     _cur_town_iter -= TOWN_GROWTH_FREQUENCY) {
 
		uint32 i = _cur_town_ctr;
 

	
 
		if (++_cur_town_ctr > GetMaxTownIndex())
 
			_cur_town_ctr = 0;
 

	
 
		if (IsValidTownID(i)) TownTickHandler(GetTown(i));
 
	}
 
}
 

	
 
static RoadBits GetTownRoadMask(TileIndex tile)
 
{
 
	TrackBits b = GetAnyRoadTrackBits(tile);
 
	RoadBits r = 0;
 

	
 
	if (b & TRACK_BIT_X)     r |= ROAD_X;
 
	if (b & TRACK_BIT_Y)     r |= ROAD_Y;
 
	if (b & TRACK_BIT_UPPER) r |= ROAD_NE | ROAD_NW;
 
	if (b & TRACK_BIT_LOWER) r |= ROAD_SE | ROAD_SW;
 
	if (b & TRACK_BIT_LEFT)  r |= ROAD_NW | ROAD_SW;
 
	if (b & TRACK_BIT_RIGHT) r |= ROAD_NE | ROAD_SE;
 
	return r;
 
}
 

	
 
static bool IsRoadAllowedHere(TileIndex tile, int dir)
 
{
 
	Slope k;
 
	Slope slope;
 

	
 
	// If this assertion fails, it might be because the world contains
 
	//  land at the edges. This is not ok.
 
	TILE_ASSERT(tile);
 

	
 
	for (;;) {
 
		// Check if there already is a road at this point?
 
		if (GetAnyRoadTrackBits(tile) == 0) {
 
			// No, try to build one in the direction.
 
			// if that fails clear the land, and if that fails exit.
 
			// This is to make sure that we can build a road here later.
 
			if (CmdFailed(DoCommand(tile, (dir & 1 ? ROAD_X : ROAD_Y), 0, DC_AUTO, CMD_BUILD_ROAD)) &&
 
					CmdFailed(DoCommand(tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR)))
 
				return false;
 
		}
 

	
 
		slope = GetTileSlope(tile, NULL);
 
		if (slope == SLOPE_FLAT) {
 
no_slope:
 
			// Tile has no slope
 
			// Disallow the road if any neighboring tile has a road.
 
			if (HASBIT(GetTownRoadMask(TILE_ADD(tile, ToTileIndexDiff(_roadblock_tileadd[dir+1]))), dir^2) ||
 
					HASBIT(GetTownRoadMask(TILE_ADD(tile, ToTileIndexDiff(_roadblock_tileadd[dir+3]))), dir^2) ||
 
					HASBIT(GetTownRoadMask(TILE_ADD(tile, ToTileIndexDiff(_roadblock_tileadd[dir+1]) + ToTileIndexDiff(_roadblock_tileadd[dir+2]))), dir) ||
 
					HASBIT(GetTownRoadMask(TILE_ADD(tile, ToTileIndexDiff(_roadblock_tileadd[dir+3]) + ToTileIndexDiff(_roadblock_tileadd[dir+2]))), dir))
 
				return false;
 

	
 
			// Otherwise allow
 
			return true;
 
		}
 

	
 
		// If the tile is not a slope in the right direction, then
 
		// maybe terraform some.
 
		k = (dir & 1) ? SLOPE_NE : SLOPE_NW;
 
		if (k != slope && ComplementSlope(k) != slope) {
 
			uint32 r = Random();
 

	
 
			if (CHANCE16I(1, 8, r) && !_generating_world) {
 
				int32 res;
 

	
 
				if (CHANCE16I(1, 16, r)) {
 
					res = DoCommand(tile, slope, 0, DC_EXEC | DC_AUTO | DC_NO_WATER,
 
					                      CMD_TERRAFORM_LAND);
 
				} else {
 
					res = DoCommand(tile, slope ^ 0xF, 1, DC_EXEC | DC_AUTO | DC_NO_WATER,
 
					                      CMD_TERRAFORM_LAND);
 
				}
 
				if (CmdFailed(res) && CHANCE16I(1, 3, r)) {
 
					// We can consider building on the slope, though.
 
					goto no_slope;
 
				}
 
			}
 
			return false;
 
		}
 
		return true;
 
	}
 
}
 

	
 
static bool TerraformTownTile(TileIndex tile, int edges, int dir)
 
{
 
	int32 r;
 

	
 
	TILE_ASSERT(tile);
 

	
 
	r = DoCommand(tile, edges, dir, DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND);
 
	if (CmdFailed(r) || r >= 126 * 16) return false;
 
	DoCommand(tile, edges, dir, DC_AUTO | DC_NO_WATER | DC_EXEC, CMD_TERRAFORM_LAND);
 
	return true;
 
}
 

	
 
static void LevelTownLand(TileIndex tile)
 
{
 
	Slope tileh;
 

	
 
	TILE_ASSERT(tile);
 

	
 
	// Don't terraform if land is plain or if there's a house there.
 
	if (IsTileType(tile, MP_HOUSE)) return;
 
	tileh = GetTileSlope(tile, NULL);
 
	if (tileh == SLOPE_FLAT) return;
 

	
 
	// First try up, then down
 
	if (!TerraformTownTile(tile, ~tileh & 0xF, 1)) {
 
		TerraformTownTile(tile, tileh & 0xF, 0);
 
	}
 
}
 

	
 
static void GrowTownInTile(TileIndex* tile_ptr, RoadBits mask, int block, Town* t1)
 
{
 
	RoadBits rcmd;
 
	TileIndex tmptile;
 
	DiagDirection i;
 
	int j;
 
	TileIndex tile = *tile_ptr;
 

	
 
	TILE_ASSERT(tile);
 

	
 
	if (mask == 0) {
 
		int a;
 
		int b;
 

	
 
		// Tile has no road. First reset the status counter
 
		// to say that this is the last iteration.
 
		_grow_town_result = 0;
 

	
 
		// Remove hills etc
 
		LevelTownLand(tile);
 

	
 
		// Is a road allowed here?
 
		if (!IsRoadAllowedHere(tile, block)) return;
 

	
 
		// Randomize new road block numbers
 
		a = block;
 
		b = block ^ 2;
 
		if (CHANCE16(1, 4)) {
 
			do {
 
				a = GB(Random(), 0, 2);
 
			} while (a == b);
 
		}
 

	
 
		if (!IsRoadAllowedHere(TILE_ADD(tile, ToTileIndexDiff(_roadblock_tileadd[a])), a)) {
 
			// A road is not allowed to continue the randomized road,
 
			//   return if the road we're trying to build is curved.
 
			if (a != (b ^ 2)) return;
 

	
 
			// Return if neither side of the new road is a house
 
			if (!IsTileType(TILE_ADD(tile, ToTileIndexDiff(_roadblock_tileadd[a + 1])), MP_HOUSE) &&
 
					!IsTileType(TILE_ADD(tile, ToTileIndexDiff(_roadblock_tileadd[a + 3])), MP_HOUSE))
 
				return;
 

	
 
			// That means that the road is only allowed if there is a house
 
			//  at any side of the new road.
 
		}
 
		rcmd = (1 << a) + (1 << b);
 

	
 
	} else if (block < 5 && !HASBIT(mask,block^2)) {
 
		// Continue building on a partial road.
 
		// Always OK.
 
		_grow_town_result = 0;
 
		rcmd = 1 << (block ^ 2);
 
	} else {
 
		int i;
 

	
 
		// Reached a tunnel/bridge? Then continue at the other side of it.
 
		if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
			if (IsTunnel(tile) && GetTunnelTransportType(tile) == TRANSPORT_ROAD) {
 
				*tile_ptr = GetOtherTunnelEnd(tile);
 
			} else if (IsBridge(tile) && GetBridgeTransportType(tile) == TRANSPORT_ROAD) {
 
				*tile_ptr = GetOtherBridgeEnd(tile);
 
			}
 
			return;
 
		}
 

	
 
		// Possibly extend the road in a direction.
 
		// Randomize a direction and if it has a road, bail out.
 
		i = GB(Random(), 0, 2);
 
		if (HASBIT(mask, i)) return;
 

	
 
		// This is the tile we will reach if we extend to this direction.
 
		tmptile = TILE_ADD(tile, ToTileIndexDiff(_roadblock_tileadd[i]));
 

	
 
		// Don't do it if it reaches to water.
 
		if (IsClearWaterTile(tmptile)) return;
 

	
 
		// Build a house at the edge. 60% chance or
 
		//  always ok if no road allowed.
 
		if (!IsRoadAllowedHere(tmptile, i) || CHANCE16(6, 10)) {
 
			// But not if there already is a house there.
 
			if (!IsTileType(tmptile, MP_HOUSE)) {
 
				// Level the land if possible
 
				LevelTownLand(tmptile);
 

	
 
				// And build a house.
 
				// Set result to -1 if we managed to build it.
 
				if (BuildTownHouse(t1, tmptile)) _grow_town_result = -1;
 
			}
 
			return;
 
		}
 

	
 
		_grow_town_result = 0;
 
		rcmd = 1 << i;
 
	}
 

	
 
	// Return if a water tile
 
	if (IsClearWaterTile(tile)) return;
 

	
 
	// Determine direction of slope,
 
	//  and build a road if not a special slope.
 
	switch (GetTileSlope(tile, NULL)) {
 
		case SLOPE_SW: i = DIAGDIR_NE; break;
 
		case SLOPE_SE: i = DIAGDIR_NW; break;
 
		case SLOPE_NW: i = DIAGDIR_SE; break;
 
		case SLOPE_NE: i = DIAGDIR_SW; break;
 

	
 
		default:
 
build_road_and_exit:
 
			if (!CmdFailed(DoCommand(tile, rcmd, t1->index, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD))) {
 
				_grow_town_result = -1;
 
			}
 
			return;
 
	}
 

	
 
	tmptile = tile;
 

	
 
	// Now it contains the direction of the slope
 
	j = -11; // max 11 tile long bridges
 
	do {
 
		if (++j == 0)
 
			goto build_road_and_exit;
 
		tmptile = TILE_MASK(tmptile + TileOffsByDiagDir(i));
 
	} while (IsClearWaterTile(tmptile));
 

	
 
	// no water tiles in between?
 
	if (j == -10)
 
		goto build_road_and_exit;
 

	
 
	// Quit if it selecting an appropiate bridge type fails a large number of times.
 
	j = 22;
 
	{
 
		int32 bridge_len = GetBridgeLength(tile, tmptile);
 
		do {
 
			byte bridge_type = RandomRange(MAX_BRIDGES - 1);
 
			if (CheckBridge_Stuff(bridge_type, bridge_len)) {
 
				if (!CmdFailed(DoCommand(tile, tmptile, 0x8000 + bridge_type, DC_EXEC | DC_AUTO, CMD_BUILD_BRIDGE)))
 
					_grow_town_result = -1;
 

	
 
				// obviously, if building any bridge would fail, there is no need to try other bridge-types
 
				return;
 
			}
 
		} while (--j != 0);
 
	}
 
}
 

	
 
// Returns true if a house was built, or no if the build failed.
 
static int GrowTownAtRoad(Town *t, TileIndex tile)
 
{
 
	int block = 5; // special case
 

	
 
	TILE_ASSERT(tile);
 

	
 
	// Number of times to search.
 
	_grow_town_result = 10 + t->num_houses * 4 / 9;
 

	
 
	do {
 
		// Get a bitmask of the road blocks on a tile
 
		RoadBits mask = GetTownRoadMask(tile);
 

	
 
		// Try to grow the town from this point
 
		GrowTownInTile(&tile,mask,block,t);
 

	
 
		// Exclude the source position from the bitmask
 
		// and return if no more road blocks available
 
		CLRBIT(mask, (block ^ 2));
 
		if (mask == 0)
 
			return _grow_town_result;
 

	
 
		// Select a random bit from the blockmask, walk a step
 
		// and continue the search from there.
 
		do block = Random() & 3; while (!HASBIT(mask,block));
 
		tile += ToTileIndexDiff(_roadblock_tileadd[block]);
 

	
 
		if (IsTileType(tile, MP_STREET)) {
 
			/* Don't allow building over roads of other cities */
 
			if (IsTileOwner(tile, OWNER_TOWN) && GetTownByTile(tile) != t) {
 
				_grow_town_result = -1;
 
			} else if (_game_mode == GM_EDITOR) {
 
				/* If we are in the SE, and this road-piece has no town owner yet, it just found an
 
				 * owner :) (happy happy happy road now) */
 
				SetTileOwner(tile, OWNER_TOWN);
 
				SetTownIndex(tile, t->index);
 
			}
 
		}
 

	
 
		// Max number of times is checked.
 
	} while (--_grow_town_result >= 0);
 

	
 
	return (_grow_town_result == -2);
 
}
 

	
 
// Generate a random road block
 
// The probability of a straight road
 
// is somewhat higher than a curved.
 
static RoadBits GenRandomRoadBits(void)
 
{
 
	uint32 r = Random();
 
	uint a = GB(r, 0, 2);
 
	uint b = GB(r, 8, 2);
 
	if (a == b) b ^= 2;
 
	return (1 << a) + (1 << b);
 
}
 

	
 
// Grow the town
 
// Returns true if a house was built, or no if the build failed.
 
static bool GrowTown(Town *t)
 
{
 
	TileIndex tile;
 
	const TileIndexDiffC *ptr;
 
	PlayerID old_player;
 

	
 
	static const TileIndexDiffC _town_coord_mod[] = {
 
		{-1,  0},
 
		{ 1,  1},
 
		{ 1, -1},
 
		{-1, -1},
 
		{-1,  0},
 
		{ 0,  2},
 
		{ 2,  0},
 
		{ 0, -2},
 
		{-1, -1},
 
		{-2,  2},
 
		{ 2,  2},
 
		{ 2, -2},
 
		{ 0,  0}
 
	};
 

	
 
	// Current player is a town
 
	old_player = _current_player;
 
	_current_player = OWNER_TOWN;
 

	
 
	// Find a road that we can base the construction on.
 
	tile = t->xy;
 
	for (ptr = _town_coord_mod; ptr != endof(_town_coord_mod); ++ptr) {
 
		if (GetAnyRoadTrackBits(tile) != 0) {
 
			int r = GrowTownAtRoad(t, tile);
 
			_current_player = old_player;
 
			return r;
 
		}
 
		tile = TILE_ADD(tile, ToTileIndexDiff(*ptr));
 
	}
 

	
 
	// No road available, try to build a random road block by
 
	// clearing some land and then building a road there.
 
	tile = t->xy;
 
	for (ptr = _town_coord_mod; ptr != endof(_town_coord_mod); ++ptr) {
 
		/* Only work with plain land that not already has a house */
 
		if (!IsTileType(tile, MP_HOUSE) && GetTileSlope(tile, NULL) == SLOPE_FLAT) {
 
			if (!CmdFailed(DoCommand(tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR))) {
 
				DoCommand(tile, GenRandomRoadBits(), t->index, DC_EXEC | DC_AUTO, CMD_BUILD_ROAD);
 
				_current_player = old_player;
 
				return true;
 
			}
 
		}
 
		tile = TILE_ADD(tile, ToTileIndexDiff(*ptr));
 
	}
 

	
 
	_current_player = old_player;
 
	return false;
 
}
 

	
 
static void UpdateTownRadius(Town *t)
 
{
 
	static const uint16 _town_radius_data[23][5] = {
 
		{  4,  0,  0,  0,  0}, // 0
 
		{ 16,  0,  0,  0,  0},
 
		{ 25,  0,  0,  0,  0},
 
		{ 36,  0,  0,  0,  0},
 
		{ 49,  0,  4,  0,  0},
 
		{ 64,  0,  4,  0,  0}, // 20
 
		{ 64,  0,  9,  0,  1},
 
		{ 64,  0,  9,  0,  4},
 
		{ 64,  0, 16,  0,  4},
 
		{ 81,  0, 16,  0,  4},
 
		{ 81,  0, 16,  0,  4}, // 40
 
		{ 81,  0, 25,  0,  9},
 
		{ 81, 36, 25,  0,  9},
 
		{ 81, 36, 25, 16,  9},
 
		{ 81, 49,  0, 25,  9},
 
		{ 81, 64,  0, 25,  9}, // 60
 
		{ 81, 64,  0, 36,  9},
 
		{ 81, 64,  0, 36, 16},
 
		{100, 81,  0, 49, 16},
 
		{100, 81,  0, 49, 25},
 
		{121, 81,  0, 49, 25}, // 80
 
		{121, 81,  0, 49, 25},
 
		{121, 81,  0, 49, 36}, // 88
 
	};
 

	
 
	if (t->num_houses < 92) {
 
		memcpy(t->radius, _town_radius_data[t->num_houses / 4], sizeof(t->radius));
 
	} else {
 
		int mass = t->num_houses / 8;
 
		// At least very roughly extrapolate. Empirical numbers dancing between
 
		// overwhelming by cottages and skyscrapers outskirts.
 
		t->radius[0] = mass * mass;
 
		// Actually we are proportional to sqrt() but that's right because
 
		// we are covering an area.
 
		t->radius[1] = mass * 7;
 
		t->radius[2] = 0;
 
		t->radius[3] = mass * 4;
 
		t->radius[4] = mass * 3;
 
		//debug("%d (->%d): %d %d %d %d\n", t->num_houses, mass, t->radius[0], t->radius[1], t->radius[3], t->radius[4]);
 
	}
 
}
 

	
 
static bool CreateTownName(uint32 *townnameparts)
 
{
 
	Town *t2;
 
	char buf1[64];
 
	char buf2[64];
 
	uint32 r;
 
	/* Do not set too low tries, since when we run out of names, we loop
 
	 * for #tries only one time anyway - then we stop generating more
 
	 * towns. Do not show it too high neither, since looping through all
 
	 * the other towns may take considerable amount of time (10000 is
 
	 * too much). */
 
	int tries = 1000;
 
	uint16 townnametype = SPECSTR_TOWNNAME_START + _opt.town_name;
 

	
 
	assert(townnameparts);
 

	
 
	for (;;) {
 
restart:
 
		r = Random();
 

	
 
		SetDParam(0, r);
 
		GetString(buf1, townnametype, lastof(buf1));
 

	
 
		// Check size and width
 
		if (strlen(buf1) >= 31 || GetStringBoundingBox(buf1).width > 130) continue;
 

	
 
		FOR_ALL_TOWNS(t2) {
 
			// We can't just compare the numbers since
 
			// several numbers may map to a single name.
 
			SetDParam(0, t2->index);
 
			GetString(buf2, STR_TOWN, lastof(buf2));
 
			if (strcmp(buf1, buf2) == 0) {
 
				if (tries-- < 0) return false;
 
				goto restart;
 
			}
 
		}
 
		*townnameparts = r;
 
		return true;
 
	}
 
}
 

	
 
void UpdateTownMaxPass(Town *t)
 
{
 
	t->max_pass = t->population >> 3;
 
	t->max_mail = t->population >> 4;
 
}
 

	
 
static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, uint size_mode)
 
{
 
	int x, i;
 

	
 
	// clear the town struct
 
	i = t->index;
 
	memset(t, 0, sizeof(Town));
 
	t->index = i;
 
	_total_towns++;
 

	
 
	t->xy = tile;
 
	t->num_houses = 0;
 
	t->time_until_rebuild = 10;
 
	UpdateTownRadius(t);
 
	t->flags12 = 0;
 
	t->population = 0;
 
	t->grow_counter = 0;
 
	t->growth_rate = 250;
 
	t->new_max_pass = 0;
 
	t->new_max_mail = 0;
 
	t->new_act_pass = 0;
 
	t->new_act_mail = 0;
 
	t->max_pass = 0;
 
	t->max_mail = 0;
 
	t->act_pass = 0;
 
	t->act_mail = 0;
 

	
 
	t->pct_pass_transported = 0;
 
	t->pct_mail_transported = 0;
 
	t->fund_buildings_months = 0;
 
	t->new_act_food = 0;
 
	t->new_act_water = 0;
 
	t->act_food = 0;
 
	t->act_water = 0;
 

	
 
	for (i = 0; i != MAX_PLAYERS; i++)
 
		t->ratings[i] = 500;
 

	
 
	t->have_ratings = 0;
 
	t->exclusivity = (byte)-1;
 
	t->exclusive_counter = 0;
 
	t->statues = 0;
 

	
 
	t->townnametype = SPECSTR_TOWNNAME_START + _opt.town_name;
 
	t->townnameparts = townnameparts;
 

	
 
	UpdateTownVirtCoord(t);
 
	_town_sort_dirty = true;
 

	
 
	if (size_mode == 0) {
 
		x = (Random() & 0xF) + 8;
 
	} else {
 
		x = (size_mode - 1) * 16 + 3;
 
	}
 

	
 
	t->num_houses += x;
 
	UpdateTownRadius(t);
 

	
 
	i = x * 4;
 
	do {
 
		GrowTown(t);
 
	} while (--i);
 

	
 
	t->num_houses -= x;
 
	UpdateTownRadius(t);
 
	UpdateTownMaxPass(t);
 
}
 

	
 
static Town *AllocateTown(void)
 
{
 
	Town *t;
 

	
 
	/* 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 (t = GetTown(0); t != NULL; t = (t->index + 1U < GetTownPoolSize()) ? GetTown(t->index + 1U) : NULL) {
 
		if (!IsValidTown(t)) {
 
			TownID index = t->index;
 

	
 
			memset(t, 0, sizeof(Town));
 
			t->index = index;
 

	
 
			return t;
 
		}
 
	}
 

	
 
	/* Check if we can add a block to the pool */
 
	if (AddBlockToPool(&_Town_pool))
 
		return AllocateTown();
 

	
 
	return NULL;
 
}
 

	
 
/** Create a new town.
 
 * This obviously only works in the scenario editor. Function not removed
 
 * as it might be possible in the future to fund your own town :)
 
 * @param tile coordinates where town is built
 
 * @param p1 size of the town (0 = random, 1 = small, 2 = medium, 3 = large)
 
 * @param p2 unused
 
 */
 
int32 CmdBuildTown(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Town *t;
 
	uint32 townnameparts;
 

	
 
	/* Only in the scenario editor */
 
	if (_game_mode != GM_EDITOR) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_OTHER);
 

	
 
	// Check if too close to the edge of map
 
	if (DistanceFromEdge(tile) < 12)
 
		return_cmd_error(STR_0237_TOO_CLOSE_TO_EDGE_OF_MAP);
 

	
 
	// Can only build on clear flat areas, possibly with trees.
 
	if ((!IsTileType(tile, MP_CLEAR) && !IsTileType(tile, MP_TREES)) || GetTileSlope(tile, NULL) != SLOPE_FLAT) {
 
		return_cmd_error(STR_0239_SITE_UNSUITABLE);
 
	}
 

	
 
	// Check distance to all other towns.
 
	if (IsCloseToTown(tile, 20))
 
		return_cmd_error(STR_0238_TOO_CLOSE_TO_ANOTHER_TOWN);
 

	
 
	// Get a unique name for the town.
 
	if (!CreateTownName(&townnameparts))
 
		return_cmd_error(STR_023A_TOO_MANY_TOWNS);
 

	
 
	// Allocate town struct
 
	t = AllocateTown();
 
	if (t == NULL) return_cmd_error(STR_023A_TOO_MANY_TOWNS);
 

	
 
	// Create the town
 
	if (flags & DC_EXEC) {
 
		_generating_world = true;
 
		DoCreateTown(t, tile, townnameparts, p1);
 
		_generating_world = false;
 
	}
 
	return 0;
 
}
 

	
 
Town *CreateRandomTown(uint attempts, uint size_mode)
 
{
 
	TileIndex tile;
 
	Town *t;
 
	uint32 townnameparts;
 

	
 
	do {
 
		// Generate a tile index not too close from the edge
 
		tile = RandomTile();
 
		if (DistanceFromEdge(tile) < 20) continue;
 

	
 
		// Make sure the tile is plain
 
		if (!IsTileType(tile, MP_CLEAR) || GetTileSlope(tile, NULL) != SLOPE_FLAT) continue;
 

	
 
		// Check not too close to a town
 
		if (IsCloseToTown(tile, 20)) continue;
 

	
 
		// Get a unique name for the town.
 
		if (!CreateTownName(&townnameparts)) break;
 

	
 
		// Allocate a town struct
 
		t = AllocateTown();
 
		if (t == NULL) break;
 

	
 
		DoCreateTown(t, tile, townnameparts, size_mode);
 
		return t;
 
	} while (--attempts);
 
	return NULL;
 
}
 

	
 
static const byte _num_initial_towns[3] = {11, 23, 46};
 

	
 
bool GenerateTowns(void)
 
{
 
	uint num = 0;
 
	uint n = ScaleByMapSize(_num_initial_towns[_opt.diff.number_towns] + (Random() & 7));
 

	
 
	SetGeneratingWorldProgress(GWP_TOWN, n);
 

	
 
	do {
 
		IncreaseGeneratingWorldProgress(GWP_TOWN);
 
		// try 20 times to create a random-sized town for the first loop.
 
		if (CreateRandomTown(20, 0) != NULL) num++;
 
	} while (--n);
 

	
 
	// give it a last try, but now more aggressive
 
	if (num == 0 && CreateRandomTown(10000, 0) == NULL) {
 
		if (GetNumTowns() == 0) {
 
			/* XXX - can we handle that more gracefully? */
 
			if (_game_mode != GM_EDITOR) error("Could not generate any town");
 

	
 
			return false;
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
static bool CheckBuildHouseMode(TileIndex tile, Slope tileh, int mode)
 
{
 
	int b;
 
	Slope slope;
 

	
 
	static const byte _masks[8] = {
 
		0xC,0x3,0x9,0x6,
 
		0x3,0xC,0x6,0x9,
 
	};
 

	
 
	slope = GetTileSlope(tile, NULL);
 
	if (IsSteepSlope(slope)) return false;
 

	
 
	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return false;
 

	
 
	b = 0;
 
	if ((slope != SLOPE_FLAT && ~slope & _masks[mode])) b = ~b;
 
	if ((tileh != SLOPE_FLAT && ~tileh & _masks[mode+4])) b = ~b;
 
	if (b)
 
		return false;
 

	
 
	return !CmdFailed(DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_LANDSCAPE_CLEAR));
 
}
 

	
 

	
 
uint GetTownRadiusGroup(const Town* t, TileIndex tile)
 
{
 
	uint dist = DistanceSquare(tile, t->xy);
 
	uint smallest;
 
	uint i;
 

	
 
	if (t->fund_buildings_months && dist <= 25) return 4;
 

	
 
	smallest = 0;
 
	for (i = 0; i != lengthof(t->radius); i++) {
 
		if (dist < t->radius[i]) smallest = i;
 
	}
 

	
 
	return smallest;
 
}
 

	
 
static bool CheckFree2x2Area(TileIndex tile)
 
{
 
	int i;
 

	
 
	static const TileIndexDiffC _tile_add[] = {
 
		{0    , 0    },
 
		{0 - 0, 1 - 0},
 
		{1 - 0, 0 - 1},
 
		{1 - 1, 1 - 0}
 
	};
 

	
 
	for (i = 0; i != 4; i++) {
 
		tile += ToTileIndexDiff(_tile_add[i]);
 

	
 
		if (GetTileSlope(tile, NULL) != SLOPE_FLAT) return false;
 

	
 
		if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return false;
 

	
 
		if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER | DC_FORCETEST, CMD_LANDSCAPE_CLEAR)))
 
			return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
static void DoBuildTownHouse(Town *t, TileIndex tile)
 
{
 
	int i;
 
	uint bitmask;
 
	int house;
 
	Slope slope;
 
	uint z;
 
	uint oneof = 0;
 

	
 
	// Above snow?
 
	slope = GetTileSlope(tile, &z);
 

	
 
	// Get the town zone type
 
	{
 
		uint rad = GetTownRadiusGroup(t, tile);
 

	
 
		int land = _opt.landscape;
 
		if (land == LT_HILLY && z >= _opt.snow_line) land = -1;
 

	
 
		bitmask = (1 << rad) + (1 << (land + 12));
 
	}
 

	
 
	// bits 0-4 are used
 
	// bits 11-15 are used
 
	// bits 5-10 are not used.
 
	{
 
		byte houses[lengthof(_housetype_flags)];
 
		int num = 0;
 

	
 
		// Generate a list of all possible houses that can be built.
 
		for (i=0; i!=lengthof(_housetype_flags); i++) {
 
			if ((~_housetype_flags[i] & bitmask) == 0)
 
				houses[num++] = (byte)i;
 
		}
 

	
 
		for (;;) {
 
			house = houses[RandomRange(num)];
 

	
 
			if (_cur_year < _housetype_years[house].min || _cur_year > _housetype_years[house].max)
 
				continue;
 

	
 
			// Special houses that there can be only one of.
 
			switch (house) {
 
				case HOUSE_TEMP_CHURCH:
 
				case HOUSE_ARCT_CHURCH:
 
				case HOUSE_SNOW_CHURCH:
 
				case HOUSE_TROP_CHURCH:
 
				case HOUSE_TOY_CHURCH:
 
					SETBIT(oneof, TOWN_HAS_CHURCH);
 
					break;
 
				case HOUSE_STADIUM:
 
				case HOUSE_MODERN_STADIUM:
 
					SETBIT(oneof, TOWN_HAS_STADIUM);
 
					break;
 
				default:
 
					oneof = 0;
 
					break;
 
			}
 

	
 
			if (HASBITS(t->flags12 , oneof)) continue;
 

	
 
			// Make sure there is no slope?
 
			if (_housetype_extra_flags[house] & 0x12 && slope != SLOPE_FLAT) continue;
 

	
 
			if (_housetype_extra_flags[house] & 0x10) {
 
				if (CheckFree2x2Area(tile) ||
 
						CheckFree2x2Area(tile += TileDiffXY(-1,  0)) ||
 
						CheckFree2x2Area(tile += TileDiffXY( 0, -1)) ||
 
						CheckFree2x2Area(tile += TileDiffXY( 1,  0))) {
 
					break;
 
				}
 
				tile += TileDiffXY(0, 1);
 
			} else if (_housetype_extra_flags[house] & 4) {
 
				if (CheckBuildHouseMode(tile + TileDiffXY(1, 0), slope, 0)) break;
 

	
 
				if (CheckBuildHouseMode(tile + TileDiffXY(-1, 0), slope, 1)) {
 
					tile += TileDiffXY(-1, 0);
 
					break;
 
				}
 
			} else if (_housetype_extra_flags[house] & 8) {
 
				if (CheckBuildHouseMode(tile + TileDiffXY(0, 1), slope, 2)) break;
 

	
 
				if (CheckBuildHouseMode(tile + TileDiffXY(0, -1), slope, 3)) {
 
					tile += TileDiffXY(0, -1);
 
					break;
 
				}
 
			} else {
 
				break;
 
			}
 
		}
 
	}
 

	
 
	t->num_houses++;
 

	
 
	// Special houses that there can be only one of.
 
	t->flags12 |= oneof;
 

	
 
	{
 
		byte construction_counter = 0, construction_stage = 0, size_flags;
 

	
 
		if (_generating_world) {
 
			uint32 r = Random();
 

	
 
			construction_stage = TOWN_HOUSE_COMPLETED;
 
			if (CHANCE16(1, 7)) construction_stage = GB(r, 0, 2);
 

	
 
			if (construction_stage == TOWN_HOUSE_COMPLETED) {
 
				ChangePopulation(t, _housetype_population[house]);
 
			} else {
 
				construction_counter = GB(r, 2, 2);
 
			}
 
		}
 
		size_flags = GB(_housetype_extra_flags[house], 2, 3);
 
		MakeTownHouse(tile, t->index, construction_counter, construction_stage, size_flags, house);
 
	}
 
}
 

	
 
static bool BuildTownHouse(Town *t, TileIndex tile)
 
{
 
	int32 r;
 

	
 
	// make sure it's possible
 
	if (!EnsureNoVehicle(tile)) return false;
 
	if (IsSteepSlope(GetTileSlope(tile, NULL))) return false;
 
	if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return false;
 

	
 
	r = DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(r)) return false;
 

	
 
	DoBuildTownHouse(t, tile);
 
	return true;
 
}
 

	
 

	
 
static void DoClearTownHouseHelper(TileIndex tile)
 
{
 
	assert(IsTileType(tile, MP_HOUSE));
 
	DoClearSquare(tile);
 
	DeleteAnimatedTile(tile);
 
}
 

	
 
static void ClearTownHouse(Town *t, TileIndex tile)
 
{
 
	uint house = GetHouseType(tile);
 
	uint eflags;
 

	
 
	assert(IsTileType(tile, MP_HOUSE));
 

	
 
	// need to align the tile to point to the upper left corner of the house
 
	if (house >= 3) { // house id 0,1,2 MUST be single tile houses, or this code breaks.
 
		if (_housetype_extra_flags[house-1] & 0x04) {
 
			house--;
 
			tile += TileDiffXY(-1, 0);
 
		} else if (_housetype_extra_flags[house-1] & 0x18) {
 
			house--;
 
			tile += TileDiffXY(0, -1);
 
		} else if (_housetype_extra_flags[house-2] & 0x10) {
 
			house-=2;
 
			tile += TileDiffXY(-1, 0);
 
		} else if (_housetype_extra_flags[house-3] & 0x10) {
 
			house-=3;
 
			tile += TileDiffXY(-1, -1);
 
		}
 
	}
 

	
 
	// Remove population from the town if the house is finished.
 
	if (GetHouseBuildingStage(tile) == TOWN_HOUSE_COMPLETED) {
 
		ChangePopulation(t, -_housetype_population[house]);
 
	}
 

	
 
	t->num_houses--;
 

	
 
	// Clear flags for houses that only may exist once/town.
 
	switch (house) {
 
		case HOUSE_TEMP_CHURCH:
 
		case HOUSE_ARCT_CHURCH:
 
		case HOUSE_SNOW_CHURCH:
 
		case HOUSE_TROP_CHURCH:
 
		case HOUSE_TOY_CHURCH:
 
			CLRBIT(t->flags12, TOWN_HAS_CHURCH);
 
			break;
 
		case HOUSE_STADIUM:
 
		case HOUSE_MODERN_STADIUM:
 
			CLRBIT(t->flags12, TOWN_HAS_STADIUM);
 
			break;
 
		default:
 
			break;
 
	}
 

	
 
	// Do the actual clearing of tiles
 
	eflags = _housetype_extra_flags[house];
 
	DoClearTownHouseHelper(tile);
 
	if (eflags & 0x14) DoClearTownHouseHelper(tile + TileDiffXY(1, 0));
 
	if (eflags & 0x18) DoClearTownHouseHelper(tile + TileDiffXY(0, 1));
 
	if (eflags & 0x10) DoClearTownHouseHelper(tile + TileDiffXY(1, 1));
 
}
 

	
 
/** Rename a town (server-only).
 
 * @param tile unused
 
 * @param p1 town ID to rename
 
 * @param p2 unused
 
 */
 
int32 CmdRenameTown(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	StringID str;
 
	Town *t;
 

	
 
	if (!IsValidTownID(p1) || _cmd_text[0] == '\0') return CMD_ERROR;
 

	
 
	t = GetTown(p1);
 

	
 
	str = AllocateNameUnique(_cmd_text, 4);
 
	if (str == 0) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		DeleteName(t->townnametype);
 
		t->townnametype = str;
 

	
 
		UpdateTownVirtCoord(t);
 
		_town_sort_dirty = true;
 
		UpdateAllStationVirtCoord();
 
		MarkWholeScreenDirty();
 
	} else {
 
		DeleteName(str);
 
	}
 
	return 0;
 
}
 

	
 
// Called from GUI
 
void ExpandTown(Town *t)
 
{
 
	int amount, n;
 

	
 
	_generating_world = true;
 

	
 
	/* The more houses, the faster we grow */
 
	amount = RandomRange(t->num_houses / 10) + 3;
 
	t->num_houses += amount;
 
	UpdateTownRadius(t);
 

	
 
	n = amount * 10;
 
	do GrowTown(t); while (--n);
 

	
 
	t->num_houses -= amount;
 
	UpdateTownRadius(t);
 

	
 
	UpdateTownMaxPass(t);
 
	_generating_world = false;
 
}
 

	
 
const byte _town_action_costs[8] = {
 
	2, 4, 9, 35, 48, 53, 117, 175
 
};
 

	
 
static void TownActionAdvertiseSmall(Town* t)
 
{
 
	ModifyStationRatingAround(t->xy, _current_player, 0x40, 10);
 
}
 

	
 
static void TownActionAdvertiseMedium(Town* t)
 
{
 
	ModifyStationRatingAround(t->xy, _current_player, 0x70, 15);
 
}
 

	
 
static void TownActionAdvertiseLarge(Town* t)
 
{
 
	ModifyStationRatingAround(t->xy, _current_player, 0xA0, 20);
 
}
 

	
 
static void TownActionRoadRebuild(Town* t)
 
{
 
	const Player* p;
 

	
 
	t->road_build_months = 6;
 

	
 
	SetDParam(0, t->index);
 

	
 
	p = GetPlayer(_current_player);
 
	SetDParam(1, p->name_1);
 
	SetDParam(2, p->name_2);
 

	
 
	AddNewsItem(STR_2055_TRAFFIC_CHAOS_IN_ROAD_REBUILDING,
 
		NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_GENERAL, 0), t->xy, 0);
 
}
 

	
 
static bool DoBuildStatueOfCompany(TileIndex tile)
 
{
 
	PlayerID old;
 
	int32 r;
 

	
 
	if (GetTileSlope(tile, NULL) != SLOPE_FLAT) return false;
 

	
 
	if (!IsTileType(tile, MP_HOUSE) &&
 
			!IsTileType(tile, MP_CLEAR) &&
 
			!IsTileType(tile, MP_TREES)) {
 
		return false;
 
	}
 

	
 
	old = _current_player;
 
	_current_player = OWNER_NONE;
 
	r = DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
	_current_player = old;
 

	
 
	if (CmdFailed(r)) return false;
 

	
 
	MakeStatue(tile, _current_player);
 
	MarkTileDirtyByTile(tile);
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Search callback function for TownActionBuildStatue
 
 * @param data that is passed by the caller.  In this case, nothing
 
 * @result of the test
 
 */
 
static bool SearchTileForStatue(TileIndex tile, uint32 data)
 
{
 
	return DoBuildStatueOfCompany(tile);
 
}
 

	
 
/**
 
 * Perform a 9x9 tiles circular search from the center of the town
 
 * in order to find a free tile to place a statue
 
 * @param t town to search in
 
 */
 
static void TownActionBuildStatue(Town* t)
 
{
 
	TileIndex tile = t->xy;
 

	
 
	if (CircularTileSearch(tile, 9, SearchTileForStatue, 0))
 
		SETBIT(t->statues, _current_player); ///< Once found and built, "inform" the Town
 
}
 

	
 
static void TownActionFundBuildings(Town* t)
 
{
 
	// Build next tick
 
	t->grow_counter = 1;
 
	// If we were not already growing
 
	SETBIT(t->flags12, TOWN_IS_FUNDED);
 
	// And grow for 3 months
 
	t->fund_buildings_months = 3;
 
}
 

	
 
static void TownActionBuyRights(Town* t)
 
{
 
	t->exclusive_counter = 12;
 
	t->exclusivity = _current_player;
 

	
 
	ModifyStationRatingAround(t->xy, _current_player, 130, 17);
 
}
 

	
 
static void TownActionBribe(Town* t)
 
{
 
	if (!RandomRange(15)) {
 
		Station *st;
 

	
 
		// set as unwanted for 6 months
 
		t->unwanted[_current_player] = 6;
 

	
 
		// set all close by station ratings to 0
 
		FOR_ALL_STATIONS(st) {
 
			if (st->town == t && st->owner == _current_player) {
 
				uint i;
 

	
 
				for (i = 0; i != NUM_CARGO; i++) st->goods[i].rating = 0;
 
			}
 
		}
 

	
 
		// only show errormessage to the executing player. All errors are handled command.c
 
		// but this is special, because it can only 'fail' on a DC_EXEC
 
		if (IsLocalPlayer()) ShowErrorMessage(STR_BRIBE_FAILED_2, STR_BRIBE_FAILED, 0, 0);
 

	
 
		/* decrease by a lot!
 
		 * ChangeTownRating is only for stuff in demolishing. Bribe failure should
 
		 * be independent of any cheat settings
 
		 */
 
		if (t->ratings[_current_player] > RATING_BRIBE_DOWN_TO) {
 
			t->ratings[_current_player] = RATING_BRIBE_DOWN_TO;
 
		}
 
	} else {
 
		ChangeTownRating(t, RATING_BRIBE_UP_STEP, RATING_BRIBE_MAXIMUM);
 
	}
 
}
 

	
 
typedef void TownActionProc(Town* t);
 
static TownActionProc * const _town_action_proc[] = {
 
	TownActionAdvertiseSmall,
 
	TownActionAdvertiseMedium,
 
	TownActionAdvertiseLarge,
 
	TownActionRoadRebuild,
 
	TownActionBuildStatue,
 
	TownActionFundBuildings,
 
	TownActionBuyRights,
 
	TownActionBribe
 
};
 

	
 
extern uint GetMaskOfTownActions(int *nump, PlayerID pid, const Town *t);
 

	
 
/** Do a town action.
 
 * This performs an action such as advertising, building a statue, funding buildings,
 
 * but also bribing the town-council
 
 * @param tile unused
 
 * @param p1 town to do the action at
 
 * @param p2 action to perform, @see _town_action_proc for the list of available actions
 
 */
 
int32 CmdDoTownAction(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 cost;
 
	Town *t;
 

	
 
	if (!IsValidTownID(p1) || p2 > lengthof(_town_action_proc)) return CMD_ERROR;
 

	
 
	t = GetTown(p1);
 

	
 
	if (!HASBIT(GetMaskOfTownActions(NULL, _current_player, t), p2)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_OTHER);
 

	
 
	cost = (_price.build_industry >> 8) * _town_action_costs[p2];
 

	
 
	if (flags & DC_EXEC) {
 
		_town_action_proc[p2](t);
 
		InvalidateWindow(WC_TOWN_AUTHORITY, p1);
 
	}
 

	
 
	return cost;
 
}
 

	
 
static void UpdateTownGrowRate(Town *t)
 
{
 
	int n;
 
	Station *st;
 
	byte m;
 
	Player *p;
 

	
 
	// Reset player ratings if they're low
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active && t->ratings[p->index] <= 200) {
 
			t->ratings[p->index] += 5;
 
		}
 
	}
 

	
 
	n = 0;
 
	FOR_ALL_STATIONS(st) {
 
		if (DistanceSquare(st->xy, t->xy) <= t->radius[0]) {
 
			if (st->time_since_load <= 20 || st->time_since_unload <= 20) {
 
				n++;
 
				if (IsValidPlayer(st->owner) && t->ratings[st->owner] <= 1000-12)
 
					t->ratings[st->owner] += 12;
 
			} else {
 
				if (IsValidPlayer(st->owner) && t->ratings[st->owner] >= -1000+15)
 
					t->ratings[st->owner] -= 15;
 
			}
 
		}
 
	}
 

	
 
	CLRBIT(t->flags12, TOWN_IS_FUNDED);
 

	
 
	if (t->fund_buildings_months != 0) {
 
		static const byte _grow_count_values[6] = {
 
			60, 60, 60, 50, 40, 30
 
		};
 
		m = _grow_count_values[min(n, 5)];
 
		t->fund_buildings_months--;
 
	} else if (n == 0) {
 
		m = 160;
 
		if (!CHANCE16(1, 12))
 
			return;
 
	} else {
 
		static const byte _grow_count_values[5] = {
 
			210, 150, 110, 80, 50
 
		};
 
		m = _grow_count_values[min(n, 5) - 1];
 
	}
 

	
 
	if (_opt.landscape == LT_HILLY) {
 
		if (TilePixelHeight(t->xy) >= _opt.snow_line && t->act_food == 0 && t->population > 90)
 
			return;
 
	} else if (_opt.landscape == LT_DESERT) {
 
		if (GetTropicZone(t->xy) == TROPICZONE_DESERT && (t->act_food==0 || t->act_water==0) && t->population > 60)
 
			return;
 
	}
 

	
 
	t->growth_rate = m / (t->num_houses / 50 + 1);
 
	if (m <= t->grow_counter)
 
		t->grow_counter = m;
 

	
 
	SETBIT(t->flags12, TOWN_IS_FUNDED);
 
}
 

	
 
static void UpdateTownAmounts(Town *t)
 
{
 
	// Using +1 here to prevent overflow and division by zero
 
	t->pct_pass_transported = t->new_act_pass * 256 / (t->new_max_pass + 1);
 

	
 
	t->max_pass = t->new_max_pass; t->new_max_pass = 0;
 
	t->act_pass = t->new_act_pass; t->new_act_pass = 0;
 
	t->act_food = t->new_act_food; t->new_act_food = 0;
 
	t->act_water = t->new_act_water; t->new_act_water = 0;
 

	
 
	// Using +1 here to prevent overflow and division by zero
 
	t->pct_mail_transported = t->new_act_mail * 256 / (t->new_max_mail + 1);
 
	t->max_mail = t->new_max_mail; t->new_max_mail = 0;
 
	t->act_mail = t->new_act_mail; t->new_act_mail = 0;
 

	
 
	InvalidateWindow(WC_TOWN_VIEW, t->index);
 
}
 

	
 
static void UpdateTownUnwanted(Town *t)
 
{
 
	const Player* p;
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		if (t->unwanted[p->index] > 0) t->unwanted[p->index]--;
 
	}
 
}
 

	
 
bool CheckIfAuthorityAllows(TileIndex tile)
 
{
 
	Town *t;
 

	
 
	if (!IsValidPlayer(_current_player)) return true;
 

	
 
	t = ClosestTownFromTile(tile, _patches.dist_local_authority);
 
	if (t == NULL) return true;
 

	
 
	if (t->ratings[_current_player] > -200) return true;
 

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

	
 
	return false;
 
}
 

	
 

	
 
Town* CalcClosestTownFromTile(TileIndex tile, uint threshold)
 
{
 
	Town *t;
 
	uint dist, best = threshold;
 
	Town *best_town = NULL;
 

	
 
	FOR_ALL_TOWNS(t) {
 
		dist = DistanceManhattan(tile, t->xy);
 
		if (dist < best) {
 
			best = dist;
 
			best_town = t;
 
		}
 
	}
 

	
 
	return best_town;
 
}
 

	
 

	
 
Town *ClosestTownFromTile(TileIndex tile, uint threshold)
 
{
 
	if (IsTileType(tile, MP_HOUSE) || (
 
				IsTileType(tile, MP_STREET) &&
 
				(IsLevelCrossing(tile) ? GetCrossingRoadOwner(tile) : GetTileOwner(tile)) == OWNER_TOWN
 
			)) {
 
		return GetTownByTile(tile);
 
	} else {
 
		return CalcClosestTownFromTile(tile, threshold);
 
	}
 
}
 

	
 

	
 
void ChangeTownRating(Town *t, int add, int max)
 
{
 
	int rating;
 

	
 
	// if magic_bulldozer cheat is active, town doesn't penaltize for removing stuff
 
	if (t == NULL ||
 
			!IsValidPlayer(_current_player) ||
 
			(_cheats.magic_bulldozer.value && add < 0)) {
 
		return;
 
	}
 

	
 
	SETBIT(t->have_ratings, _current_player);
 

	
 
	rating = t->ratings[_current_player];
 

	
 
	if (add < 0) {
 
		if (rating > max) {
 
			rating += add;
 
			if (rating < max) rating = max;
 
		}
 
	} else {
 
		if (rating < max) {
 
			rating += add;
 
			if (rating > max) rating = max;
 
		}
 
	}
 
	t->ratings[_current_player] = rating;
 
}
 

	
 
/* penalty for removing town-owned stuff */
 
static const int _default_rating_settings [3][3] = {
 
	// ROAD_REMOVE, TUNNELBRIDGE_REMOVE, INDUSTRY_REMOVE
 
	{  0, 128, 384}, // Permissive
 
	{ 48, 192, 480}, // Neutral
 
	{ 96, 384, 768}, // Hostile
 
};
 

	
 
bool CheckforTownRating(uint32 flags, Town *t, byte type)
 
{
 
	int modemod;
 

	
 
	// if magic_bulldozer cheat is active, town doesn't restrict your destructive actions
 
	if (t == NULL || !IsValidPlayer(_current_player) || _cheats.magic_bulldozer.value)
 
		return true;
 

	
 
	/* check if you're allowed to remove the street/bridge/tunnel/industry
 
	 * owned by a town no removal if rating is lower than ... depends now on
 
	 * difficulty setting. Minimum town rating selected by difficulty level
 
	 */
 
	modemod = _default_rating_settings[_opt.diff.town_council_tolerance][type];
 

	
 
	if (t->ratings[_current_player] < 16 + modemod && !(flags & DC_NO_TOWN_RATING)) {
 
		SetDParam(0, t->index);
 
		_error_message = STR_2009_LOCAL_AUTHORITY_REFUSES;
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
void TownsMonthlyLoop(void)
 
{
 
	Town *t;
 

	
 
	FOR_ALL_TOWNS(t) {
 
		if (t->road_build_months != 0) t->road_build_months--;
 

	
 
		if (t->exclusive_counter != 0)
 
			if (--t->exclusive_counter == 0) t->exclusivity = (byte)-1;
 

	
 
		UpdateTownGrowRate(t);
 
		UpdateTownAmounts(t);
 
		UpdateTownUnwanted(t);
 
	}
 
}
 

	
 
void InitializeTowns(void)
 
{
 
	Subsidy *s;
 

	
 
	/* Clean the town pool and create 1 block in it */
 
	CleanPool(&_Town_pool);
 
	AddBlockToPool(&_Town_pool);
 

	
 
	memset(_subsidies, 0, sizeof(_subsidies));
 
	for (s=_subsidies; s != endof(_subsidies); s++)
 
		s->cargo_type = CT_INVALID;
 

	
 
	_cur_town_ctr = 0;
 
	_cur_town_iter = 0;
 
	_total_towns = 0;
 
	_town_sort_dirty = true;
 
}
 

	
 
const TileTypeProcs _tile_type_town_procs = {
 
	DrawTile_Town,           /* draw_tile_proc */
 
	GetSlopeZ_Town,          /* get_slope_z_proc */
 
	ClearTile_Town,          /* clear_tile_proc */
 
	GetAcceptedCargo_Town,   /* get_accepted_cargo_proc */
 
	GetTileDesc_Town,        /* get_tile_desc_proc */
 
	GetTileTrackStatus_Town, /* get_tile_track_status_proc */
 
	ClickTile_Town,          /* click_tile_proc */
 
	AnimateTile_Town,        /* animate_tile_proc */
 
	TileLoop_Town,           /* tile_loop_clear */
 
	ChangeTileOwner_Town,    /* change_tile_owner_clear */
 
	NULL,                    /* get_produced_cargo_proc */
 
	NULL,                    /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_Town,      /* get_slope_tileh_proc */
 
};
 

	
 

	
 
// Save and load of towns.
 
static const SaveLoad _town_desc[] = {
 
	SLE_CONDVAR(Town, xy,                    SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(Town, xy,                    SLE_UINT32,                 6, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Town, population,            SLE_FILE_U16 | SLE_VAR_U32, 0, 2),
 
	SLE_CONDVAR(Town, population,            SLE_UINT32,                 3, SL_MAX_VERSION),
 

	
 

	
 
	    SLE_VAR(Town, num_houses,            SLE_UINT16),
 
	    SLE_VAR(Town, townnametype,          SLE_UINT16),
 
	    SLE_VAR(Town, townnameparts,         SLE_UINT32),
 

	
 
	    SLE_VAR(Town, flags12,               SLE_UINT8),
 
	    SLE_VAR(Town, statues,               SLE_UINT8),
 

	
 
	// sort_index_obsolete was stored here in savegame format 0 - 1
 
	SLE_CONDNULL(1, 0, 1),
 

	
 
	    SLE_VAR(Town, have_ratings,          SLE_UINT8),
 
	    SLE_ARR(Town, ratings,               SLE_INT16, 8),
 
	// failed bribe attempts are stored since savegame format 4
 
	SLE_CONDARR(Town, unwanted,              SLE_INT8, 8, 4,SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Town, max_pass,              SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
 
	SLE_CONDVAR(Town, max_mail,              SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
 
	SLE_CONDVAR(Town, new_max_pass,          SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
 
	SLE_CONDVAR(Town, new_max_mail,          SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
 
	SLE_CONDVAR(Town, act_pass,              SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
 
	SLE_CONDVAR(Town, act_mail,              SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
 
	SLE_CONDVAR(Town, new_act_pass,          SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
 
	SLE_CONDVAR(Town, new_act_mail,          SLE_FILE_U16 | SLE_VAR_U32, 0, 8),
 

	
 
	SLE_CONDVAR(Town, max_pass,              SLE_UINT32,                 9, SL_MAX_VERSION),
 
	SLE_CONDVAR(Town, max_mail,              SLE_UINT32,                 9, SL_MAX_VERSION),
 
	SLE_CONDVAR(Town, new_max_pass,          SLE_UINT32,                 9, SL_MAX_VERSION),
 
	SLE_CONDVAR(Town, new_max_mail,          SLE_UINT32,                 9, SL_MAX_VERSION),
 
	SLE_CONDVAR(Town, act_pass,              SLE_UINT32,                 9, SL_MAX_VERSION),
 
	SLE_CONDVAR(Town, act_mail,              SLE_UINT32,                 9, SL_MAX_VERSION),
 
	SLE_CONDVAR(Town, new_act_pass,          SLE_UINT32,                 9, SL_MAX_VERSION),
 
	SLE_CONDVAR(Town, new_act_mail,          SLE_UINT32,                 9, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(Town, pct_pass_transported,  SLE_UINT8),
 
	    SLE_VAR(Town, pct_mail_transported,  SLE_UINT8),
 

	
 
	    SLE_VAR(Town, act_food,              SLE_UINT16),
 
	    SLE_VAR(Town, act_water,             SLE_UINT16),
 
	    SLE_VAR(Town, new_act_food,          SLE_UINT16),
 
	    SLE_VAR(Town, new_act_water,         SLE_UINT16),
 

	
 
	    SLE_VAR(Town, time_until_rebuild,    SLE_UINT8),
 
	    SLE_VAR(Town, grow_counter,          SLE_UINT8),
 
	    SLE_VAR(Town, growth_rate,           SLE_UINT8),
 
	    SLE_VAR(Town, fund_buildings_months, SLE_UINT8),
 
	    SLE_VAR(Town, road_build_months,     SLE_UINT8),
 

	
 
	    SLE_VAR(Town, exclusivity,           SLE_UINT8),
 
	    SLE_VAR(Town, exclusive_counter,     SLE_UINT8),
 
	// reserve extra space in savegame here. (currently 30 bytes)
 
	SLE_CONDNULL(30, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static void Save_TOWN(void)
 
{
 
	Town *t;
 

	
 
	FOR_ALL_TOWNS(t) {
 
		SlSetArrayIndex(t->index);
 
		SlObject(t, _town_desc);
 
	}
 
}
 

	
 
static void Load_TOWN(void)
 
{
 
	int index;
 

	
 
	_total_towns = 0;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		Town *t;
 

	
 
		if (!AddBlockIfNeeded(&_Town_pool, index))
 
			error("Towns: failed loading savegame: too many towns");
 

	
 
		t = GetTown(index);
 
		SlObject(t, _town_desc);
 

	
 
		_total_towns++;
 
	}
 

	
 
	/* This is to ensure all pointers are within the limits of
 
	 *  the size of the TownPool */
 
	if (_cur_town_ctr > GetMaxTownIndex())
 
		_cur_town_ctr = 0;
 
}
 

	
 
void AfterLoadTown(void)
 
{
 
	Town *t;
 
	FOR_ALL_TOWNS(t) {
 
		UpdateTownRadius(t);
 
		UpdateTownVirtCoord(t);
 
	}
 
	_town_sort_dirty = true;
 
}
 

	
 

	
 
const ChunkHandler _town_chunk_handlers[] = {
 
	{ 'CITY', Save_TOWN, Load_TOWN, CH_ARRAY | CH_LAST},
 
};
src/town_gui.c
Show inline comments
 
deleted file
src/town_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "strings.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "town.h"
 
#include "window.h"
 
#include "gfx.h"
 
#include "viewport.h"
 
#include "gui.h"
 
#include "command.h"
 
#include "player.h"
 
#include "network/network.h"
 
#include "variables.h"
 

	
 
static const Widget _town_authority_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    13,    11,   316,     0,    13, STR_2022_LOCAL_AUTHORITY, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    13,     0,   316,    14,   105, 0x0,                      STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    13,     0,   304,   106,   157, 0x0,                      STR_2043_LIST_OF_THINGS_TO_DO_AT},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,    13,   305,   316,   106,   157, 0x0,                      STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,   RESIZE_NONE,    13,     0,   316,   158,   209, 0x0,                      STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,     0,   316,   210,   221, STR_2042_DO_IT,           STR_2044_CARRY_OUT_THE_HIGHLIGHTED},
 
{   WIDGETS_END},
 
};
 

	
 
extern const byte _town_action_costs[8];
 
extern void DrawPlayerIcon(PlayerID pid, int x, int y);
 

	
 
/** Get a list of available actions to do at a town.
 
 * @param *nump if not NULL add put the number of available actions in it
 
 * @param pid the player that is querying the town
 
 * @param *t the town that is queried
 
 * @return bitmasked value of enabled actions
 
 */
 
uint GetMaskOfTownActions(int *nump, PlayerID pid, const Town *t)
 
{
 
	int32 avail, ref;
 
	int num = 0;
 
	uint avail_buttons = 0x7F; // by default all buttons except bribe are enabled.
 
	uint buttons = 0;
 

	
 
	if (pid != PLAYER_SPECTATOR) {
 
		uint i;
 

	
 
		// bribe option enabled?
 
		if (_patches.bribe) {
 
			// if unwanted, disable everything.
 
			if (t->unwanted[pid]) {
 
				avail_buttons = 0;
 
			} else if (t->ratings[pid] < RATING_BRIBE_MAXIMUM) {
 
				SETBIT(avail_buttons, 7); // Allow bribing
 
			}
 
		}
 

	
 
		// Things worth more than this are not shown
 
		avail = GetPlayer(pid)->player_money + _price.station_value * 200;
 
		ref = _price.build_industry >> 8;
 

	
 
		for (i = 0; i != lengthof(_town_action_costs); i++, avail_buttons >>= 1) {
 
			if (HASBIT(avail_buttons, 0) && avail >= _town_action_costs[i] * ref) {
 
				SETBIT(buttons, i);
 
				num++;
 
			}
 
		}
 

	
 
		/* Disable build statue if already built */
 
		if (HASBIT(t->statues, pid)) {
 
			CLRBIT(buttons, 4);
 
			num--;
 
		}
 

	
 
	}
 

	
 
	if (nump != NULL) *nump = num;
 
	return buttons;
 
}
 

	
 
static int GetNthSetBit(uint32 bits, int n)
 
{
 
	int i = 0;
 

	
 
	if (n >= 0) {
 
		do {
 
			if (bits & 1 && --n < 0) return i;
 
			i++;
 
		} while (bits >>= 1);
 
	}
 
	return -1;
 
}
 

	
 
static void TownAuthorityWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const Town *t = GetTown(w->window_number);
 
		int numact;
 
		uint buttons = GetMaskOfTownActions(&numact, _local_player, t);
 

	
 
		SetVScrollCount(w, numact + 1);
 

	
 
		if (WP(w,def_d).data_1 != -1 && !HASBIT(buttons, WP(w,def_d).data_1))
 
			WP(w,def_d).data_1 = -1;
 

	
 
		SetWindowWidgetDisabledState(w, 6, WP(w, def_d).data_1 == -1);
 

	
 
		{
 
			int y;
 
			const Player *p;
 
			int r;
 
			StringID str;
 

	
 
			SetDParam(0, w->window_number);
 
			DrawWindowWidgets(w);
 

	
 
			DrawString(2, 15, STR_2023_TRANSPORT_COMPANY_RATINGS, 0);
 

	
 
			// Draw list of players
 
			y = 25;
 
			FOR_ALL_PLAYERS(p) {
 
				if (p->is_active && (HASBIT(t->have_ratings, p->index) || t->exclusivity == p->index)) {
 
					DrawPlayerIcon(p->index, 2, y);
 

	
 
					SetDParam(0, p->name_1);
 
					SetDParam(1, p->name_2);
 
					SetDParam(2, GetPlayerNameString(p->index, 3));
 

	
 
					r = t->ratings[p->index];
 
					(str = STR_3035_APPALLING, r <= RATING_APPALLING) || // Apalling
 
					(str++,                    r <= RATING_VERYPOOR)  || // Very Poor
 
					(str++,                    r <= RATING_POOR)      || // Poor
 
					(str++,                    r <= RATING_MEDIOCRE)  || // Mediocore
 
					(str++,                    r <= RATING_GOOD)      || // Good
 
					(str++,                    r <= RATING_VERYGOOD)  || // Very Good
 
					(str++,                    r <= RATING_EXCELLENT) || // Excellent
 
					(str++,                    true);                    // Outstanding
 

	
 
					SetDParam(4, str);
 
					if (t->exclusivity == p->index) // red icon for player with exclusive rights
 
						DrawSprite(SPR_BLOT | PALETTE_TO_RED, 18, y);
 

	
 
					DrawString(28, y, STR_2024, 0);
 
					y += 10;
 
				}
 
			}
 
		}
 

	
 
		// Draw actions list
 
		{
 
			int y = 107, i;
 
			int pos = w->vscroll.pos;
 

	
 
			if (--pos < 0) {
 
				DrawString(2, y, STR_2045_ACTIONS_AVAILABLE, 0);
 
				y += 10;
 
			}
 
			for (i = 0; buttons; i++, buttons >>= 1) {
 
				if (pos <= -5) break;
 

	
 
				if (buttons&1 && --pos < 0) {
 
					DrawString(3, y, STR_2046_SMALL_ADVERTISING_CAMPAIGN + i, 6);
 
					y += 10;
 
				}
 
			}
 
		}
 

	
 
		{
 
			int i = WP(w,def_d).data_1;
 

	
 
			if (i != -1) {
 
				SetDParam(1, (_price.build_industry >> 8) * _town_action_costs[i]);
 
				SetDParam(0, STR_2046_SMALL_ADVERTISING_CAMPAIGN + i);
 
				DrawStringMultiLine(2, 159, STR_204D_INITIATE_A_SMALL_LOCAL + i, 313);
 
			}
 
		}
 

	
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 3: { /* listbox */
 
			const Town *t = GetTown(w->window_number);
 
			int y = (e->we.click.pt.y - 0x6B) / 10;
 

	
 
			if (!IS_INT_INSIDE(y, 0, 5)) return;
 

	
 
			y = GetNthSetBit(GetMaskOfTownActions(NULL, _local_player, t), y + w->vscroll.pos - 1);
 
			if (y >= 0) {
 
				WP(w,def_d).data_1 = y;
 
				SetWindowDirty(w);
 
			}
 
			break;
 
		}
 

	
 
		case 6: { /* carry out the action */
 
			DoCommandP(GetTown(w->window_number)->xy, w->window_number, WP(w,def_d).data_1, NULL, CMD_DO_TOWN_ACTION | CMD_MSG(STR_00B4_CAN_T_DO_THIS));
 
			break;
 
		}
 
		}
 
		break;
 

	
 
	case WE_4:
 
		SetWindowDirty(w);
 
		break;
 
	}
 
}
 

	
 
static const WindowDesc _town_authority_desc = {
 
	WDP_AUTO, WDP_AUTO, 317, 222,
 
	WC_TOWN_AUTHORITY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_town_authority_widgets,
 
	TownAuthorityWndProc
 
};
 

	
 
static void ShowTownAuthorityWindow(uint town)
 
{
 
	Window *w = AllocateWindowDescFront(&_town_authority_desc, town);
 

	
 
	if (w != NULL) {
 
		w->vscroll.cap = 5;
 
		WP(w,def_d).data_1 = -1;
 
	}
 
}
 

	
 
static void TownViewWndProc(Window *w, WindowEvent *e)
 
{
 
	Town *t = GetTown(w->window_number);
 

	
 
	switch (e->event) {
 
	case WE_PAINT:
 
		// disable renaming town in network games if you are not the server
 
		SetWindowWidgetDisabledState(w, 8, _networking && !_network_server);
 

	
 
		SetDParam(0, t->index);
 
		DrawWindowWidgets(w);
 

	
 
		SetDParam(0, t->population);
 
		SetDParam(1, t->num_houses);
 
		DrawString(2, 107, STR_2006_POPULATION, 0);
 

	
 
		SetDParam(0, t->act_pass);
 
		SetDParam(1, t->max_pass);
 
		DrawString(2, 117, STR_200D_PASSENGERS_LAST_MONTH_MAX, 0);
 

	
 
		SetDParam(0, t->act_mail);
 
		SetDParam(1, t->max_mail);
 
		DrawString(2, 127, STR_200E_MAIL_LAST_MONTH_MAX, 0);
 

	
 
		DrawWindowViewport(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
			case 6: /* scroll to location */
 
				ScrollMainWindowToTile(t->xy);
 
				break;
 

	
 
			case 7: /* town authority */
 
				ShowTownAuthorityWindow(w->window_number);
 
				break;
 

	
 
			case 8: /* rename */
 
				SetDParam(0, w->window_number);
 
				ShowQueryString(STR_TOWN, STR_2007_RENAME_TOWN, 31, 130, w, CS_ALPHANUMERAL);
 
				break;
 

	
 
			case 9: /* expand town */
 
				ExpandTown(t);
 
				break;
 

	
 
			case 10: /* delete town */
 
				DeleteTown(t);
 
				break;
 
		}
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT:
 
		if (e->we.edittext.str[0] != '\0') {
 
			_cmd_text = e->we.edittext.str;
 
			DoCommandP(0, w->window_number, 0, NULL,
 
				CMD_RENAME_TOWN | CMD_MSG(STR_2008_CAN_T_RENAME_TOWN));
 
		}
 
		break;
 
	}
 
}
 

	
 

	
 
static const Widget _town_view_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    13,    11,   247,     0,    13, STR_2005,                 STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    13,   248,   259,     0,    13, 0x0,                      STR_STICKY_BUTTON},
 
{      WWT_PANEL,   RESIZE_NONE,    13,     0,   259,    14,   105, 0x0,                      STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    13,     2,   257,    16,   103, 0x0,                      STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    13,     0,   259,   106,   137, 0x0,                      STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,     0,    85,   138,   149, STR_00E4_LOCATION,        STR_200B_CENTER_THE_MAIN_VIEW_ON},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,    86,   171,   138,   149, STR_2020_LOCAL_AUTHORITY, STR_2021_SHOW_INFORMATION_ON_LOCAL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   172,   259,   138,   149, STR_0130_RENAME,          STR_200C_CHANGE_TOWN_NAME},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _town_view_desc = {
 
	WDP_AUTO, WDP_AUTO, 260, 150,
 
	WC_TOWN_VIEW,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
 
	_town_view_widgets,
 
	TownViewWndProc
 
};
 

	
 
static const Widget _town_view_scen_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    13,    11,   172,     0,    13, STR_2005,          STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    13,   248,   259,     0,    13, 0x0,               STR_STICKY_BUTTON},
 
{      WWT_PANEL,   RESIZE_NONE,    13,     0,   259,    14,   105, 0x0,               STR_NULL},
 
{      WWT_INSET,   RESIZE_NONE,    13,     2,   257,    16,   103, 0x0,               STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    13,     0,   259,   106,   137, 0x0,               STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,     0,    85,   138,   149, STR_00E4_LOCATION, STR_200B_CENTER_THE_MAIN_VIEW_ON},
 
{      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,               STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   173,   247,     0,    13, STR_0130_RENAME,   STR_200C_CHANGE_TOWN_NAME},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,    86,   171,   138,   149, STR_023C_EXPAND,   STR_023B_INCREASE_SIZE_OF_TOWN},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   172,   259,   138,   149, STR_0290_DELETE,   STR_0291_DELETE_THIS_TOWN_COMPLETELY},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _town_view_scen_desc = {
 
	WDP_AUTO, WDP_AUTO, 260, 150,
 
	WC_TOWN_VIEW,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
 
	_town_view_scen_widgets,
 
	TownViewWndProc
 
};
 

	
 
void ShowTownViewWindow(TownID town)
 
{
 
	Window *w;
 

	
 
	if (_game_mode != GM_EDITOR) {
 
		w = AllocateWindowDescFront(&_town_view_desc, town);
 
	} else {
 
		w = AllocateWindowDescFront(&_town_view_scen_desc, town);
 
	}
 

	
 
	if (w != NULL) {
 
		w->flags4 |= WF_DISABLE_VP_SCROLL;
 
		AssignWindowViewport(w, 3, 17, 0xFE, 0x56, GetTown(town)->xy, 1);
 
	}
 
}
 

	
 
static const Widget _town_directory_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,               STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    13,    11,   195,     0,    13, STR_2000_TOWNS,         STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    13,   196,   207,     0,    13, 0x0,                    STR_STICKY_BUTTON},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,     0,    98,    14,    25, STR_SORT_BY_NAME,       STR_SORT_ORDER_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,    13,    99,   195,    14,    25, STR_SORT_BY_POPULATION, STR_SORT_ORDER_TIP},
 
{      WWT_PANEL, RESIZE_BOTTOM,    13,     0,   195,    26,   189, 0x0,                    STR_200A_TOWN_NAMES_CLICK_ON_NAME},
 
{  WWT_SCROLLBAR, RESIZE_BOTTOM,    13,   196,   207,    14,   189, 0x0,                    STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,     RESIZE_TB,    13,     0,   195,   190,   201, 0x0,                    STR_NULL},
 
{  WWT_RESIZEBOX,     RESIZE_TB,    13,   196,   207,   190,   201, 0x0,                    STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 

	
 
// used to get a sorted list of the towns
 
static uint _num_town_sort;
 

	
 
static char _bufcache[64];
 
static const Town* _last_town;
 

	
 
static int CDECL TownNameSorter(const void *a, const void *b)
 
{
 
	const Town* ta = *(const Town**)a;
 
	const Town* tb = *(const Town**)b;
 
	char buf1[64];
 
	int r;
 

	
 
	SetDParam(0, ta->index);
 
	GetString(buf1, STR_TOWN, lastof(buf1));
 

	
 
	/* If 'b' is the same town as in the last round, use the cached value
 
	 *  We do this to speed stuff up ('b' is called with the same value a lot of
 
	 *  times after eachother) */
 
	if (tb != _last_town) {
 
		_last_town = tb;
 
		SetDParam(0, tb->index);
 
		GetString(_bufcache, STR_TOWN, lastof(_bufcache));
 
	}
 

	
 
	r = strcmp(buf1, _bufcache);
 
	if (_town_sort_order & 1) r = -r;
 
	return r;
 
}
 

	
 
static int CDECL TownPopSorter(const void *a, const void *b)
 
{
 
	const Town* ta = *(const Town**)a;
 
	const Town* tb = *(const Town**)b;
 
	int r = ta->population - tb->population;
 
	if (_town_sort_order & 1) r = -r;
 
	return r;
 
}
 

	
 
static void MakeSortedTownList(void)
 
{
 
	const Town* t;
 
	uint n = 0;
 

	
 
	/* Create array for sorting */
 
	_town_sort = realloc((void*)_town_sort, (GetMaxTownIndex() + 1) * sizeof(_town_sort[0]));
 
	if (_town_sort == NULL) error("Could not allocate memory for the town-sorting-list");
 

	
 
	FOR_ALL_TOWNS(t) _town_sort[n++] = t;
 

	
 
	_num_town_sort = n;
 

	
 
	_last_town = NULL; // used for "cache"
 
	qsort((void*)_town_sort, n, sizeof(_town_sort[0]), _town_sort_order & 2 ? TownPopSorter : TownNameSorter);
 

	
 
	DEBUG(misc, 3, "Resorting towns list");
 
}
 

	
 

	
 
static void TownDirectoryWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		if (_town_sort_dirty) {
 
			_town_sort_dirty = false;
 
			MakeSortedTownList();
 
		}
 

	
 
		SetVScrollCount(w, _num_town_sort);
 

	
 
		DrawWindowWidgets(w);
 
		DoDrawString(_town_sort_order & 1 ? DOWNARROW : UPARROW, (_town_sort_order <= 1) ? 88 : 187, 15, 0x10);
 

	
 
		{
 
			int n = 0;
 
			uint16 i = w->vscroll.pos;
 
			int y = 28;
 

	
 
			while (i < _num_town_sort) {
 
				const Town* t = _town_sort[i];
 

	
 
				assert(t->xy);
 

	
 
				SetDParam(0, t->index);
 
				SetDParam(1, t->population);
 
				DrawString(2, y, STR_2057, 0);
 

	
 
				y += 10;
 
				i++;
 
				if (++n == w->vscroll.cap) break; // max number of towns in 1 window
 
			}
 
			SetDParam(0, GetWorldPopulation());
 
			DrawString(3, w->height - 12 + 2, STR_TOWN_POPULATION, 0);
 
		}
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 3: { /* Sort by Name ascending/descending */
 
			_town_sort_order = (_town_sort_order == 0) ? 1 : 0;
 
			_town_sort_dirty = true;
 
			SetWindowDirty(w);
 
		} break;
 

	
 
		case 4: { /* Sort by Population ascending/descending */
 
			_town_sort_order = (_town_sort_order == 2) ? 3 : 2;
 
			_town_sort_dirty = true;
 
			SetWindowDirty(w);
 
		} break;
 

	
 
		case 5: { /* Click on Town Matrix */
 
			const Town* t;
 

	
 
			uint16 id_v = (e->we.click.pt.y - 28) / 10;
 

	
 
			if (id_v >= w->vscroll.cap) return; // click out of bounds
 

	
 
			id_v += w->vscroll.pos;
 

	
 
			if (id_v >= _num_town_sort) return; // click out of town bounds
 

	
 
			t = _town_sort[id_v];
 
			assert(t->xy);
 
			ScrollMainWindowToTile(t->xy);
 
			break;
 
		}
 
		}
 
		break;
 

	
 
	case WE_4:
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_RESIZE:
 
		w->vscroll.cap += e->we.sizing.diff.y / 10;
 
		break;
 
	}
 
}
 

	
 
static const WindowDesc _town_directory_desc = {
 
	WDP_AUTO, WDP_AUTO, 208, 202,
 
	WC_TOWN_DIRECTORY,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_town_directory_widgets,
 
	TownDirectoryWndProc
 
};
 

	
 

	
 
void ShowTownDirectory(void)
 
{
 
	Window *w = AllocateWindowDescFront(&_town_directory_desc, 0);
 

	
 
	if (w != NULL) {
 
		w->vscroll.cap = 16;
 
		w->resize.step_height = 10;
 
		w->resize.height = w->height - 10 * 6; // minimum of 10 items in the list, each item 10 high
 
	}
 
}
src/train_cmd.c
Show inline comments
 
deleted file
src/train_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "gui.h"
 
#include "station_map.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "tunnel_map.h"
 
#include "vehicle.h"
 
#include "command.h"
 
#include "pathfind.h"
 
#include "npf.h"
 
#include "station.h"
 
#include "table/train_cmd.h"
 
#include "news.h"
 
#include "engine.h"
 
#include "player.h"
 
#include "sound.h"
 
#include "depot.h"
 
#include "waypoint.h"
 
#include "vehicle_gui.h"
 
#include "train.h"
 
#include "bridge.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_sound.h"
 
#include "newgrf_text.h"
 
#include "direction.h"
 
#include "yapf/yapf.h"
 
#include "date.h"
 

	
 
static bool TrainCheckIfLineEnds(Vehicle *v);
 
static void TrainController(Vehicle *v, bool update_image);
 

	
 
static const byte _vehicle_initial_x_fract[4] = {10, 8, 4,  8};
 
static const byte _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
 
static const byte _state_dir_table[4] = { 0x20, 8, 0x10, 4 };
 

	
 

	
 
/** Return the cargo weight multiplier to use for a rail vehicle
 
 * @param cargo Cargo type to get multiplier for
 
 * @return Cargo weight multiplier
 
 */
 
byte FreightWagonMult(CargoID cargo)
 
{
 
	// XXX NewCargos introduces a specific "is freight" flag for this test.
 
	if (cargo == CT_PASSENGERS || cargo == CT_MAIL) return 1;
 
	return _patches.freight_trains;
 
}
 

	
 

	
 
/**
 
 * Recalculates the cached total power of a train. Should be called when the consist is changed
 
 * @param v First vehicle of the consist.
 
 */
 
void TrainPowerChanged(Vehicle* v)
 
{
 
	Vehicle* u;
 
	uint32 power = 0;
 
	uint32 max_te = 0;
 

	
 
	for (u = v; u != NULL; u = u->next) {
 
		const RailVehicleInfo *rvi_u;
 
		bool engine_has_power = true;
 
		bool wagon_has_power = true;
 

	
 
		/* Power is not added for articulated parts */
 
		if (IsArticulatedPart(u)) continue;
 

	
 
		if (IsLevelCrossingTile(u->tile)) {
 
			if (!HasPowerOnRail(u->u.rail.railtype, GetRailTypeCrossing(u->tile))) engine_has_power = false;
 
			if (!HasPowerOnRail(v->u.rail.railtype, GetRailTypeCrossing(u->tile))) wagon_has_power = false;
 
		} else {
 
			if (!HasPowerOnRail(u->u.rail.railtype, GetRailType(u->tile))) engine_has_power = false;
 
			if (!HasPowerOnRail(v->u.rail.railtype, GetRailType(u->tile))) wagon_has_power = false;
 
		}
 

	
 
		rvi_u = RailVehInfo(u->engine_type);
 

	
 
		if (engine_has_power && rvi_u->power != 0) {
 
			power += rvi_u->power;
 
			/* Tractive effort in (tonnes * 1000 * 10 =) N */
 
			max_te += (u->u.rail.cached_veh_weight * 10000 * rvi_u->tractive_effort) / 256;
 
		}
 

	
 
		if (HASBIT(u->u.rail.flags, VRF_POWEREDWAGON) && (wagon_has_power)) {
 
			power += RailVehInfo(u->u.rail.first_engine)->pow_wag_power;
 
		}
 
	}
 

	
 
	if (v->u.rail.cached_power != power || v->u.rail.cached_max_te != max_te) {
 
		v->u.rail.cached_power = power;
 
		v->u.rail.cached_max_te = max_te;
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 
}
 

	
 

	
 
/**
 
 * Recalculates the cached weight of a train and its vehicles. Should be called each time the cargo on
 
 * the consist changes.
 
 * @param v First vehicle of the consist.
 
 */
 
static void TrainCargoChanged(Vehicle* v)
 
{
 
	Vehicle *u;
 
	uint32 weight = 0;
 

	
 
	for (u = v; u != NULL; u = u->next) {
 
		const RailVehicleInfo *rvi = RailVehInfo(u->engine_type);
 
		uint32 vweight = (_cargoc.weights[u->cargo_type] * u->cargo_count * FreightWagonMult(u->cargo_type)) / 16;
 

	
 
		// Vehicle weight is not added for articulated parts.
 
		if (!IsArticulatedPart(u)) {
 
			// vehicle weight is the sum of the weight of the vehicle and the weight of its cargo
 
			vweight += rvi->weight;
 

	
 
			// powered wagons have extra weight added
 
			if (HASBIT(u->u.rail.flags, VRF_POWEREDWAGON))
 
				vweight += RailVehInfo(u->u.rail.first_engine)->pow_wag_weight;
 
		}
 

	
 
		// consist weight is the sum of the weight of all vehicles in the consist
 
		weight += vweight;
 

	
 
		// store vehicle weight in cache
 
		u->u.rail.cached_veh_weight = vweight;
 
	};
 

	
 
	// store consist weight in cache
 
	v->u.rail.cached_weight = weight;
 

	
 
	/* Now update train power (tractive effort is dependent on weight) */
 
	TrainPowerChanged(v);
 
}
 

	
 

	
 
/**
 
 * Recalculates the cached stuff of a train. Should be called each time a vehicle is added
 
 * to/removed from the chain, and when the game is loaded.
 
 * Note: this needs to be called too for 'wagon chains' (in the depot, without an engine)
 
 * @param v First vehicle of the chain.
 
 */
 
void TrainConsistChanged(Vehicle* v)
 
{
 
	const RailVehicleInfo *rvi_v;
 
	Vehicle *u;
 
	uint16 max_speed = 0xFFFF;
 
	EngineID first_engine;
 

	
 
	assert(v->type == VEH_Train);
 

	
 
	assert(IsFrontEngine(v) || IsFreeWagon(v));
 

	
 
	rvi_v = RailVehInfo(v->engine_type);
 
	first_engine = IsFrontEngine(v) ? v->engine_type : INVALID_ENGINE;
 
	v->u.rail.cached_total_length = 0;
 
	v->u.rail.compatible_railtypes = 0;
 

	
 
	for (u = v; u != NULL; u = u->next) {
 
		const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type);
 
		uint16 veh_len;
 

	
 
		// Update the v->first cache. This is faster than having to brute force it later.
 
		if (u->first == NULL) u->first = v;
 

	
 
		// update the 'first engine'
 
		u->u.rail.first_engine = (v == u) ? INVALID_ENGINE : first_engine;
 
		u->u.rail.railtype = GetEngine(u->engine_type)->railtype;
 

	
 
		if (IsTrainEngine(u)) first_engine = u->engine_type;
 

	
 
		if (rvi_u->visual_effect != 0) {
 
			u->u.rail.cached_vis_effect = rvi_u->visual_effect;
 
		} else {
 
			if (IsTrainWagon(u) || IsArticulatedPart(u)) {
 
				// Wagons and articulated parts have no effect by default
 
				u->u.rail.cached_vis_effect = 0x40;
 
			} else if (rvi_u->engclass == 0) {
 
				// Steam is offset by -4 units
 
				u->u.rail.cached_vis_effect = 4;
 
			} else {
 
				// Diesel fumes and sparks come from the centre
 
				u->u.rail.cached_vis_effect = 8;
 
			}
 
		}
 

	
 
		if (!IsArticulatedPart(u)) {
 
			// check if its a powered wagon
 
			CLRBIT(u->u.rail.flags, VRF_POWEREDWAGON);
 

	
 
			/* Check powered wagon / visual effect callback */
 
			if (HASBIT(EngInfo(u->engine_type)->callbackmask, CBM_WAGON_POWER)) {
 
				uint16 callback = GetVehicleCallback(CBID_TRAIN_WAGON_POWER, 0, 0, u->engine_type, u);
 

	
 
				if (callback != CALLBACK_FAILED) u->u.rail.cached_vis_effect = callback;
 
			}
 

	
 
			if ((rvi_v->pow_wag_power != 0) && (rvi_u->flags & RVI_WAGON) && UsesWagonOverride(u)) {
 
				if (u->u.rail.cached_vis_effect < 0x40) {
 
					/* wagon is powered */
 
					SETBIT(u->u.rail.flags, VRF_POWEREDWAGON); // cache 'powered' status
 
				}
 
			}
 

	
 
			/* Do not count powered wagons for the compatible railtypes, as wagons always
 
			   have railtype normal */
 
			if (rvi_u->power > 0) {
 
				v->u.rail.compatible_railtypes |= GetRailTypeInfo(u->u.rail.railtype)->powered_railtypes;
 
			}
 

	
 
			/* Some electric engines can be allowed to run on normal rail. It happens to all
 
			 * existing electric engines when elrails are disabled and then re-enabled */
 
			if (HASBIT(u->u.rail.flags, VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL)) {
 
				u->u.rail.railtype = RAILTYPE_RAIL;
 
				u->u.rail.compatible_railtypes |= (1 << RAILTYPE_RAIL);
 
			}
 

	
 
			// max speed is the minimum of the speed limits of all vehicles in the consist
 
			if (!(rvi_u->flags & RVI_WAGON) || _patches.wagon_speed_limits)
 
				if (rvi_u->max_speed != 0 && !UsesWagonOverride(u))
 
					max_speed = min(rvi_u->max_speed, max_speed);
 
		}
 

	
 
		// check the vehicle length (callback)
 
		veh_len = CALLBACK_FAILED;
 
		if (HASBIT(EngInfo(u->engine_type)->callbackmask, CBM_VEHICLE_LENGTH)) {
 
			veh_len = GetVehicleCallback(CBID_TRAIN_VEHICLE_LENGTH, 0, 0, u->engine_type, u);
 
		}
 
		if (veh_len == CALLBACK_FAILED) veh_len = rvi_u->shorten_factor;
 
		veh_len = clamp(veh_len, 0, u->next == NULL ? 7 : 5); // the clamp on vehicles not the last in chain is stricter, as too short wagons can break the 'follow next vehicle' code
 
		u->u.rail.cached_veh_length = 8 - veh_len;
 
		v->u.rail.cached_total_length += u->u.rail.cached_veh_length;
 

	
 
	};
 

	
 
	// store consist weight/max speed in cache
 
	v->u.rail.cached_max_speed = max_speed;
 

	
 
	// recalculate cached weights and power too (we do this *after* the rest, so it is known which wagons are powered and need extra weight added)
 
	TrainCargoChanged(v);
 
}
 

	
 
/* These two arrays are used for realistic acceleration. XXX: How should they
 
 * be interpreted? */
 
static const byte _curve_neighbours45[8][2] = {
 
	{7, 1},
 
	{0, 2},
 
	{1, 3},
 
	{2, 4},
 
	{3, 5},
 
	{4, 6},
 
	{5, 7},
 
	{6, 0},
 
};
 

	
 
static const byte _curve_neighbours90[8][2] = {
 
	{6, 2},
 
	{7, 3},
 
	{0, 4},
 
	{1, 5},
 
	{2, 6},
 
	{3, 7},
 
	{4, 0},
 
	{5, 1},
 
};
 

	
 
enum AccelType {
 
	AM_ACCEL,
 
	AM_BRAKE
 
};
 

	
 
static bool TrainShouldStop(const Vehicle* v, TileIndex tile)
 
{
 
	const Order* o = &v->current_order;
 
	StationID sid = GetStationIndex(tile);
 

	
 
	assert(v->type == VEH_Train);
 
	//When does a train drive through a station
 
	//first we deal with the "new nonstop handling"
 
	if (_patches.new_nonstop && o->flags & OF_NON_STOP && sid == o->dest) {
 
		return false;
 
	}
 

	
 
	if (v->last_station_visited == sid) return false;
 

	
 
	if (sid != o->dest && (o->flags & OF_NON_STOP || _patches.new_nonstop)) {
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
//new acceleration
 
static int GetTrainAcceleration(Vehicle *v, bool mode)
 
{
 
	const Vehicle *u;
 
	int num = 0; //number of vehicles, change this into the number of axles later
 
	int power = 0;
 
	int mass = 0;
 
	int max_speed = 2000;
 
	int area = 120;
 
	int friction = 35; //[1e-3]
 
	int drag_coeff = 20; //[1e-4]
 
	int incl = 0;
 
	int resistance;
 
	int speed = v->cur_speed; //[mph]
 
	int force = 0x3FFFFFFF;
 
	int pos = 0;
 
	int lastpos = -1;
 
	int curvecount[2] = {0, 0};
 
	int sum = 0;
 
	int numcurve = 0;
 
	int max_te = v->u.rail.cached_max_te; // [N]
 

	
 
	speed *= 10;
 
	speed /= 16;
 

	
 
	//first find the curve speed limit
 
	for (u = v; u->next != NULL; u = u->next, pos++) {
 
		Direction dir = u->direction;
 
		Direction ndir = u->next->direction;
 
		int i;
 

	
 
		for (i = 0; i < 2; i++) {
 
			if ( _curve_neighbours45[dir][i] == ndir) {
 
				curvecount[i]++;
 
				if (lastpos != -1) {
 
					numcurve++;
 
					sum += pos - lastpos;
 
					if (pos - lastpos == 1) {
 
						max_speed = 88;
 
					}
 
				}
 
				lastpos = pos;
 
			}
 
		}
 

	
 
		//if we have a 90 degree turn, fix the speed limit to 60
 
		if (_curve_neighbours90[dir][0] == ndir ||
 
				_curve_neighbours90[dir][1] == ndir) {
 
			max_speed = 61;
 
		}
 
	}
 

	
 
	if (numcurve > 0) sum /= numcurve;
 

	
 
	if ((curvecount[0] != 0 || curvecount[1] != 0) && max_speed > 88) {
 
		int total = curvecount[0] + curvecount[1];
 

	
 
		if (curvecount[0] == 1 && curvecount[1] == 1) {
 
			max_speed = 0xFFFF;
 
		} else if (total > 1) {
 
			max_speed = 232 - (13 - clamp(sum, 1, 12)) * (13 - clamp(sum, 1, 12));
 
		}
 
	}
 

	
 
	max_speed += (max_speed / 2) * v->u.rail.railtype;
 

	
 
	if (IsTileType(v->tile, MP_STATION) && IsFrontEngine(v)) {
 
		if (TrainShouldStop(v, v->tile)) {
 
			uint station_length = GetPlatformLength(v->tile, DirToDiagDir(v->direction));
 
			int delta_v;
 

	
 
			max_speed = 120;
 

	
 
			delta_v = v->cur_speed / (station_length + 1);
 
			if (v->max_speed > (v->cur_speed - delta_v))
 
				max_speed = v->cur_speed - (delta_v / 10);
 

	
 
			max_speed = max(max_speed, 25 * station_length);
 
		}
 
	}
 

	
 
	mass = v->u.rail.cached_weight;
 
	power = v->u.rail.cached_power * 746;
 
	max_speed = min(max_speed, v->u.rail.cached_max_speed);
 

	
 
	for (u = v; u != NULL; u = u->next) {
 
		num++;
 
		drag_coeff += 3;
 

	
 
		if (u->u.rail.track == 0x80) max_speed = min(max_speed, 61);
 

	
 
		if (HASBIT(u->u.rail.flags, VRF_GOINGUP)) {
 
			incl += u->u.rail.cached_veh_weight * 60; //3% slope, quite a bit actually
 
		} else if (HASBIT(u->u.rail.flags, VRF_GOINGDOWN)) {
 
			incl -= u->u.rail.cached_veh_weight * 60;
 
		}
 
	}
 

	
 
	v->max_speed = max_speed;
 

	
 
	if (v->u.rail.railtype != RAILTYPE_MAGLEV) {
 
		resistance = 13 * mass / 10;
 
		resistance += 60 * num;
 
		resistance += friction * mass * speed / 1000;
 
		resistance += (area * drag_coeff * speed * speed) / 10000;
 
	} else {
 
		resistance = (area * (drag_coeff / 2) * speed * speed) / 10000;
 
	}
 
	resistance += incl;
 
	resistance *= 4; //[N]
 

	
 
	/* Due to the mph to m/s conversion below, at speeds below 3 mph the force is
 
	 * actually double the train's power */
 
	if (speed > 2) {
 
		switch (v->u.rail.railtype) {
 
			case RAILTYPE_RAIL:
 
			case RAILTYPE_ELECTRIC:
 
			case RAILTYPE_MONO:
 
				force = power / speed; //[N]
 
				force *= 22;
 
				force /= 10;
 
				if (mode == AM_ACCEL && force > max_te) force = max_te;
 
				break;
 

	
 
			case RAILTYPE_MAGLEV:
 
				force = power / 25;
 
				break;
 
		}
 
	} else {
 
		//"kickoff" acceleration
 
		force = (mode == AM_ACCEL && v->u.rail.railtype != RAILTYPE_MAGLEV) ? min(max_te, power) : power;
 
		force = max(force, (mass * 8) + resistance);
 
	}
 

	
 
	if (force <= 0) force = 10000;
 

	
 
	if (v->u.rail.railtype != RAILTYPE_MAGLEV) force = min(force, mass * 10 * 200);
 

	
 
	if (mode == AM_ACCEL) {
 
		return (force - resistance) / (mass * 4);
 
	} else {
 
		return min((-force - resistance) / (mass * 4), -10000 / (mass * 4));
 
	}
 
}
 

	
 
static void UpdateTrainAcceleration(Vehicle* v)
 
{
 
	uint power = 0;
 
	uint weight = 0;
 

	
 
	assert(IsFrontEngine(v));
 

	
 
	weight = v->u.rail.cached_weight;
 
	power = v->u.rail.cached_power;
 
	v->max_speed = v->u.rail.cached_max_speed;
 

	
 
	assert(weight != 0);
 

	
 
	v->acceleration = clamp(power / weight * 4, 1, 255);
 
}
 

	
 
int GetTrainImage(const Vehicle* v, Direction direction)
 
{
 
	int img = v->spritenum;
 
	int base;
 

	
 
	if (HASBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION)) direction = ReverseDir(direction);
 

	
 
	if (is_custom_sprite(img)) {
 
		base = GetCustomVehicleSprite(v, direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE(img));
 
		if (base != 0) return base;
 
		img = orig_rail_vehicle_info[v->engine_type].image_index;
 
	}
 

	
 
	base = _engine_sprite_base[img] + ((direction + _engine_sprite_add[img]) & _engine_sprite_and[img]);
 

	
 
	if (v->cargo_count >= v->cargo_cap / 2) base += _wagon_full_adder[img];
 
	return base;
 
}
 

	
 
void DrawTrainEngine(int x, int y, EngineID engine, uint32 image_ormod)
 
{
 
	const RailVehicleInfo *rvi = RailVehInfo(engine);
 

	
 
	int img = rvi->image_index;
 
	uint32 image = 0;
 

	
 
	if (is_custom_sprite(img)) {
 
		image = GetCustomVehicleIcon(engine, DIR_W);
 
		if (image == 0) {
 
			img = orig_rail_vehicle_info[engine].image_index;
 
		} else {
 
			y += _traininfo_vehicle_pitch;
 
		}
 
	}
 
	if (image == 0) {
 
		image = (6 & _engine_sprite_and[img]) + _engine_sprite_base[img];
 
	}
 

	
 
	if (rvi->flags & RVI_MULTIHEAD) {
 
		DrawSprite(image | image_ormod, x - 14, y);
 
		x += 15;
 
		image = 0;
 
		if (is_custom_sprite(img)) {
 
			image = GetCustomVehicleIcon(engine, 2);
 
			if (image == 0) img = orig_rail_vehicle_info[engine].image_index;
 
		}
 
		if (image == 0) {
 
			image =
 
				((6 + _engine_sprite_add[img + 1]) & _engine_sprite_and[img + 1]) +
 
				_engine_sprite_base[img + 1];
 
		}
 
	}
 
	DrawSprite(image | image_ormod, x, y);
 
}
 

	
 
uint CountArticulatedParts(EngineID engine_type)
 
{
 
	uint16 callback;
 
	uint i;
 

	
 
	if (!HASBIT(EngInfo(engine_type)->callbackmask, CBM_ARTIC_ENGINE)) return 0;
 

	
 
	for (i = 1; i < 10; i++) {
 
		callback = GetVehicleCallback(CBID_TRAIN_ARTIC_ENGINE, i, 0, engine_type, NULL);
 
		if (callback == CALLBACK_FAILED || callback == 0xFF) break;
 
	}
 

	
 
	return i - 1;
 
}
 

	
 
static void AddArticulatedParts(Vehicle **vl)
 
{
 
	const RailVehicleInfo *rvi_artic;
 
	EngineID engine_type;
 
	Vehicle *v = vl[0];
 
	Vehicle *u = v;
 
	uint16 callback;
 
	bool flip_image;
 
	uint i;
 

	
 
	if (!HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_ARTIC_ENGINE)) return;
 

	
 
	for (i = 1; i < 10; i++) {
 
		callback = GetVehicleCallback(CBID_TRAIN_ARTIC_ENGINE, i, 0, v->engine_type, v);
 
		if (callback == CALLBACK_FAILED || callback == 0xFF) return;
 

	
 
		/* Attempt to use pre-allocated vehicles until they run out. This can happen
 
		 * if the callback returns different values depending on the cargo type. */
 
		u->next = vl[i];
 
		if (u->next == NULL) u->next = AllocateVehicle();
 
		if (u->next == NULL) return;
 

	
 
		u = u->next;
 

	
 
		engine_type = GB(callback, 0, 7);
 
		flip_image = HASBIT(callback, 7);
 
		rvi_artic = RailVehInfo(engine_type);
 

	
 
		// get common values from first engine
 
		u->direction = v->direction;
 
		u->owner = v->owner;
 
		u->tile = v->tile;
 
		u->x_pos = v->x_pos;
 
		u->y_pos = v->y_pos;
 
		u->z_pos = v->z_pos;
 
		u->z_height = v->z_height;
 
		u->u.rail.track = v->u.rail.track;
 
		u->u.rail.railtype = v->u.rail.railtype;
 
		u->build_year = v->build_year;
 
		u->vehstatus = v->vehstatus & ~VS_STOPPED;
 
		u->u.rail.first_engine = v->engine_type;
 

	
 
		// get more settings from rail vehicle info
 
		u->spritenum = rvi_artic->image_index;
 
		if (flip_image) u->spritenum++;
 
		u->cargo_type = rvi_artic->cargo_type;
 
		u->cargo_subtype = 0;
 
		u->cargo_cap = rvi_artic->capacity;
 
		u->max_speed = 0;
 
		u->max_age = 0;
 
		u->engine_type = engine_type;
 
		u->value = 0;
 
		u->type = VEH_Train;
 
		u->subtype = 0;
 
		SetArticulatedPart(u);
 
		u->cur_image = 0xAC2;
 
		u->random_bits = VehicleRandomBits();
 

	
 
		VehiclePositionChanged(u);
 
	}
 
}
 

	
 
static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags)
 
{
 
	int32 value;
 
	const RailVehicleInfo *rvi;
 
	uint num_vehicles;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	rvi = RailVehInfo(engine);
 
	value = (rvi->base_cost * _price.build_railwagon) >> 8;
 

	
 
	num_vehicles = 1 + CountArticulatedParts(engine);
 

	
 
	if (!(flags & DC_QUERY_COST)) {
 
		Vehicle *vl[11]; // Allow for wagon and upto 10 artic parts.
 
		Vehicle* v;
 
		int x;
 
		int y;
 

	
 
		memset(&vl, 0, sizeof(vl));
 

	
 
		if (!AllocateVehicles(vl, num_vehicles))
 
			return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
		if (flags & DC_EXEC) {
 
			Vehicle *u, *w;
 
			DiagDirection dir;
 

	
 
			v = vl[0];
 
			v->spritenum = rvi->image_index;
 

	
 
			u = NULL;
 

	
 
			FOR_ALL_VEHICLES(w) {
 
				if (w->type == VEH_Train && w->tile == tile &&
 
				    IsFreeWagon(w) && w->engine_type == engine) {
 
					u = GetLastVehicleInChain(w);
 
					break;
 
				}
 
			}
 

	
 
			v->engine_type = engine;
 

	
 
			dir = GetRailDepotDirection(tile);
 

	
 
			v->direction = DiagDirToDir(dir);
 
			v->tile = tile;
 

	
 
			x = TileX(tile) * TILE_SIZE | _vehicle_initial_x_fract[dir];
 
			y = TileY(tile) * TILE_SIZE | _vehicle_initial_y_fract[dir];
 

	
 
			v->x_pos = x;
 
			v->y_pos = y;
 
			v->z_pos = GetSlopeZ(x,y);
 
			v->owner = _current_player;
 
			v->z_height = 6;
 
			v->u.rail.track = 0x80;
 
			v->vehstatus = VS_HIDDEN | VS_DEFPAL;
 

	
 
			v->subtype = 0;
 
			SetTrainWagon(v);
 
			if (u != NULL) {
 
				u->next = v;
 
			} else {
 
				SetFreeWagon(v);
 
				InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
			}
 

	
 
			v->cargo_type = rvi->cargo_type;
 
			v->cargo_subtype = 0;
 
			v->cargo_cap = rvi->capacity;
 
			v->value = value;
 
//			v->day_counter = 0;
 

	
 
			v->u.rail.railtype = GetEngine(engine)->railtype;
 

	
 
			v->build_year = _cur_year;
 
			v->type = VEH_Train;
 
			v->cur_image = 0xAC2;
 
			v->random_bits = VehicleRandomBits();
 

	
 
			AddArticulatedParts(vl);
 

	
 
			_new_vehicle_id = v->index;
 

	
 
			VehiclePositionChanged(v);
 
			TrainConsistChanged(GetFirstVehicleInChain(v));
 
			GetPlayer(_current_player)->num_engines[engine]++;
 

	
 
			InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
			if (IsLocalPlayer()) {
 
				InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train); // updates the replace Train window
 
			}
 
		}
 
	}
 

	
 
	return value;
 
}
 

	
 
// Move all free vehicles in the depot to the train
 
static void NormalizeTrainVehInDepot(const Vehicle* u)
 
{
 
	const Vehicle* v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Train && IsFreeWagon(v) &&
 
				v->tile == u->tile &&
 
				v->u.rail.track == 0x80) {
 
			if (CmdFailed(DoCommand(0, v->index | (u->index << 16), 1, DC_EXEC,
 
					CMD_MOVE_RAIL_VEHICLE)))
 
				break;
 
		}
 
	}
 
}
 

	
 
static int32 EstimateTrainCost(const RailVehicleInfo* rvi)
 
{
 
	return rvi->base_cost * (_price.build_railvehicle >> 3) >> 5;
 
}
 

	
 
static void AddRearEngineToMultiheadedTrain(Vehicle* v, Vehicle* u, bool building)
 
{
 
	u->direction = v->direction;
 
	u->owner = v->owner;
 
	u->tile = v->tile;
 
	u->x_pos = v->x_pos;
 
	u->y_pos = v->y_pos;
 
	u->z_pos = v->z_pos;
 
	u->z_height = 6;
 
	u->u.rail.track = 0x80;
 
	u->vehstatus = v->vehstatus & ~VS_STOPPED;
 
	u->subtype = 0;
 
	SetMultiheaded(u);
 
	u->spritenum = v->spritenum + 1;
 
	u->cargo_type = v->cargo_type;
 
	u->cargo_subtype = v->cargo_subtype;
 
	u->cargo_cap = v->cargo_cap;
 
	u->u.rail.railtype = v->u.rail.railtype;
 
	if (building) v->next = u;
 
	u->engine_type = v->engine_type;
 
	u->build_year = v->build_year;
 
	if (building) v->value >>= 1;
 
	u->value = v->value;
 
	u->type = VEH_Train;
 
	u->cur_image = 0xAC2;
 
	u->random_bits = VehicleRandomBits();
 
	VehiclePositionChanged(u);
 
}
 

	
 
/** Build a railroad vehicle.
 
 * @param tile tile of the depot where rail-vehicle is built
 
 * @param p1 engine type id
 
 * @param p2 bit 0 when set, the train will get number 0, otherwise it will get a free number
 
 *           bit 1 prevents any free cars from being added to the train
 
 */
 
int32 CmdBuildRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	const RailVehicleInfo *rvi;
 
	int value;
 
	Vehicle *v;
 
	UnitID unit_num;
 
	Engine *e;
 
	uint num_vehicles;
 

	
 
	/* Check if the engine-type is valid (for the player) */
 
	if (!IsEngineBuildable(p1, VEH_Train, _current_player)) return_cmd_error(STR_ENGINE_NOT_BUILDABLE);
 

	
 
	/* Check if the train is actually being built in a depot belonging
 
	 * to the player. Doesn't matter if only the cost is queried */
 
	if (!(flags & DC_QUERY_COST)) {
 
		if (!IsTileDepotType(tile, TRANSPORT_RAIL)) return CMD_ERROR;
 
		if (!IsTileOwner(tile, _current_player)) return CMD_ERROR;
 
	}
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	rvi = RailVehInfo(p1);
 
	e = GetEngine(p1);
 

	
 
	/* Check if depot and new engine uses the same kind of tracks */
 
	/* We need to see if the engine got power on the tile to avoid eletric engines in non-electric depots */
 
	if (!HasPowerOnRail(e->railtype, GetRailType(tile))) return CMD_ERROR;
 

	
 
	if (rvi->flags & RVI_WAGON) return CmdBuildRailWagon(p1, tile, flags);
 

	
 
	value = EstimateTrainCost(rvi);
 

	
 
	num_vehicles = (rvi->flags & RVI_MULTIHEAD) ? 2 : 1;
 
	num_vehicles += CountArticulatedParts(p1);
 

	
 
	if (!(flags & DC_QUERY_COST)) {
 
		Vehicle *vl[12]; // Allow for upto 10 artic parts and dual-heads
 

	
 
		memset(&vl, 0, sizeof(vl));
 

	
 
		if (!AllocateVehicles(vl, num_vehicles) || IsOrderPoolFull())
 
			return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
		v = vl[0];
 

	
 
		unit_num = HASBIT(p2, 0) ? 0 : GetFreeUnitNumber(VEH_Train);
 
		if (unit_num > _patches.max_trains)
 
			return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
		if (flags & DC_EXEC) {
 
			DiagDirection dir = GetRailDepotDirection(tile);
 
			int x = TileX(tile) * TILE_SIZE + _vehicle_initial_x_fract[dir];
 
			int y = TileY(tile) * TILE_SIZE + _vehicle_initial_y_fract[dir];
 

	
 
			v->unitnumber = unit_num;
 
			v->direction = DiagDirToDir(dir);
 
			v->tile = tile;
 
			v->owner = _current_player;
 
			v->x_pos = x;
 
			v->y_pos = y;
 
			v->z_pos = GetSlopeZ(x,y);
 
			v->z_height = 6;
 
			v->u.rail.track = 0x80;
 
			v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
 
			v->spritenum = rvi->image_index;
 
			v->cargo_type = rvi->cargo_type;
 
			v->cargo_subtype = 0;
 
			v->cargo_cap = rvi->capacity;
 
			v->max_speed = rvi->max_speed;
 
			v->value = value;
 
			v->last_station_visited = INVALID_STATION;
 
			v->dest_tile = 0;
 

	
 
			v->engine_type = p1;
 

	
 
			v->reliability = e->reliability;
 
			v->reliability_spd_dec = e->reliability_spd_dec;
 
			v->max_age = e->lifelength * 366;
 

	
 
			v->string_id = STR_SV_TRAIN_NAME;
 
			v->u.rail.railtype = e->railtype;
 
			_new_vehicle_id = v->index;
 

	
 
			v->service_interval = _patches.servint_trains;
 
			v->date_of_last_service = _date;
 
			v->build_year = _cur_year;
 
			v->type = VEH_Train;
 
			v->cur_image = 0xAC2;
 
			v->random_bits = VehicleRandomBits();
 

	
 
			v->subtype = 0;
 
			SetFrontEngine(v);
 
			SetTrainEngine(v);
 

	
 
			VehiclePositionChanged(v);
 

	
 
			if (rvi->flags & RVI_MULTIHEAD) {
 
				SetMultiheaded(v);
 
				AddRearEngineToMultiheadedTrain(vl[0], vl[1], true);
 
				/* Now we need to link the front and rear engines together
 
				 * other_multiheaded_part is the pointer that links to the other half of the engine
 
				 * vl[0] is the front and vl[1] is the rear
 
				 */
 
				vl[0]->u.rail.other_multiheaded_part = vl[1];
 
				vl[1]->u.rail.other_multiheaded_part = vl[0];
 
			} else {
 
				AddArticulatedParts(vl);
 
			}
 

	
 
			TrainConsistChanged(v);
 
			UpdateTrainAcceleration(v);
 

	
 
			if (!HASBIT(p2, 1)) { // check if the cars should be added to the new vehicle
 
				NormalizeTrainVehInDepot(v);
 
			}
 

	
 
			GetPlayer(_current_player)->num_engines[p1]++;
 
			InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
			RebuildVehicleLists();
 
			InvalidateWindow(WC_COMPANY, v->owner);
 
			if (IsLocalPlayer()) {
 
				InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train); // updates the replace Train window
 
			}
 
		}
 
	}
 

	
 
	return value;
 
}
 

	
 

	
 
/* Check if all the wagons of the given train are in a depot, returns the
 
 * number of cars (including loco) then. If not it returns -1 */
 
int CheckTrainInDepot(const Vehicle *v, bool needs_to_be_stopped)
 
{
 
	int count;
 
	TileIndex tile = v->tile;
 

	
 
	/* check if stopped in a depot */
 
	if (!IsTileDepotType(tile, TRANSPORT_RAIL) || v->cur_speed != 0) return -1;
 

	
 
	count = 0;
 
	for (; v != NULL; v = v->next) {
 
		/* This count is used by the depot code to determine the number of engines
 
		 * in the consist. Exclude articulated parts so that autoreplacing to
 
		 * engines with more articulated parts than before works correctly.
 
		 *
 
		 * Also skip counting rear ends of multiheaded engines */
 
		if (!IsArticulatedPart(v) && !(!IsTrainEngine(v) && IsMultiheaded(v))) count++;
 
		if (v->u.rail.track != 0x80 || v->tile != tile ||
 
				(IsFrontEngine(v) && needs_to_be_stopped && !(v->vehstatus & VS_STOPPED))) {
 
			return -1;
 
		}
 
	}
 

	
 
	return count;
 
}
 

	
 
/* Used to check if the train is inside the depot and verifying that the VS_STOPPED flag is set */
 
inline int CheckTrainStoppedInDepot(const Vehicle *v)
 
{
 
	return CheckTrainInDepot(v, true);
 
}
 

	
 
/* Used to check if the train is inside the depot, but not checking the VS_STOPPED flag */
 
inline bool CheckTrainIsInsideDepot(const Vehicle *v)
 
{
 
	return (CheckTrainInDepot(v, false) > 0);
 
}
 

	
 
/**
 
 * Unlink a rail wagon from the consist.
 
 * @param v Vehicle to remove.
 
 * @param first The first vehicle of the consist.
 
 * @return The first vehicle of the consist.
 
 */
 
static Vehicle *UnlinkWagon(Vehicle *v, Vehicle *first)
 
{
 
	Vehicle *u;
 

	
 
	// unlinking the first vehicle of the chain?
 
	if (v == first) {
 
		v = GetNextVehicle(v);
 
		if (v == NULL) return NULL;
 

	
 
		if (IsTrainWagon(v)) SetFreeWagon(v);
 

	
 
		return v;
 
	}
 

	
 
	for (u = first; GetNextVehicle(u) != v; u = GetNextVehicle(u)) {}
 
	GetLastEnginePart(u)->next = GetNextVehicle(v);
 
	return first;
 
}
 

	
 
static Vehicle *FindGoodVehiclePos(const Vehicle *src)
 
{
 
	Vehicle *dst;
 
	EngineID eng = src->engine_type;
 
	TileIndex tile = src->tile;
 

	
 
	FOR_ALL_VEHICLES(dst) {
 
		if (dst->type == VEH_Train && IsFreeWagon(dst) && dst->tile == tile) {
 
			// check so all vehicles in the line have the same engine.
 
			Vehicle *v = dst;
 

	
 
			while (v->engine_type == eng) {
 
				v = v->next;
 
				if (v == NULL) return dst;
 
			}
 
		}
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/*
 
 * add a vehicle v behind vehicle dest
 
 * use this function since it sets flags as needed
 
 */
 
static void AddWagonToConsist(Vehicle *v, Vehicle *dest)
 
{
 
	UnlinkWagon(v, GetFirstVehicleInChain(v));
 
	if (dest == NULL) return;
 

	
 
	v->next = dest->next;
 
	dest->next = v;
 
	ClearFreeWagon(v);
 
	ClearFrontEngine(v);
 
}
 

	
 
/*
 
 * move around on the train so rear engines are placed correctly according to the other engines
 
 * always call with the front engine
 
 */
 
static void NormaliseTrainConsist(Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	if (IsFreeWagon(v)) return;
 

	
 
	assert(IsFrontEngine(v));
 

	
 
	for (; v != NULL; v = GetNextVehicle(v)) {
 
		if (!IsMultiheaded(v) || !IsTrainEngine(v)) continue;
 

	
 
		/* make sure that there are no free cars before next engine */
 
		for (u = v; u->next != NULL && !IsTrainEngine(u->next); u = u->next);
 

	
 
		if (u == v->u.rail.other_multiheaded_part) continue;
 
		AddWagonToConsist(v->u.rail.other_multiheaded_part, u);
 
	}
 
}
 

	
 
/** Move a rail vehicle around inside the depot.
 
 * @param tile unused
 
 * @param p1 various bitstuffed elements
 
 * - p1 (bit  0 - 15) source vehicle index
 
 * - p1 (bit 16 - 31) what wagon to put the source wagon AFTER, XXX - INVALID_VEHICLE to make a new line
 
 * @param p2 (bit 0) move all vehicles following the source vehicle
 
 */
 
int32 CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	VehicleID s = GB(p1, 0, 16);
 
	VehicleID d = GB(p1, 16, 16);
 
	Vehicle *src, *dst, *src_head, *dst_head;
 

	
 
	if (!IsValidVehicleID(s)) return CMD_ERROR;
 

	
 
	src = GetVehicle(s);
 

	
 
	if (src->type != VEH_Train) return CMD_ERROR;
 

	
 
	// if nothing is selected as destination, try and find a matching vehicle to drag to.
 
	if (d == INVALID_VEHICLE) {
 
		dst = IsTrainEngine(src) ? NULL : FindGoodVehiclePos(src);
 
	} else {
 
		dst = GetVehicle(d);
 
	}
 

	
 
	// if an articulated part is being handled, deal with its parent vehicle
 
	while (IsArticulatedPart(src)) src = GetPrevVehicleInChain(src);
 
	if (dst != NULL) {
 
		while (IsArticulatedPart(dst)) dst = GetPrevVehicleInChain(dst);
 
	}
 

	
 
	// don't move the same vehicle..
 
	if (src == dst) return 0;
 

	
 
	/* the player must be the owner */
 
	if (!CheckOwnership(src->owner) || (dst != NULL && !CheckOwnership(dst->owner)))
 
		return CMD_ERROR;
 

	
 
	/* locate the head of the two chains */
 
	src_head = GetFirstVehicleInChain(src);
 
	dst_head = NULL;
 
	if (dst != NULL) {
 
		dst_head = GetFirstVehicleInChain(dst);
 
		// Now deal with articulated part of destination wagon
 
		dst = GetLastEnginePart(dst);
 
	}
 

	
 
	if (dst != NULL && IsMultiheaded(dst) && !IsTrainEngine(dst) && IsTrainWagon(src)) {
 
		/* We are moving a wagon to the rear part of a multiheaded engine */
 
		if (dst->next == NULL) {
 
			/* It's the last one, so we will add the wagon just before the rear engine */
 
			dst = GetPrevVehicleInChain(dst);
 
			/* Now if the vehicle we want to link to is the vehicle itself, drop out */
 
			if (dst == src) return CMD_ERROR;
 
			// if dst is NULL, it means that dst got a rear multiheaded engine as first engine. We can't use that
 
			if (dst == NULL) return CMD_ERROR;
 
		} else {
 
			/* there are more units on this train, so we will add the wagon after the next one*/
 
			dst = dst->next;
 
		}
 
	}
 

	
 
	if (IsTrainEngine(src) && dst_head != NULL) {
 
		/* we need to make sure that we didn't place it between a pair of multiheaded engines */
 
		Vehicle *u, *engine = NULL;
 

	
 
		for (u = dst_head; u != NULL; u = u->next) {
 
			if (IsTrainEngine(u) && IsMultiheaded(u) && u->u.rail.other_multiheaded_part != NULL) {
 
				engine = u;
 
			}
 
			if (engine != NULL && engine->u.rail.other_multiheaded_part == u) {
 
				engine = NULL;
 
			}
 
			if (u == dst) {
 
				if (engine != NULL) dst = engine->u.rail.other_multiheaded_part;
 
				break;
 
			}
 
		}
 
	}
 

	
 
	if (IsMultiheaded(src) && !IsTrainEngine(src)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR);
 

	
 
	// when moving all wagons, we can't have the same src_head and dst_head
 
	if (HASBIT(p2, 0) && src_head == dst_head) return 0;
 

	
 
	{
 
		int src_len = 0;
 
		int max_len = _patches.mammoth_trains ? 100 : 9;
 

	
 
		// check if all vehicles in the source train are stopped inside a depot.
 
		src_len = CheckTrainStoppedInDepot(src_head);
 
		if (src_len < 0) return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED);
 

	
 
		// check the destination row if the source and destination aren't the same.
 
		if (src_head != dst_head) {
 
			int dst_len = 0;
 

	
 
			if (dst_head != NULL) {
 
				// check if all vehicles in the dest train are stopped.
 
				dst_len = CheckTrainStoppedInDepot(dst_head);
 
				if (dst_len < 0) return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED);
 

	
 
				assert(dst_head->tile == src_head->tile);
 
			}
 

	
 
			// We are moving between rows, so only count the wagons from the source
 
			// row that are being moved.
 
			if (HASBIT(p2, 0)) {
 
				const Vehicle *u;
 
				for (u = src_head; u != src && u != NULL; u = GetNextVehicle(u))
 
					src_len--;
 
			} else {
 
				// If moving only one vehicle, just count that.
 
				src_len = 1;
 
			}
 

	
 
			if (src_len + dst_len > max_len) {
 
				// Abort if we're adding too many wagons to a train.
 
				if (dst_head != NULL && IsFrontEngine(dst_head)) return_cmd_error(STR_8819_TRAIN_TOO_LONG);
 
				// Abort if we're making a train on a new row.
 
				if (dst_head == NULL && IsTrainEngine(src)) return_cmd_error(STR_8819_TRAIN_TOO_LONG);
 
			}
 
		} else {
 
			// Abort if we're creating a new train on an existing row.
 
			if (src_len > max_len && src == src_head && IsTrainEngine(GetNextVehicle(src_head)))
 
				return_cmd_error(STR_8819_TRAIN_TOO_LONG);
 
		}
 
	}
 

	
 
	// moving a loco to a new line?, then we need to assign a unitnumber.
 
	if (dst == NULL && !IsFrontEngine(src) && IsTrainEngine(src)) {
 
		UnitID unit_num = GetFreeUnitNumber(VEH_Train);
 
		if (unit_num > _patches.max_trains)
 
			return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
		if (flags & DC_EXEC) src->unitnumber = unit_num;
 
	}
 

	
 
	if (dst_head != NULL) {
 
		/* Check NewGRF Callback 0x1D */
 
		uint16 callback = GetVehicleCallbackParent(CBID_TRAIN_ALLOW_WAGON_ATTACH, 0, 0, dst_head->engine_type, src, dst_head);
 
		if (callback != CALLBACK_FAILED) {
 
			if (callback == 0xFD) return_cmd_error(STR_INCOMPATIBLE_RAIL_TYPES);
 
			if (callback < 0xFD) {
 
				StringID error = GetGRFStringID(GetEngineGRFID(dst_head->engine_type), 0xD000 + callback);
 
				return_cmd_error(error);
 
			}
 
		}
 
	}
 

	
 
	/* do it? */
 
	if (flags & DC_EXEC) {
 
		/* clear the ->first cache */
 
		{
 
			Vehicle *u;
 

	
 
			for (u = src_head; u != NULL; u = u->next) u->first = NULL;
 
			for (u = dst_head; u != NULL; u = u->next) u->first = NULL;
 
		}
 

	
 
		if (HASBIT(p2, 0)) {
 
			// unlink ALL wagons
 
			if (src != src_head) {
 
				Vehicle *v = src_head;
 
				while (GetNextVehicle(v) != src) v = GetNextVehicle(v);
 
				GetLastEnginePart(v)->next = NULL;
 
			} else {
 
				InvalidateWindowData(WC_VEHICLE_DEPOT, src_head->tile); // We removed a line
 
				src_head = NULL;
 
			}
 
		} else {
 
			// if moving within the same chain, dont use dst_head as it may get invalidated
 
			if (src_head == dst_head) dst_head = NULL;
 
			// unlink single wagon from linked list
 
			src_head = UnlinkWagon(src, src_head);
 
			GetLastEnginePart(src)->next = NULL;
 
		}
 

	
 
		if (dst == NULL) {
 
			/* We make a new line in the depot, so we know already that we invalidate the window data */
 
			InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile);
 

	
 
			// move the train to an empty line. for locomotives, we set the type to TS_Front. for wagons, 4.
 
			if (IsTrainEngine(src)) {
 
				if (!IsFrontEngine(src)) {
 
					// setting the type to 0 also involves setting up the orders field.
 
					SetFrontEngine(src);
 
					assert(src->orders == NULL);
 
					src->num_orders = 0;
 
				}
 
			} else {
 
				SetFreeWagon(src);
 
			}
 
			dst_head = src;
 
		} else {
 
			if (IsFrontEngine(src)) {
 
				// the vehicle was previously a loco. need to free the order list and delete vehicle windows etc.
 
				DeleteWindowById(WC_VEHICLE_VIEW, src->index);
 
				DeleteVehicleOrders(src);
 
			}
 

	
 
			if (IsFrontEngine(src) || IsFreeWagon(src)) {
 
				InvalidateWindowData(WC_VEHICLE_DEPOT, src->tile);
 
				ClearFrontEngine(src);
 
				ClearFreeWagon(src);
 
				src->unitnumber = 0; // doesn't occupy a unitnumber anymore.
 
			}
 

	
 
			// link in the wagon(s) in the chain.
 
			{
 
				Vehicle *v;
 

	
 
				for (v = src; GetNextVehicle(v) != NULL; v = GetNextVehicle(v));
 
				GetLastEnginePart(v)->next = dst->next;
 
			}
 
			dst->next = src;
 
		}
 
		if (src->u.rail.other_multiheaded_part != NULL) {
 
			if (src->u.rail.other_multiheaded_part == src_head) {
 
				src_head = src_head->next;
 
			}
 
			AddWagonToConsist(src->u.rail.other_multiheaded_part, src);
 
			// previous line set the front engine to the old front. We need to clear that
 
			src->u.rail.other_multiheaded_part->first = NULL;
 
		}
 

	
 
		if (HASBIT(p2, 0) && src_head != NULL && src_head != src) {
 
			/* if we stole a rear multiheaded engine, we better give it back to the front end */
 
			Vehicle *engine = NULL, *u;
 
			for (u = src_head; u != NULL; u = u->next) {
 
				if (IsMultiheaded(u)) {
 
					if (IsTrainEngine(u)) {
 
						engine = u;
 
						continue;
 
					}
 
					/* we got the rear engine to match with the front one */
 
					engine = NULL;
 
				}
 
			}
 
			if (engine != NULL && engine->u.rail.other_multiheaded_part != NULL) {
 
				AddWagonToConsist(engine->u.rail.other_multiheaded_part, engine);
 
				// previous line set the front engine to the old front. We need to clear that
 
				engine->u.rail.other_multiheaded_part->first = NULL;
 
			}
 
		}
 

	
 
		/* If there is an engine behind first_engine we moved away, it should become new first_engine
 
		 * To do this, CmdMoveRailVehicle must be called once more
 
		 * we can't loop forever here because next time we reach this line we will have a front engine */
 
		if (src_head != NULL && !IsFrontEngine(src_head) && IsTrainEngine(src_head)) {
 
			CmdMoveRailVehicle(0, flags, src_head->index | (INVALID_VEHICLE << 16), 1);
 
			src_head = NULL; // don't do anything more to this train since the new call will do it
 
		}
 

	
 
		if (src_head != NULL) {
 
			NormaliseTrainConsist(src_head);
 
			TrainConsistChanged(src_head);
 
			if (IsFrontEngine(src_head)) {
 
				UpdateTrainAcceleration(src_head);
 
				InvalidateWindow(WC_VEHICLE_DETAILS, src_head->index);
 
				/* Update the refit button and window */
 
				InvalidateWindow(WC_VEHICLE_REFIT, src_head->index);
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, src_head->index, 12);
 
			}
 
			/* Update the depot window */
 
			InvalidateWindow(WC_VEHICLE_DEPOT, src_head->tile);
 
		};
 

	
 
		if (dst_head != NULL) {
 
			NormaliseTrainConsist(dst_head);
 
			TrainConsistChanged(dst_head);
 
			if (IsFrontEngine(dst_head)) {
 
				UpdateTrainAcceleration(dst_head);
 
				InvalidateWindow(WC_VEHICLE_DETAILS, dst_head->index);
 
				/* Update the refit button and window */
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, dst_head->index, 12);
 
				InvalidateWindow(WC_VEHICLE_REFIT, dst_head->index);
 
			}
 
			/* Update the depot window */
 
			InvalidateWindow(WC_VEHICLE_DEPOT, dst_head->tile);
 
		}
 

	
 
		RebuildVehicleLists();
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Start/Stop a train.
 
 * @param tile unused
 
 * @param p1 train to start/stop
 
 * @param p2 unused
 
 */
 
int32 CmdStartStopTrain(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	uint16 callback;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	/* Check if this train can be started/stopped. The callback will fail or
 
	 * return 0xFF if it can. */
 
	callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
 
	if (callback != CALLBACK_FAILED && callback != 0xFF) {
 
		StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
 
		return_cmd_error(error);
 
	}
 

	
 
	if (v->vehstatus & VS_STOPPED && v->u.rail.cached_power == 0) return_cmd_error(STR_TRAIN_START_NO_CATENARY);
 

	
 
	if (flags & DC_EXEC) {
 
		if (v->vehstatus & VS_STOPPED && v->u.rail.track == 0x80) {
 
			DeleteVehicleNews(p1, STR_8814_TRAIN_IS_WAITING_IN_DEPOT);
 
		}
 

	
 
		v->u.rail.days_since_order_progr = 0;
 
		v->vehstatus ^= VS_STOPPED;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
	}
 
	return 0;
 
}
 

	
 
/** Sell a (single) train wagon/engine.
 
 * @param tile unused
 
 * @param p1 the wagon/engine index
 
 * @param p2 the selling mode
 
 * - p2 = 0: only sell the single dragged wagon/engine (and any belonging rear-engines)
 
 * - p2 = 1: sell the vehicle and all vehicles following it in the chain
 
             if the wagon is dragged, don't delete the possibly belonging rear-engine to some front
 
 * - p2 = 2: when selling attached locos, rearrange all vehicles after it to separate lines;
 
 *           all wagons of the same type will go on the same line. Used by the AI currently
 
 */
 
int32 CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v, *tmp, *first;
 
	Vehicle *new_f = NULL;
 
	int32 cost = 0;
 

	
 
	if (!IsValidVehicleID(p1) || p2 > 2) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	while (IsArticulatedPart(v)) v = GetPrevVehicleInChain(v);
 
	first = GetFirstVehicleInChain(v);
 

	
 
	// make sure the vehicle is stopped in the depot
 
	if (CheckTrainStoppedInDepot(first) < 0) {
 
		return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED);
 
	}
 

	
 
	if (IsMultiheaded(v) && !IsTrainEngine(v)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR);
 

	
 
	if (flags & DC_EXEC) {
 
		if (v == first && IsFrontEngine(first)) {
 
			DeleteWindowById(WC_VEHICLE_VIEW, first->index);
 
		}
 
		if (IsLocalPlayer() && (p1 == 1 || !(RailVehInfo(v->engine_type)->flags & RVI_WAGON))) {
 
			InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train);
 
		}
 
		InvalidateWindow(WC_VEHICLE_DEPOT, first->tile);
 
		RebuildVehicleLists();
 
	}
 

	
 
	switch (p2) {
 
		case 0: case 2: { /* Delete given wagon */
 
			bool switch_engine = false;    // update second wagon to engine?
 
			byte ori_subtype = v->subtype; // backup subtype of deleted wagon in case DeleteVehicle() changes
 

	
 
			/* 1. Delete the engine, if it is dualheaded also delete the matching
 
			 * rear engine of the loco (from the point of deletion onwards) */
 
			Vehicle *rear = (IsMultiheaded(v) &&
 
				IsTrainEngine(v)) ? v->u.rail.other_multiheaded_part : NULL;
 

	
 
			if (rear != NULL) {
 
				cost -= rear->value;
 
				if (flags & DC_EXEC) {
 
					UnlinkWagon(rear, first);
 
					DeleteDepotHighlightOfVehicle(rear);
 
					DeleteVehicle(rear);
 
				}
 
			}
 

	
 
			/* 2. We are selling the first engine, some special action might be required
 
			 * here, so take attention */
 
			if ((flags & DC_EXEC) && v == first) {
 
				new_f = GetNextVehicle(first);
 

	
 
				/* 2.1 If the first wagon is sold, update the first-> pointers to NULL */
 
				for (tmp = first; tmp != NULL; tmp = tmp->next) tmp->first = NULL;
 

	
 
				/* 2.2 If there are wagons present after the deleted front engine, check
 
         * if the second wagon (which will be first) is an engine. If it is one,
 
         * promote it as a new train, retaining the unitnumber, orders */
 
				if (new_f != NULL) {
 
					if (IsTrainEngine(new_f)) {
 
						switch_engine = true;
 
						/* Copy important data from the front engine */
 
						new_f->unitnumber = first->unitnumber;
 
						new_f->current_order = first->current_order;
 
						new_f->cur_order_index = first->cur_order_index;
 
						new_f->orders = first->orders;
 
						if (first->prev_shared != NULL) {
 
							first->prev_shared->next_shared = new_f;
 
							new_f->prev_shared = first->prev_shared;
 
						}
 

	
 
						if (first->next_shared != NULL) {
 
							first->next_shared->prev_shared = new_f;
 
							new_f->next_shared = first->next_shared;
 
						}
 

	
 
						new_f->num_orders = first->num_orders;
 
						first->orders = NULL; // XXX - to not to delete the orders */
 
						if (IsLocalPlayer()) ShowTrainViewWindow(new_f);
 
					}
 
				}
 
			}
 

	
 
			/* 3. Delete the requested wagon */
 
			cost -= v->value;
 
			if (flags & DC_EXEC) {
 
				first = UnlinkWagon(v, first);
 
				DeleteDepotHighlightOfVehicle(v);
 
				DeleteVehicle(v);
 

	
 
				/* 4 If the second wagon was an engine, update it to front_engine
 
					* which UnlinkWagon() has changed to TS_Free_Car */
 
				if (switch_engine) SetFrontEngine(first);
 

	
 
				/* 5. If the train still exists, update its acceleration, window, etc. */
 
				if (first != NULL) {
 
					NormaliseTrainConsist(first);
 
					TrainConsistChanged(first);
 
					if (IsFrontEngine(first)) {
 
						InvalidateWindow(WC_VEHICLE_DETAILS, first->index);
 
						InvalidateWindow(WC_VEHICLE_REFIT, first->index);
 
						UpdateTrainAcceleration(first);
 
					}
 
				}
 

	
 

	
 
				/* (6.) Borked AI. If it sells an engine it expects all wagons lined
 
				 * up on a new line to be added to the newly built loco. Replace it is.
 
				 * Totally braindead cause building a new engine adds all loco-less
 
				 * engines to its train anyways */
 
				if (p2 == 2 && HASBIT(ori_subtype, Train_Front)) {
 
					for (v = first; v != NULL; v = tmp) {
 
						tmp = GetNextVehicle(v);
 
						DoCommand(v->tile, v->index | INVALID_VEHICLE << 16, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 
					}
 
				}
 
			}
 
		} break;
 
		case 1: { /* Delete wagon and all wagons after it given certain criteria */
 
			/* Start deleting every vehicle after the selected one
 
			 * If we encounter a matching rear-engine to a front-engine
 
			 * earlier in the chain (before deletion), leave it alone */
 
			for (; v != NULL; v = tmp) {
 
				tmp = GetNextVehicle(v);
 

	
 
				if (IsMultiheaded(v)) {
 
					if (IsTrainEngine(v)) {
 
						/* We got a front engine of a multiheaded set. Now we will sell the rear end too */
 
						Vehicle *rear = v->u.rail.other_multiheaded_part;
 

	
 
						if (rear != NULL) {
 
							cost -= rear->value;
 
							if (flags & DC_EXEC) {
 
								first = UnlinkWagon(rear, first);
 
								DeleteDepotHighlightOfVehicle(rear);
 
								DeleteVehicle(rear);
 
							}
 
						}
 
					} else if (v->u.rail.other_multiheaded_part != NULL) {
 
						/* The front to this engine is earlier in this train. Do nothing */
 
						continue;
 
					}
 
				}
 

	
 
				cost -= v->value;
 
				if (flags & DC_EXEC) {
 
					first = UnlinkWagon(v, first);
 
					DeleteDepotHighlightOfVehicle(v);
 
					DeleteVehicle(v);
 
				}
 
			}
 

	
 
			/* 3. If it is still a valid train after selling, update its acceleration and cached values */
 
			if (flags & DC_EXEC && first != NULL) {
 
				NormaliseTrainConsist(first);
 
				TrainConsistChanged(first);
 
				if (IsFrontEngine(first)) UpdateTrainAcceleration(first);
 
				InvalidateWindow(WC_VEHICLE_DETAILS, first->index);
 
				InvalidateWindow(WC_VEHICLE_REFIT, first->index);
 
			}
 
		} break;
 
	}
 
	return cost;
 
}
 

	
 
static void UpdateTrainDeltaXY(Vehicle *v, Direction direction)
 
{
 
#define MKIT(a,b,c,d) ((a&0xFF)<<24) | ((b&0xFF)<<16) | ((c&0xFF)<<8) | ((d&0xFF)<<0)
 
	static const uint32 _delta_xy_table[8] = {
 
		MKIT(3, 3, -1, -1),
 
		MKIT(3, 7, -1, -3),
 
		MKIT(3, 3, -1, -1),
 
		MKIT(7, 3, -3, -1),
 
		MKIT(3, 3, -1, -1),
 
		MKIT(3, 7, -1, -3),
 
		MKIT(3, 3, -1, -1),
 
		MKIT(7, 3, -3, -1),
 
	};
 
#undef MKIT
 

	
 
	uint32 x = _delta_xy_table[direction];
 

	
 
	v->x_offs        = GB(x,  0, 8);
 
	v->y_offs        = GB(x,  8, 8);
 
	v->sprite_width  = GB(x, 16, 8);
 
	v->sprite_height = GB(x, 24, 8);
 
}
 

	
 
static void UpdateVarsAfterSwap(Vehicle *v)
 
{
 
	UpdateTrainDeltaXY(v, v->direction);
 
	v->cur_image = GetTrainImage(v, v->direction);
 
	BeginVehicleMove(v);
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
}
 

	
 
static void SetLastSpeed(Vehicle* v, int spd)
 
{
 
	int old = v->u.rail.last_speed;
 
	if (spd != old) {
 
		v->u.rail.last_speed = spd;
 
		if (_patches.vehicle_speed || (old == 0) != (spd == 0))
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 
}
 

	
 
static void SwapTrainFlags(byte *swap_flag1, byte *swap_flag2)
 
{
 
	byte flag1, flag2;
 

	
 
	flag1 = *swap_flag1;
 
	flag2 = *swap_flag2;
 

	
 
	/* Clear the flags */
 
	CLRBIT(*swap_flag1, VRF_GOINGUP);
 
	CLRBIT(*swap_flag1, VRF_GOINGDOWN);
 
	CLRBIT(*swap_flag2, VRF_GOINGUP);
 
	CLRBIT(*swap_flag2, VRF_GOINGDOWN);
 

	
 
	/* Reverse the rail-flags (if needed) */
 
	if (HASBIT(flag1, VRF_GOINGUP)) {
 
		SETBIT(*swap_flag2, VRF_GOINGDOWN);
 
	} else if (HASBIT(flag1, VRF_GOINGDOWN)) {
 
		SETBIT(*swap_flag2, VRF_GOINGUP);
 
	}
 
	if (HASBIT(flag2, VRF_GOINGUP)) {
 
		SETBIT(*swap_flag1, VRF_GOINGDOWN);
 
	} else if (HASBIT(flag2, VRF_GOINGDOWN)) {
 
		SETBIT(*swap_flag1, VRF_GOINGUP);
 
	}
 
}
 

	
 
static void ReverseTrainSwapVeh(Vehicle *v, int l, int r)
 
{
 
	Vehicle *a, *b;
 

	
 
	/* locate vehicles to swap */
 
	for (a = v; l != 0; l--) a = a->next;
 
	for (b = v; r != 0; r--) b = b->next;
 

	
 
	if (a != b) {
 
		/* swap the hidden bits */
 
		{
 
			uint16 tmp = (a->vehstatus & ~VS_HIDDEN) | (b->vehstatus&VS_HIDDEN);
 
			b->vehstatus = (b->vehstatus & ~VS_HIDDEN) | (a->vehstatus&VS_HIDDEN);
 
			a->vehstatus = tmp;
 
		}
 

	
 
		/* swap variables */
 
		swap_byte(&a->u.rail.track, &b->u.rail.track);
 
		swap_byte(&a->direction, &b->direction);
 

	
 
		/* toggle direction */
 
		if (!(a->u.rail.track & 0x80)) a->direction = ReverseDir(a->direction);
 
		if (!(b->u.rail.track & 0x80)) b->direction = ReverseDir(b->direction);
 

	
 
		/* swap more variables */
 
		swap_int32(&a->x_pos, &b->x_pos);
 
		swap_int32(&a->y_pos, &b->y_pos);
 
		swap_tile(&a->tile, &b->tile);
 
		swap_byte(&a->z_pos, &b->z_pos);
 

	
 
		SwapTrainFlags(&a->u.rail.flags, &b->u.rail.flags);
 

	
 
		/* update other vars */
 
		UpdateVarsAfterSwap(a);
 
		UpdateVarsAfterSwap(b);
 

	
 
		/* call the proper EnterTile function unless we are in a wormhole */
 
		if (!(a->u.rail.track & 0x40)) VehicleEnterTile(a, a->tile, a->x_pos, a->y_pos);
 
		if (!(b->u.rail.track & 0x40)) VehicleEnterTile(b, b->tile, b->x_pos, b->y_pos);
 
	} else {
 
		if (!(a->u.rail.track & 0x80)) a->direction = ReverseDir(a->direction);
 
		UpdateVarsAfterSwap(a);
 

	
 
		if (!(a->u.rail.track & 0x40)) VehicleEnterTile(a, a->tile, a->x_pos, a->y_pos);
 
	}
 

	
 
	/* Update train's power incase tiles were different rail type */
 
	TrainPowerChanged(v);
 
}
 

	
 
/* Check if the vehicle is a train and is on the tile we are testing */
 
static void *TestTrainOnCrossing(Vehicle *v, void *data)
 
{
 
	if (v->tile != *(const TileIndex*)data || v->type != VEH_Train) return NULL;
 
	return v;
 
}
 

	
 
static void DisableTrainCrossing(TileIndex tile)
 
{
 
	if (IsLevelCrossingTile(tile) &&
 
			VehicleFromPos(tile, &tile, TestTrainOnCrossing) == NULL && // empty?
 
			IsCrossingBarred(tile)) {
 
		UnbarCrossing(tile);
 
		MarkTileDirtyByTile(tile);
 
	}
 
}
 

	
 
/**
 
 * Advances wagons for train reversing, needed for variable length wagons.
 
 * Needs to be called once before the train is reversed, and once after it.
 
 * @param v First vehicle in chain
 
 * @param before Set to true for the call before reversing, false otherwise
 
 */
 
static void AdvanceWagons(Vehicle *v, bool before)
 
{
 
	Vehicle* base;
 
	Vehicle* first;
 
	int length;
 

	
 
	base = v;
 
	first = base->next;
 
	length = CountVehiclesInChain(v);
 

	
 
	while (length > 2) {
 
		Vehicle* last;
 
		int differential;
 
		int i;
 

	
 
		// find pairwise matching wagon
 
		// start<>end, start+1<>end-1, ...
 
		last = first;
 
		for (i = length - 3; i > 0; i--) last = last->next;
 

	
 
		differential = last->u.rail.cached_veh_length - base->u.rail.cached_veh_length;
 
		if (before) differential *= -1;
 

	
 
		if (differential > 0) {
 
			Vehicle* tempnext;
 

	
 
			// disconnect last car to make sure only this subset moves
 
			tempnext = last->next;
 
			last->next = NULL;
 

	
 
			for (i = 0; i < differential; i++) TrainController(first, false);
 

	
 
			last->next = tempnext;
 
		}
 

	
 
		base = first;
 
		first = first->next;
 
		length -= 2;
 
	}
 
}
 

	
 

	
 
static void ReverseTrainDirection(Vehicle *v)
 
{
 
	int l = 0, r = -1;
 
	Vehicle *u;
 

	
 
	if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) {
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	}
 

	
 
	/* Check if we were approaching a rail/road-crossing */
 
	{
 
		TileIndex tile = v->tile;
 
		DiagDirection dir = DirToDiagDir(v->direction);
 

	
 
		/* Determine the diagonal direction in which we will exit this tile */
 
		if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[dir]) {
 
			dir = ChangeDiagDir(dir, DIAGDIRDIFF_90LEFT);
 
		}
 
		/* Calculate next tile */
 
		tile += TileOffsByDiagDir(dir);
 

	
 
		/* Check if the train left a rail/road-crossing */
 
		DisableTrainCrossing(tile);
 
	}
 

	
 
	// count number of vehicles
 
	u = v;
 
	do r++; while ( (u = u->next) != NULL );
 

	
 
	AdvanceWagons(v, true);
 

	
 
	/* swap start<>end, start+1<>end-1, ... */
 
	do {
 
		ReverseTrainSwapVeh(v, l++, r--);
 
	} while (l <= r);
 

	
 
	AdvanceWagons(v, false);
 

	
 
	if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) {
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	}
 

	
 
	CLRBIT(v->u.rail.flags, VRF_REVERSING);
 
}
 

	
 
/** Reverse train.
 
 * @param tile unused
 
 * @param p1 train to reverse
 
 * @param p2 if true, reverse a unit in a train (needs to be in a depot)
 
 */
 
int32 CmdReverseTrainDirection(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (p2) {
 
		// turn a single unit around
 
		Vehicle *front;
 

	
 
		if (IsMultiheaded(v) || HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_ARTIC_ENGINE)) {
 
			return_cmd_error(STR_ONLY_TURN_SINGLE_UNIT);
 
		}
 

	
 
		front = GetFirstVehicleInChain(v);
 
		// make sure the vehicle is stopped in the depot
 
		if (CheckTrainStoppedInDepot(front) < 0) {
 
			return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED);
 
		}
 

	
 
		if (flags & DC_EXEC) {
 
			TOGGLEBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION);
 
			InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
			InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		}
 
	} else {
 
		//turn the whole train around
 
		if (v->u.rail.crash_anim_pos != 0 || v->breakdown_ctr != 0) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
 
			if (_patches.realistic_acceleration && v->cur_speed != 0) {
 
				TOGGLEBIT(v->u.rail.flags, VRF_REVERSING);
 
			} else {
 
				v->cur_speed = 0;
 
				SetLastSpeed(v, 0);
 
				ReverseTrainDirection(v);
 
			}
 
		}
 
	}
 
	return 0;
 
}
 

	
 
/** Force a train through a red signal
 
 * @param tile unused
 
 * @param p1 train to ignore the red signal
 
 * @param p2 unused
 
 */
 
int32 CmdForceTrainProceed(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) v->u.rail.force_proceed = 0x50;
 

	
 
	return 0;
 
}
 

	
 
/** Refits a train to the specified cargo type.
 
 * @param tile unused
 
 * @param p1 vehicle ID of the train to refit
 
 * param p2 various bitstuffed elements
 
 * - p2 = (bit 0-7) - the new cargo type to refit to
 
 * - p2 = (bit 8-15) - the new cargo subtype to refit to
 
 */
 
int32 CmdRefitRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	CargoID new_cid = GB(p2, 0, 8);
 
	byte new_subtype = GB(p2, 8, 8);
 
	Vehicle *v;
 
	int32 cost;
 
	uint num;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR;
 
	if (CheckTrainStoppedInDepot(v) < 0) return_cmd_error(STR_TRAIN_MUST_BE_STOPPED);
 

	
 
	/* Check cargo */
 
	if (new_cid > NUM_CARGO) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_TRAIN_RUN);
 

	
 
	cost = 0;
 
	num = 0;
 

	
 
	do {
 
		/* XXX: We also refit all the attached wagons en-masse if they
 
		 * can be refitted. This is how TTDPatch does it.  TODO: Have
 
		 * some nice [Refit] button near each wagon. --pasky */
 
		if (!CanRefitTo(v->engine_type, new_cid)) continue;
 

	
 
		if (v->cargo_cap != 0) {
 
			const RailVehicleInfo *rvi = RailVehInfo(v->engine_type);
 
			uint16 amount = CALLBACK_FAILED;
 

	
 
			if (HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_REFIT_CAPACITY)) {
 
				/* Back up the vehicle's cargo type */
 
				CargoID temp_cid = v->cargo_type;
 
				byte temp_subtype = v->cargo_subtype;
 
				v->cargo_type = new_cid;
 
				v->cargo_subtype = new_subtype;
 
				/* Check the refit capacity callback */
 
				amount = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
 
				/* Restore the original cargo type */
 
				v->cargo_type = temp_cid;
 
				v->cargo_subtype = temp_subtype;
 
			}
 

	
 
			if (amount == CALLBACK_FAILED) { // callback failed or not used, use default
 
				CargoID old_cid = rvi->cargo_type;
 
				/* normally, the capacity depends on the cargo type, a rail vehicle can
 
				 * carry twice as much mail/goods as normal cargo, and four times as
 
				 * many passengers
 
				 */
 
				amount = rvi->capacity;
 
				switch (old_cid) {
 
					case CT_PASSENGERS: break;
 
					case CT_MAIL:
 
					case CT_GOODS: amount *= 2; break;
 
					default:       amount *= 4; break;
 
				}
 
				switch (new_cid) {
 
					case CT_PASSENGERS: break;
 
					case CT_MAIL:
 
					case CT_GOODS: amount /= 2; break;
 
					default:       amount /= 4; break;
 
				}
 
			};
 

	
 
			if (amount != 0) {
 
				if (new_cid != v->cargo_type) {
 
					cost += GetRefitCost(v->engine_type);
 
				}
 

	
 
				num += amount;
 
				if (flags & DC_EXEC) {
 
					v->cargo_count = (v->cargo_type == new_cid) ? min(amount, v->cargo_count) : 0;
 
					v->cargo_type = new_cid;
 
					v->cargo_cap = amount;
 
					v->cargo_subtype = new_subtype;
 
					InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
					InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
					RebuildVehicleLists();
 
				}
 
			}
 
		}
 
	} while ((v = v->next) != NULL);
 

	
 
	_returned_refit_capacity = num;
 

	
 
	/* Update the train's cached variables */
 
	if (flags & DC_EXEC) TrainConsistChanged(GetFirstVehicleInChain(GetVehicle(p1)));
 

	
 
	return cost;
 
}
 

	
 
typedef struct TrainFindDepotData {
 
	uint best_length;
 
	TileIndex tile;
 
	PlayerID owner;
 
	/**
 
	 * true if reversing is necessary for the train to get to this depot
 
	 * This value is unused when new depot finding and NPF are both disabled
 
	 */
 
	bool reverse;
 
} TrainFindDepotData;
 

	
 
static bool NtpCallbFindDepot(TileIndex tile, TrainFindDepotData *tfdd, int track, uint length)
 
{
 
	if (IsTileType(tile, MP_RAILWAY) &&
 
			IsTileOwner(tile, tfdd->owner) &&
 
			IsRailDepot(tile)) {
 
		/* approximate number of tiles by dividing by DIAG_FACTOR */
 
		tfdd->best_length = length / DIAG_FACTOR;
 
		tfdd->tile = tile;
 
		return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
// returns the tile of a depot to goto to. The given vehicle must not be
 
// crashed!
 
static TrainFindDepotData FindClosestTrainDepot(Vehicle *v, int max_distance)
 
{
 
	TrainFindDepotData tfdd;
 
	TileIndex tile = v->tile;
 

	
 
	assert(!(v->vehstatus & VS_CRASHED));
 

	
 
	tfdd.owner = v->owner;
 
	tfdd.best_length = (uint)-1;
 
	tfdd.reverse = false;
 

	
 
	if (IsTileDepotType(tile, TRANSPORT_RAIL)){
 
		tfdd.tile = tile;
 
		tfdd.best_length = 0;
 
		return tfdd;
 
	}
 

	
 
	if (_patches.yapf.rail_use_yapf) {
 
		bool found = YapfFindNearestRailDepotTwoWay(v, max_distance, NPF_INFINITE_PENALTY, &tfdd.tile, &tfdd.reverse);
 
		tfdd.best_length = found ? max_distance / 2 : -1; // some fake distance or NOT_FOUND
 
	} else if (_patches.new_pathfinding_all) {
 
		NPFFoundTargetData ftd;
 
		Vehicle* last = GetLastVehicleInChain(v);
 
		Trackdir trackdir = GetVehicleTrackdir(v);
 
		Trackdir trackdir_rev = ReverseTrackdir(GetVehicleTrackdir(last));
 

	
 
		assert (trackdir != INVALID_TRACKDIR);
 
		ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, last->tile, trackdir_rev, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes, NPF_INFINITE_PENALTY);
 
		if (ftd.best_bird_dist == 0) {
 
			/* Found target */
 
			tfdd.tile = ftd.node.tile;
 
			/* Our caller expects a number of tiles, so we just approximate that
 
			 * number by this. It might not be completely what we want, but it will
 
			 * work for now :-) We can possibly change this when the old pathfinder
 
			 * is removed. */
 
			tfdd.best_length = ftd.best_path_dist / NPF_TILE_LENGTH;
 
			if (NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE)) tfdd.reverse = true;
 
		}
 
	} else {
 
		// search in the forward direction first.
 
		DiagDirection i;
 

	
 
		i = DirToDiagDir(v->direction);
 
		if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[i]) {
 
			i = ChangeDiagDir(i, DIAGDIRDIFF_90LEFT);
 
		}
 
		NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd);
 
		if (tfdd.best_length == (uint)-1){
 
			tfdd.reverse = true;
 
			// search in backwards direction
 
			i = ReverseDiagDir(DirToDiagDir(v->direction));
 
			if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[i]) {
 
				i = ChangeDiagDir(i, DIAGDIRDIFF_90LEFT);
 
			}
 
			NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd);
 
		}
 
	}
 

	
 
	return tfdd;
 
}
 

	
 
/** Send a train to a depot
 
 * @param tile unused
 
 * @param p1 train to send to the depot
 
 * @param p2 various bitmasked elements
 
 * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h)
 
 * - p2 bit 8-10 - VLW flag (for mass goto depot)
 
 */
 
int32 CmdSendTrainToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	TrainFindDepotData tfdd;
 

	
 
	if (p2 & DEPOT_MASS_SEND) {
 
		/* Mass goto depot requested */
 
		if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
 
		return SendAllVehiclesToDepot(VEH_Train, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1);
 
	}
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT) {
 
		if (!!(p2 & DEPOT_SERVICE) == HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT)) {
 
			/* We called with a different DEPOT_SERVICE setting.
 
			 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
 
			 * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
 
			if (flags & DC_EXEC) {
 
				TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
			}
 
			return 0;
 
		}
 

	
 
		if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
 
		if (flags & DC_EXEC) {
 
			if (HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) {
 
				v->u.rail.days_since_order_progr = 0;
 
				v->cur_order_index++;
 
			}
 

	
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
		return 0;
 
	}
 

	
 
	/* check if at a standstill (not stopped only) in a depot
 
	 * the check is down here to make it possible to alter stop/service for trains entering the depot */
 
	if (IsTileDepotType(v->tile, TRANSPORT_RAIL) && v->cur_speed == 0) return CMD_ERROR;
 

	
 
	tfdd = FindClosestTrainDepot(v, 0);
 
	if (tfdd.best_length == (uint)-1) return_cmd_error(STR_883A_UNABLE_TO_FIND_ROUTE_TO);
 

	
 
	if (flags & DC_EXEC) {
 
		v->dest_tile = tfdd.tile;
 
		v->current_order.type = OT_GOTO_DEPOT;
 
		v->current_order.flags = OF_NON_STOP;
 
		if (!(p2 & DEPOT_SERVICE)) SETBIT(v->current_order.flags, OFB_HALT_IN_DEPOT);
 
		v->current_order.dest = GetDepotByTile(tfdd.tile)->index;
 
		v->current_order.refit_cargo = CT_INVALID;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		/* If there is no depot in front, reverse automatically */
 
		if (tfdd.reverse)
 
			DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
void OnTick_Train(void)
 
{
 
	_age_cargo_skip_counter = (_age_cargo_skip_counter == 0) ? 184 : (_age_cargo_skip_counter - 1);
 
}
 

	
 
static const int8 _vehicle_smoke_pos[8] = {
 
	1, 1, 1, 0, -1, -1, -1, 0
 
};
 

	
 
static void HandleLocomotiveSmokeCloud(const Vehicle* v)
 
{
 
	const Vehicle* u;
 
	bool sound = false;
 

	
 
	if (v->vehstatus & VS_TRAIN_SLOWING || v->load_unload_time_rem != 0 || v->cur_speed < 2)
 
		return;
 

	
 
	u = v;
 

	
 
	do {
 
		EngineID engtype = v->engine_type;
 
		int effect_offset = GB(v->u.rail.cached_vis_effect, 0, 4) - 8;
 
		byte effect_type = GB(v->u.rail.cached_vis_effect, 4, 2);
 
		bool disable_effect = HASBIT(v->u.rail.cached_vis_effect, 6);
 
		int x, y;
 

	
 
		// no smoke?
 
		if ((RailVehInfo(engtype)->flags & RVI_WAGON && effect_type == 0) ||
 
				disable_effect ||
 
				GetEngine(engtype)->railtype > RAILTYPE_ELECTRIC ||
 
				v->vehstatus & VS_HIDDEN) {
 
			continue;
 
		}
 

	
 
		// No smoke in depots or tunnels
 
		if (IsTileDepotType(v->tile, TRANSPORT_RAIL) || IsTunnelTile(v->tile)) continue;
 

	
 
		// No sparks for electric vehicles on nonelectrified tracks
 
		if (!HasPowerOnRail(v->u.rail.railtype, GetTileRailType(v->tile, GetVehicleTrackdir(v)))) continue;
 

	
 
		if (effect_type == 0) {
 
			// Use default effect type for engine class.
 
			effect_type = RailVehInfo(engtype)->engclass;
 
		} else {
 
			effect_type--;
 
		}
 

	
 
		x = _vehicle_smoke_pos[v->direction] * effect_offset;
 
		y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
 

	
 
		if (HASBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION)) {
 
			x = -x;
 
			y = -y;
 
		}
 

	
 
		switch (effect_type) {
 
		case 0:
 
			// steam smoke.
 
			if (GB(v->tick_counter, 0, 4) == 0) {
 
				CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
 
				sound = true;
 
			}
 
			break;
 

	
 
		case 1:
 
			// diesel smoke
 
			if (u->cur_speed <= 40 && CHANCE16(15, 128)) {
 
				CreateEffectVehicleRel(v, 0, 0, 10, EV_DIESEL_SMOKE);
 
				sound = true;
 
			}
 
			break;
 

	
 
		case 2:
 
			// blue spark
 
			if (GB(v->tick_counter, 0, 2) == 0 && CHANCE16(1, 45)) {
 
				CreateEffectVehicleRel(v, 0, 0, 10, EV_ELECTRIC_SPARK);
 
				sound = true;
 
			}
 
			break;
 
		}
 
	} while ((v = v->next) != NULL);
 

	
 
	if (sound) PlayVehicleSound(u, VSE_TRAIN_EFFECT);
 
}
 

	
 
static void TrainPlayLeaveStationSound(const Vehicle* v)
 
{
 
	static const SoundFx sfx[] = {
 
		SND_04_TRAIN,
 
		SND_0A_TRAIN_HORN,
 
		SND_0A_TRAIN_HORN
 
	};
 

	
 
	EngineID engtype = v->engine_type;
 

	
 
	if (PlayVehicleSound(v, VSE_START)) return;
 

	
 
	switch (GetEngine(engtype)->railtype) {
 
		case RAILTYPE_RAIL:
 
		case RAILTYPE_ELECTRIC:
 
			SndPlayVehicleFx(sfx[RailVehInfo(engtype)->engclass], v);
 
			break;
 

	
 
		case RAILTYPE_MONO: SndPlayVehicleFx(SND_47_MAGLEV_2, v); break;
 
		case RAILTYPE_MAGLEV: SndPlayVehicleFx(SND_41_MAGLEV, v); break;
 
	}
 
}
 

	
 
static bool CheckTrainStayInDepot(Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	// bail out if not all wagons are in the same depot or not in a depot at all
 
	for (u = v; u != NULL; u = u->next) {
 
		if (u->u.rail.track != 0x80 || u->tile != v->tile) return false;
 
	}
 

	
 
	// if the train got no power, then keep it in the depot
 
	if (v->u.rail.cached_power == 0) {
 
		v->vehstatus |= VS_STOPPED;
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		return true;
 
	}
 

	
 
	if (v->u.rail.force_proceed == 0) {
 
		if (++v->load_unload_time_rem < 37) {
 
			InvalidateWindowClasses(WC_TRAINS_LIST);
 
			return true;
 
		}
 

	
 
		v->load_unload_time_rem = 0;
 

	
 
		if (UpdateSignalsOnSegment(v->tile, DirToDiagDir(v->direction))) {
 
			InvalidateWindowClasses(WC_TRAINS_LIST);
 
			return true;
 
		}
 
	}
 

	
 
	VehicleServiceInDepot(v);
 
	InvalidateWindowClasses(WC_TRAINS_LIST);
 
	TrainPlayLeaveStationSound(v);
 

	
 
	v->u.rail.track = 1;
 
	if (v->direction & 2) v->u.rail.track = 2;
 

	
 
	v->vehstatus &= ~VS_HIDDEN;
 
	v->cur_speed = 0;
 

	
 
	UpdateTrainDeltaXY(v, v->direction);
 
	v->cur_image = GetTrainImage(v, v->direction);
 
	VehiclePositionChanged(v);
 
	UpdateSignalsOnSegment(v->tile, DirToDiagDir(v->direction));
 
	UpdateTrainAcceleration(v);
 
	InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 

	
 
	return false;
 
}
 

	
 
/* Check for station tiles */
 
typedef struct TrainTrackFollowerData {
 
	TileIndex dest_coords;
 
	StationID station_index; // station index we're heading for
 
	uint best_bird_dist;
 
	uint best_track_dist;
 
	byte best_track;
 
} TrainTrackFollowerData;
 

	
 
static bool NtpCallbFindStation(TileIndex tile, TrainTrackFollowerData *ttfd, int track, uint length)
 
{
 
	// heading for nowhere?
 
	if (ttfd->dest_coords == 0) return false;
 

	
 
	// did we reach the final station?
 
	if ((ttfd->station_index == INVALID_STATION && tile == ttfd->dest_coords) || (
 
				IsTileType(tile, MP_STATION) &&
 
				IsRailwayStation(tile) &&
 
				GetStationIndex(tile) == ttfd->station_index
 
			)) {
 
		/* We do not check for dest_coords if we have a station_index,
 
		 * because in that case the dest_coords are just an
 
		 * approximation of where the station is */
 
		// found station
 
		ttfd->best_track = track;
 
		return true;
 
	} else {
 
		uint dist;
 

	
 
		// didn't find station, keep track of the best path so far.
 
		dist = DistanceManhattan(tile, ttfd->dest_coords);
 
		if (dist < ttfd->best_bird_dist) {
 
			ttfd->best_bird_dist = dist;
 
			ttfd->best_track = track;
 
		}
 
		return false;
 
	}
 
}
 

	
 
static void FillWithStationData(TrainTrackFollowerData* fd, const Vehicle* v)
 
{
 
	fd->dest_coords = v->dest_tile;
 
	if (v->current_order.type == OT_GOTO_STATION) {
 
		fd->station_index = v->current_order.dest;
 
	} else {
 
		fd->station_index = INVALID_STATION;
 
	}
 
}
 

	
 
static const byte _initial_tile_subcoord[6][4][3] = {
 
{{ 15, 8, 1 }, { 0, 0, 0 }, { 0, 8, 5 }, { 0,  0, 0 }},
 
{{  0, 0, 0 }, { 8, 0, 3 }, { 0, 0, 0 }, { 8, 15, 7 }},
 
{{  0, 0, 0 }, { 7, 0, 2 }, { 0, 7, 6 }, { 0,  0, 0 }},
 
{{ 15, 8, 2 }, { 0, 0, 0 }, { 0, 0, 0 }, { 8, 15, 6 }},
 
{{ 15, 7, 0 }, { 8, 0, 4 }, { 0, 0, 0 }, { 0,  0, 0 }},
 
{{  0, 0, 0 }, { 0, 0, 0 }, { 0, 8, 4 }, { 7, 15, 0 }},
 
};
 

	
 
static const uint32 _reachable_tracks[4] = {
 
	0x10091009,
 
	0x00160016,
 
	0x05200520,
 
	0x2A002A00,
 
};
 

	
 
static const byte _search_directions[6][4] = {
 
	{ 0, 9, 2, 9 }, // track 1
 
	{ 9, 1, 9, 3 }, // track 2
 
	{ 9, 0, 3, 9 }, // track upper
 
	{ 1, 9, 9, 2 }, // track lower
 
	{ 3, 2, 9, 9 }, // track left
 
	{ 9, 9, 1, 0 }, // track right
 
};
 

	
 
static const byte _pick_track_table[6] = {1, 3, 2, 2, 0, 0};
 

	
 
/* choose a track */
 
static byte ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackdirBits trackdirbits)
 
{
 
	TrainTrackFollowerData fd;
 
	uint best_track;
 
	// pathfinders are able to tell that route was only 'guessed'
 
	bool path_not_found = false;
 

	
 
#ifdef PF_BENCHMARK
 
	TIC()
 
#endif
 

	
 
	assert((trackdirbits & ~0x3F) == 0);
 

	
 
	/* quick return in case only one possible track is available */
 
	if (KILL_FIRST_BIT(trackdirbits) == 0) return FIND_FIRST_BIT(trackdirbits);
 

	
 
	if (_patches.yapf.rail_use_yapf) {
 
		Trackdir trackdir = YapfChooseRailTrack(v, tile, enterdir, trackdirbits, &path_not_found);
 
		if (trackdir != INVALID_TRACKDIR) {
 
			best_track = TrackdirToTrack(trackdir);
 
		} else {
 
			best_track = FIND_FIRST_BIT(TrackdirBitsToTrackBits(trackdirbits));
 
		}
 
	} else if (_patches.new_pathfinding_all) { /* Use a new pathfinding for everything */
 
		void* perf = NpfBeginInterval();
 
		int time = 0;
 

	
 
		NPFFindStationOrTileData fstd;
 
		NPFFoundTargetData ftd;
 
		Trackdir trackdir;
 

	
 
		NPFFillWithOrderData(&fstd, v);
 
		/* The enterdir for the new tile, is the exitdir for the old tile */
 
		trackdir = GetVehicleTrackdir(v);
 
		assert(trackdir != 0xff);
 

	
 
		ftd = NPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes);
 

	
 
		if (ftd.best_trackdir == 0xff) {
 
			/* We are already at our target. Just do something */
 
			//TODO: maybe display error?
 
			//TODO: go straight ahead if possible?
 
			best_track = FIND_FIRST_BIT(trackdirbits);
 
		} else {
 
			/* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
 
			the direction we need to take to get there, if ftd.best_bird_dist is not 0,
 
			we did not find our target, but ftd.best_trackdir contains the direction leading
 
			to the tile closest to our target. */
 
			if (ftd.best_bird_dist != 0) path_not_found = true;
 
			/* Discard enterdir information, making it a normal track */
 
			best_track = TrackdirToTrack(ftd.best_trackdir);
 
		}
 

	
 
		time = NpfEndInterval(perf);
 
		DEBUG(yapf, 4, "[NPFT] %d us - %d rounds - %d open - %d closed -- ", time, 0, _aystar_stats_open_size, _aystar_stats_closed_size);
 
	} else {
 
		void* perf = NpfBeginInterval();
 
		int time = 0;
 

	
 
		FillWithStationData(&fd, v);
 

	
 
		/* New train pathfinding */
 
		fd.best_bird_dist = (uint)-1;
 
		fd.best_track_dist = (uint)-1;
 
		fd.best_track = 0xFF;
 

	
 
		NewTrainPathfind(tile - TileOffsByDiagDir(enterdir), v->dest_tile,
 
			v->u.rail.compatible_railtypes, enterdir, (NTPEnumProc*)NtpCallbFindStation, &fd);
 

	
 
		// check whether the path was found or only 'guessed'
 
		if (fd.best_bird_dist != 0) path_not_found = true;
 

	
 
		if (fd.best_track == 0xff) {
 
			// blaha
 
			best_track = FIND_FIRST_BIT(trackdirbits);
 
		} else {
 
			best_track = fd.best_track & 7;
 
		}
 

	
 
		time = NpfEndInterval(perf);
 
		DEBUG(yapf, 4, "[NTPT] %d us - %d rounds - %d open - %d closed -- ", time, 0, 0, 0);
 
	}
 
	// handle "path not found" state
 
	if (path_not_found) {
 
		// PF didn't find the route
 
		if (!HASBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) {
 
			// it is first time the problem occurred, set the "path not found" flag
 
			SETBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION);
 
			// and notify user about the event
 
			if (_patches.lost_train_warn && v->owner == _local_player) {
 
				SetDParam(0, v->unitnumber);
 
				AddNewsItem(
 
					STR_TRAIN_IS_LOST,
 
					NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0),
 
					v->index,
 
					0);
 
			}
 
		}
 
	} else {
 
		// route found, is the train marked with "path not found" flag?
 
		if (HASBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) {
 
			// clear the flag as the PF's problem was solved
 
			CLRBIT(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION);
 
			// can we also delete the "News" item somehow?
 
		}
 
	}
 

	
 
#ifdef PF_BENCHMARK
 
	TOC("PF time = ", 1)
 
#endif
 

	
 
	return best_track;
 
}
 

	
 

	
 
static bool CheckReverseTrain(Vehicle *v)
 
{
 
	TrainTrackFollowerData fd;
 
	int i, r;
 
	int best_track;
 
	uint best_bird_dist  = 0;
 
	uint best_track_dist = 0;
 
	uint reverse, reverse_best;
 

	
 
	if (_opt.diff.line_reverse_mode != 0 ||
 
			v->u.rail.track & 0xC0 ||
 
			!(v->direction & 1))
 
		return false;
 

	
 
	FillWithStationData(&fd, v);
 

	
 
	best_track = -1;
 
	reverse_best = reverse = 0;
 

	
 
	assert(v->u.rail.track);
 

	
 
	i = _search_directions[FIND_FIRST_BIT(v->u.rail.track)][DirToDiagDir(v->direction)];
 

	
 
	if (_patches.yapf.rail_use_yapf) {
 
		reverse_best = YapfCheckReverseTrain(v);
 
	} else if (_patches.new_pathfinding_all) { /* Use a new pathfinding for everything */
 
		NPFFindStationOrTileData fstd;
 
		NPFFoundTargetData ftd;
 
		byte trackdir, trackdir_rev;
 
		Vehicle* last = GetLastVehicleInChain(v);
 

	
 
		NPFFillWithOrderData(&fstd, v);
 

	
 
		trackdir = GetVehicleTrackdir(v);
 
		trackdir_rev = ReverseTrackdir(GetVehicleTrackdir(last));
 
		assert(trackdir != 0xff);
 
		assert(trackdir_rev != 0xff);
 

	
 
		ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, last->tile, trackdir_rev, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.compatible_railtypes);
 
		if (ftd.best_bird_dist != 0) {
 
			/* We didn't find anything, just keep on going straight ahead */
 
			reverse_best = false;
 
		} else {
 
			if (NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE)) {
 
				reverse_best = true;
 
			} else {
 
				reverse_best = false;
 
			}
 
		}
 
	} else {
 
		for (;;) {
 
			fd.best_bird_dist = (uint)-1;
 
			fd.best_track_dist = (uint)-1;
 

	
 
			NewTrainPathfind(v->tile, v->dest_tile, v->u.rail.compatible_railtypes, reverse ^ i, (NTPEnumProc*)NtpCallbFindStation, &fd);
 

	
 
			if (best_track != -1) {
 
				if (best_bird_dist != 0) {
 
					if (fd.best_bird_dist != 0) {
 
						/* neither reached the destination, pick the one with the smallest bird dist */
 
						if (fd.best_bird_dist > best_bird_dist) goto bad;
 
						if (fd.best_bird_dist < best_bird_dist) goto good;
 
					} else {
 
						/* we found the destination for the first time */
 
						goto good;
 
					}
 
				} else {
 
					if (fd.best_bird_dist != 0) {
 
						/* didn't find destination, but we've found the destination previously */
 
						goto bad;
 
					} else {
 
						/* both old & new reached the destination, compare track length */
 
						if (fd.best_track_dist > best_track_dist) goto bad;
 
						if (fd.best_track_dist < best_track_dist) goto good;
 
					}
 
				}
 

	
 
				/* if we reach this position, there's two paths of equal value so far.
 
				 * pick one randomly. */
 
				r = GB(Random(), 0, 8);
 
				if (_pick_track_table[i] == (v->direction & 3)) r += 80;
 
				if (_pick_track_table[best_track] == (v->direction & 3)) r -= 80;
 
				if (r <= 127) goto bad;
 
			}
 
good:;
 
			best_track = i;
 
			best_bird_dist = fd.best_bird_dist;
 
			best_track_dist = fd.best_track_dist;
 
			reverse_best = reverse;
 
bad:;
 
			if (reverse != 0) break;
 
			reverse = 2;
 
		}
 
	}
 

	
 
	return reverse_best != 0;
 
}
 

	
 
static bool ProcessTrainOrder(Vehicle *v)
 
{
 
	const Order *order;
 
	bool at_waypoint = false;
 

	
 
	switch (v->current_order.type) {
 
		case OT_GOTO_DEPOT:
 
			if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return false;
 
			if ((v->current_order.flags & OF_SERVICE_IF_NEEDED) &&
 
					!VehicleNeedsService(v)) {
 
				v->cur_order_index++;
 
			}
 
			break;
 

	
 
		case OT_LOADING:
 
		case OT_LEAVESTATION:
 
			return false;
 

	
 
		default: break;
 
	}
 

	
 
	// check if we've reached the waypoint?
 
	if (v->current_order.type == OT_GOTO_WAYPOINT && v->tile == v->dest_tile) {
 
		v->cur_order_index++;
 
		at_waypoint = true;
 
	}
 

	
 
	// check if we've reached a non-stop station while TTDPatch nonstop is enabled..
 
	if (_patches.new_nonstop &&
 
			v->current_order.flags & OF_NON_STOP &&
 
			IsTileType(v->tile, MP_STATION) &&
 
			v->current_order.dest == GetStationIndex(v->tile)) {
 
		v->cur_order_index++;
 
	}
 

	
 
	// Get the current order
 
	if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
 

	
 
	order = GetVehicleOrder(v, v->cur_order_index);
 

	
 
	// If no order, do nothing.
 
	if (order == NULL) {
 
		v->current_order.type = OT_NOTHING;
 
		v->current_order.flags = 0;
 
		v->dest_tile = 0;
 
		return false;
 
	}
 

	
 
	// If it is unchanged, keep it.
 
	if (order->type  == v->current_order.type &&
 
			order->flags == v->current_order.flags &&
 
			order->dest  == v->current_order.dest)
 
		return false;
 

	
 
	// Otherwise set it, and determine the destination tile.
 
	v->current_order = *order;
 

	
 
	v->dest_tile = 0;
 

	
 
	InvalidateVehicleOrder(v);
 

	
 
	switch (order->type) {
 
		case OT_GOTO_STATION:
 
			if (order->dest == v->last_station_visited)
 
				v->last_station_visited = INVALID_STATION;
 
			v->dest_tile = GetStation(order->dest)->xy;
 
			break;
 

	
 
		case OT_GOTO_DEPOT:
 
			v->dest_tile = GetDepot(order->dest)->xy;
 
			break;
 

	
 
		case OT_GOTO_WAYPOINT:
 
			v->dest_tile = GetWaypoint(order->dest)->xy;
 
			break;
 

	
 
		default:
 
			return false;
 
	}
 

	
 
	return !at_waypoint && CheckReverseTrain(v);
 
}
 

	
 
static void MarkTrainDirty(Vehicle *v)
 
{
 
	do {
 
		v->cur_image = GetTrainImage(v, v->direction);
 
		MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
 
	} while ((v = v->next) != NULL);
 
}
 

	
 
static void HandleTrainLoading(Vehicle *v, bool mode)
 
{
 
	if (v->current_order.type == OT_NOTHING) return;
 

	
 
	if (v->current_order.type != OT_DUMMY) {
 
		if (v->current_order.type != OT_LOADING) return;
 
		if (mode) return;
 

	
 
		// don't mark the train as lost if we're loading on the final station.
 
		if (v->current_order.flags & OF_NON_STOP)
 
			v->u.rail.days_since_order_progr = 0;
 

	
 
		if (--v->load_unload_time_rem) return;
 

	
 
		if (CanFillVehicle(v) && (v->current_order.flags & OF_FULL_LOAD ||
 
				(_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED)))) {
 
			v->u.rail.days_since_order_progr = 0; /* Prevent a train lost message for full loading trains */
 
			SET_EXPENSES_TYPE(EXPENSES_TRAIN_INC);
 
			if (LoadUnloadVehicle(v, false)) {
 
				InvalidateWindow(WC_TRAINS_LIST, v->owner);
 
				MarkTrainDirty(v);
 

	
 
				// need to update acceleration and cached values since the goods on the train changed.
 
				TrainCargoChanged(v);
 
				UpdateTrainAcceleration(v);
 
			}
 
			return;
 
		}
 

	
 
		TrainPlayLeaveStationSound(v);
 

	
 
		{
 
			Order b = v->current_order;
 
			v->current_order.type = OT_LEAVESTATION;
 
			v->current_order.flags = 0;
 

	
 
			// If this was not the final order, don't remove it from the list.
 
			if (!(b.flags & OF_NON_STOP)) return;
 
		}
 
	}
 

	
 
	v->u.rail.days_since_order_progr = 0;
 
	v->cur_order_index++;
 
	InvalidateVehicleOrder(v);
 
}
 

	
 
static int UpdateTrainSpeed(Vehicle *v)
 
{
 
	uint spd;
 
	uint accel;
 

	
 
	if (v->vehstatus & VS_STOPPED || HASBIT(v->u.rail.flags, VRF_REVERSING)) {
 
		if (_patches.realistic_acceleration) {
 
			accel = GetTrainAcceleration(v, AM_BRAKE) * 2;
 
		} else {
 
			accel = v->acceleration * -2;
 
		}
 
	} else {
 
		if (_patches.realistic_acceleration) {
 
			accel = GetTrainAcceleration(v, AM_ACCEL);
 
		} else {
 
			accel = v->acceleration;
 
		}
 
	}
 

	
 
	spd = v->subspeed + accel * 2;
 
	v->subspeed = (byte)spd;
 
	{
 
		int tempmax = v->max_speed;
 
		if (v->cur_speed > v->max_speed)
 
			tempmax = v->cur_speed - (v->cur_speed / 10) - 1;
 
		v->cur_speed = spd = clamp(v->cur_speed + ((int)spd >> 8), 0, tempmax);
 
	}
 

	
 
	if (!(v->direction & 1)) spd = spd * 3 >> 2;
 

	
 
	spd += v->progress;
 
	v->progress = (byte)spd;
 
	return (spd >> 8);
 
}
 

	
 
static void TrainEnterStation(Vehicle *v, StationID station)
 
{
 
	Station *st;
 
	uint32 flags;
 

	
 
	v->last_station_visited = station;
 

	
 
	/* check if a train ever visited this station before */
 
	st = GetStation(station);
 
	if (!(st->had_vehicle_of_type & HVOT_TRAIN)) {
 
		st->had_vehicle_of_type |= HVOT_TRAIN;
 
		SetDParam(0, st->index);
 
		flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0);
 
		AddNewsItem(
 
			STR_8801_CITIZENS_CELEBRATE_FIRST,
 
			flags,
 
			v->index,
 
			0
 
		);
 
	}
 

	
 
	// Did we reach the final destination?
 
	if (v->current_order.type == OT_GOTO_STATION &&
 
			v->current_order.dest == station) {
 
		// Yeah, keep the load/unload flags
 
		// Non Stop now means if the order should be increased.
 
		v->current_order.type = OT_LOADING;
 
		v->current_order.flags &= OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER;
 
		v->current_order.flags |= OF_NON_STOP;
 
	} else {
 
		// No, just do a simple load
 
		v->current_order.type = OT_LOADING;
 
		v->current_order.flags = 0;
 
	}
 
	v->current_order.dest = 0;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_TRAIN_INC);
 
	if (LoadUnloadVehicle(v, true) != 0) {
 
		InvalidateWindow(WC_TRAINS_LIST, v->owner);
 
		TrainCargoChanged(v);
 
		UpdateTrainAcceleration(v);
 
	}
 
	MarkTrainDirty(v);
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
}
 

	
 
static byte AfterSetTrainPos(Vehicle *v, bool new_tile)
 
{
 
	byte new_z, old_z;
 

	
 
	// need this hint so it returns the right z coordinate on bridges.
 
	new_z = GetSlopeZ(v->x_pos, v->y_pos);
 

	
 
	old_z = v->z_pos;
 
	v->z_pos = new_z;
 

	
 
	if (new_tile) {
 
		CLRBIT(v->u.rail.flags, VRF_GOINGUP);
 
		CLRBIT(v->u.rail.flags, VRF_GOINGDOWN);
 

	
 
		if (new_z != old_z) {
 
			TileIndex tile = TileVirtXY(v->x_pos, v->y_pos);
 

	
 
			// XXX workaround, whole UP/DOWN detection needs overhaul
 
			if (!IsTunnelTile(tile)) {
 
				SETBIT(v->u.rail.flags, (new_z > old_z) ? VRF_GOINGUP : VRF_GOINGDOWN);
 
			}
 
		}
 
	}
 

	
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
	return old_z;
 
}
 

	
 
static const Direction _new_vehicle_direction_table[11] = {
 
	DIR_N , DIR_NW, DIR_W , 0,
 
	DIR_NE, DIR_N , DIR_SW, 0,
 
	DIR_E , DIR_SE, DIR_S
 
};
 

	
 
static Direction GetNewVehicleDirectionByTile(TileIndex new_tile, TileIndex old_tile)
 
{
 
	uint offs = (TileY(new_tile) - TileY(old_tile) + 1) * 4 +
 
							TileX(new_tile) - TileX(old_tile) + 1;
 
	assert(offs < 11);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
static Direction GetNewVehicleDirection(const Vehicle *v, int x, int y)
 
{
 
	uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1);
 
	assert(offs < 11);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
static int GetDirectionToVehicle(const Vehicle *v, int x, int y)
 
{
 
	byte offs;
 

	
 
	x -= v->x_pos;
 
	if (x >= 0) {
 
		offs = (x > 2) ? 0 : 1;
 
	} else {
 
		offs = (x < -2) ? 2 : 1;
 
	}
 

	
 
	y -= v->y_pos;
 
	if (y >= 0) {
 
		offs += ((y > 2) ? 0 : 1) * 4;
 
	} else {
 
		offs += ((y < -2) ? 2 : 1) * 4;
 
	}
 

	
 
	assert(offs < 11);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
/* Check if the vehicle is compatible with the specified tile */
 
static bool CheckCompatibleRail(const Vehicle *v, TileIndex tile)
 
{
 
	switch (GetTileType(tile)) {
 
		case MP_TUNNELBRIDGE:
 
		case MP_RAILWAY:
 
		case MP_STATION:
 
			// normal tracks, jump to owner check
 
			break;
 

	
 
		case MP_STREET:
 
			// tracks over roads, do owner check of tracks
 
			return
 
				IsTileOwner(tile, v->owner) && (
 
					!IsFrontEngine(v) ||
 
					IsCompatibleRail(v->u.rail.railtype, GetRailTypeCrossing(tile))
 
				);
 

	
 
		default:
 
			return true;
 
	}
 

	
 
	return
 
		IsTileOwner(tile, v->owner) && (
 
			!IsFrontEngine(v) ||
 
			HASBIT(v->u.rail.compatible_railtypes, GetRailType(tile))
 
		);
 
}
 

	
 
typedef struct {
 
	byte small_turn, large_turn;
 
	byte z_up; // fraction to remove when moving up
 
	byte z_down; // fraction to remove when moving down
 
} RailtypeSlowdownParams;
 

	
 
static const RailtypeSlowdownParams _railtype_slowdown[] = {
 
	// normal accel
 
	{256 / 4, 256 / 2, 256 / 4, 2}, // normal
 
	{256 / 4, 256 / 2, 256 / 4, 2}, // electrified
 
	{256 / 4, 256 / 2, 256 / 4, 2}, // monorail
 
	{0,       256 / 2, 256 / 4, 2}, // maglev
 
};
 

	
 
/* Modify the speed of the vehicle due to a turn */
 
static void AffectSpeedByDirChange(Vehicle* v, Direction new_dir)
 
{
 
	DirDiff diff;
 
	const RailtypeSlowdownParams *rsp;
 

	
 
	if (_patches.realistic_acceleration) return;
 

	
 
	diff = DirDifference(v->direction, new_dir);
 
	if (diff == DIRDIFF_SAME) return;
 

	
 
	rsp = &_railtype_slowdown[v->u.rail.railtype];
 
	v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? rsp->small_turn : rsp->large_turn) * v->cur_speed >> 8;
 
}
 

	
 
/* Modify the speed of the vehicle due to a change in altitude */
 
static void AffectSpeedByZChange(Vehicle *v, byte old_z)
 
{
 
	const RailtypeSlowdownParams *rsp;
 
	if (old_z == v->z_pos || _patches.realistic_acceleration) return;
 

	
 
	rsp = &_railtype_slowdown[v->u.rail.railtype];
 

	
 
	if (old_z < v->z_pos) {
 
		v->cur_speed -= (v->cur_speed * rsp->z_up >> 8);
 
	} else {
 
		uint16 spd = v->cur_speed + rsp->z_down;
 
		if (spd <= v->max_speed) v->cur_speed = spd;
 
	}
 
}
 

	
 
static const DiagDirection _otherside_signal_directions[] = {
 
	DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE, 0, 0,
 
	DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
 
};
 

	
 
static void TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
 
{
 
	if (IsTileType(tile, MP_RAILWAY) &&
 
			GetRailTileType(tile) == RAIL_TILE_SIGNALS) {
 
		uint i = FindFirstBit2x64(GetTrackBits(tile) * 0x101 & _reachable_tracks[dir]);
 
		UpdateSignalsOnSegment(tile, _otherside_signal_directions[i]);
 
	}
 
}
 

	
 

	
 
typedef struct TrainCollideChecker {
 
	const Vehicle *v;
 
	const Vehicle *v_skip;
 
} TrainCollideChecker;
 

	
 
static void *FindTrainCollideEnum(Vehicle *v, void *data)
 
{
 
	const TrainCollideChecker* tcc = data;
 

	
 
	if (v != tcc->v &&
 
			v != tcc->v_skip &&
 
			v->type == VEH_Train &&
 
			v->u.rail.track != 0x80 &&
 
			myabs(v->z_pos - tcc->v->z_pos) <= 6 &&
 
			myabs(v->x_pos - tcc->v->x_pos) < 6 &&
 
			myabs(v->y_pos - tcc->v->y_pos) < 6) {
 
		return v;
 
	} else {
 
		return NULL;
 
	}
 
}
 

	
 
static void SetVehicleCrashed(Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	if (v->u.rail.crash_anim_pos != 0) return;
 

	
 
	v->u.rail.crash_anim_pos++;
 

	
 
	u = v;
 
	BEGIN_ENUM_WAGONS(v)
 
		v->vehstatus |= VS_CRASHED;
 
	END_ENUM_WAGONS(v)
 

	
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, u->index, STATUS_BAR);
 
}
 

	
 
static uint CountPassengersInTrain(const Vehicle* v)
 
{
 
	uint num = 0;
 
	BEGIN_ENUM_WAGONS(v)
 
		if (v->cargo_type == CT_PASSENGERS) num += v->cargo_count;
 
	END_ENUM_WAGONS(v)
 
	return num;
 
}
 

	
 
/*
 
 * Checks whether the specified train has a collision with another vehicle. If
 
 * so, destroys this vehicle, and the other vehicle if its subtype has TS_Front.
 
 * Reports the incident in a flashy news item, modifies station ratings and
 
 * plays a sound.
 
 */
 
static void CheckTrainCollision(Vehicle *v)
 
{
 
	TrainCollideChecker tcc;
 
	Vehicle *coll;
 
	Vehicle *realcoll;
 
	uint num;
 

	
 
	/* can't collide in depot */
 
	if (v->u.rail.track == 0x80) return;
 

	
 
	assert(v->u.rail.track == 0x40 || TileVirtXY(v->x_pos, v->y_pos) == v->tile);
 

	
 
	tcc.v = v;
 
	tcc.v_skip = v->next;
 

	
 
	/* find colliding vehicle */
 
	realcoll = VehicleFromPos(TileVirtXY(v->x_pos, v->y_pos), &tcc, FindTrainCollideEnum);
 
	if (realcoll == NULL) return;
 

	
 
	coll = GetFirstVehicleInChain(realcoll);
 

	
 
	/* it can't collide with its own wagons */
 
	if (v == coll ||
 
			(v->u.rail.track & 0x40 && (v->direction & 2) != (realcoll->direction & 2)))
 
		return;
 

	
 
	//two drivers + passangers killed in train v
 
	num = 2 + CountPassengersInTrain(v);
 
	if (!(coll->vehstatus & VS_CRASHED))
 
		//two drivers + passangers killed in train coll (if it was not crashed already)
 
		num += 2 + CountPassengersInTrain(coll);
 

	
 
	SetVehicleCrashed(v);
 
	if (IsFrontEngine(coll)) SetVehicleCrashed(coll);
 

	
 
	SetDParam(0, num);
 
	AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL,
 
		NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, 0),
 
		v->index,
 
		0
 
	);
 

	
 
	ModifyStationRatingAround(v->tile, v->owner, -160, 30);
 
	SndPlayVehicleFx(SND_13_BIG_CRASH, v);
 
}
 

	
 
typedef struct VehicleAtSignalData {
 
	TileIndex tile;
 
	Direction direction;
 
} VehicleAtSignalData;
 

	
 
static void *CheckVehicleAtSignal(Vehicle *v, void *data)
 
{
 
	const VehicleAtSignalData* vasd = data;
 

	
 
	if (v->type == VEH_Train && IsFrontEngine(v) && v->tile == vasd->tile) {
 
		DirDiff diff = ChangeDirDiff(DirDifference(v->direction, vasd->direction), DIRDIFF_90RIGHT);
 

	
 
		if (diff == DIRDIFF_90RIGHT || (v->cur_speed <= 5 && diff <= DIRDIFF_REVERSE)) return v;
 
	}
 
	return NULL;
 
}
 

	
 
static void TrainController(Vehicle *v, bool update_image)
 
{
 
	Vehicle *prev;
 
	GetNewVehiclePosResult gp;
 
	uint32 r, tracks,ts;
 
	int i;
 
	DiagDirection enterdir;
 
	Direction dir;
 
	Direction newdir;
 
	Direction chosen_dir;
 
	byte chosen_track;
 
	byte old_z;
 

	
 
	/* For every vehicle after and including the given vehicle */
 
	for (prev = GetPrevVehicleInChain(v); v != NULL; prev = v, v = v->next) {
 
		BeginVehicleMove(v);
 

	
 
		if (v->u.rail.track != 0x40) {
 
			/* Not inside tunnel */
 
			if (GetNewVehiclePos(v, &gp)) {
 
				/* Staying in the old tile */
 
				if (v->u.rail.track == 0x80) {
 
					/* inside depot */
 
					gp.x = v->x_pos;
 
					gp.y = v->y_pos;
 
				} else {
 
					/* is not inside depot */
 

	
 
					if (!TrainCheckIfLineEnds(v)) return;
 

	
 
					r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
 
					if (r & 0x8) {
 
						//debug("%x & 0x8", r);
 
						goto invalid_rail;
 
					}
 
					if (r & 0x2) {
 
						TrainEnterStation(v, r >> 8);
 
						return;
 
					}
 

	
 
					if (v->current_order.type == OT_LEAVESTATION) {
 
						v->current_order.type = OT_NOTHING;
 
						v->current_order.flags = 0;
 
						InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
					}
 
				}
 
			} else {
 
				/* A new tile is about to be entered. */
 

	
 
				byte bits;
 
				/* Determine what direction we're entering the new tile from */
 
				dir = GetNewVehicleDirectionByTile(gp.new_tile, gp.old_tile);
 
				enterdir = DirToDiagDir(dir);
 
				assert(enterdir==0 || enterdir==1 || enterdir==2 || enterdir==3);
 

	
 
				/* Get the status of the tracks in the new tile and mask
 
				 * away the bits that aren't reachable. */
 
				ts = GetTileTrackStatus(gp.new_tile, TRANSPORT_RAIL) & _reachable_tracks[enterdir];
 

	
 
				/* Combine the from & to directions.
 
				 * Now, the lower byte contains the track status, and the byte at bit 16 contains
 
				 * the signal status. */
 
				tracks = ts | (ts >> 8);
 
				bits = tracks & 0xFF;
 
				if ((_patches.new_pathfinding_all || _patches.yapf.rail_use_yapf) && _patches.forbid_90_deg && prev == NULL) {
 
					/* We allow wagons to make 90 deg turns, because forbid_90_deg
 
					 * can be switched on halfway a turn */
 
					bits &= ~TrackCrossesTracks(FIND_FIRST_BIT(v->u.rail.track));
 
				}
 

	
 
				if (bits == 0) {
 
					//debug("%x == 0", bits);
 
					goto invalid_rail;
 
				}
 

	
 
				/* Check if the new tile contrains tracks that are compatible
 
				 * with the current train, if not, bail out. */
 
				if (!CheckCompatibleRail(v, gp.new_tile)) {
 
					//debug("!CheckCompatibleRail(%p, %x)", v, gp.new_tile);
 
					goto invalid_rail;
 
				}
 

	
 
				if (prev == NULL) {
 
					/* Currently the locomotive is active. Determine which one of the
 
					 * available tracks to choose */
 
					chosen_track = 1 << ChooseTrainTrack(v, gp.new_tile, enterdir, bits);
 
					assert(chosen_track & tracks);
 

	
 
					/* Check if it's a red signal and that force proceed is not clicked. */
 
					if ( (tracks>>16)&chosen_track && v->u.rail.force_proceed == 0) goto red_light;
 
				} else {
 
					static byte _matching_tracks[8] = {0x30, 1, 0xC, 2, 0x30, 1, 0xC, 2};
 

	
 
					/* The wagon is active, simply follow the prev vehicle. */
 
					chosen_track = (byte)(_matching_tracks[GetDirectionToVehicle(prev, gp.x, gp.y)] & bits);
 
				}
 

	
 
				/* make sure chosen track is a valid track */
 
				assert(chosen_track==1 || chosen_track==2 || chosen_track==4 || chosen_track==8 || chosen_track==16 || chosen_track==32);
 

	
 
				/* Update XY to reflect the entrance to the new tile, and select the direction to use */
 
				{
 
					const byte *b = _initial_tile_subcoord[FIND_FIRST_BIT(chosen_track)][enterdir];
 
					gp.x = (gp.x & ~0xF) | b[0];
 
					gp.y = (gp.y & ~0xF) | b[1];
 
					chosen_dir = b[2];
 
				}
 

	
 
				/* Call the landscape function and tell it that the vehicle entered the tile */
 
				r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
 
				if (r & 0x8) {
 
					//debug("%x & 0x8", r);
 
					goto invalid_rail;
 
				}
 

	
 
				if (IsLevelCrossingTile(v->tile) && v->next == NULL) {
 
					UnbarCrossing(v->tile);
 
					MarkTileDirtyByTile(v->tile);
 
				}
 

	
 
				if (IsFrontEngine(v)) v->load_unload_time_rem = 0;
 

	
 
				if (!(r&0x4)) {
 
					v->tile = gp.new_tile;
 

	
 
					if (GetTileRailType(gp.new_tile, chosen_track) != GetTileRailType(gp.old_tile, v->u.rail.track)) {
 
						TrainPowerChanged(GetFirstVehicleInChain(v));
 
					}
 

	
 
					v->u.rail.track = chosen_track;
 
					assert(v->u.rail.track);
 
				}
 

	
 
				if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir);
 

	
 
				/* Signals can only change when the first
 
				 * (above) or the last vehicle moves. */
 
				if (v->next == NULL)
 
					TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
 

	
 
				if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir);
 

	
 
				v->direction = chosen_dir;
 
			}
 
		} else {
 
			/* in tunnel on on a bridge */
 
			GetNewVehiclePos(v, &gp);
 

	
 
			SetSpeedLimitOnBridge(v);
 

	
 
			if (!(IsTunnelTile(gp.new_tile) || IsBridgeTile(gp.new_tile)) || !(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y) & 0x4)) {
 
				v->x_pos = gp.x;
 
				v->y_pos = gp.y;
 
				VehiclePositionChanged(v);
 
				if (!(v->vehstatus & VS_HIDDEN)) EndVehicleMove(v);
 
				continue;
 
			}
 
		}
 

	
 
		/* update image of train, as well as delta XY */
 
		newdir = GetNewVehicleDirection(v, gp.x, gp.y);
 
		UpdateTrainDeltaXY(v, newdir);
 
		if (update_image) v->cur_image = GetTrainImage(v, newdir);
 

	
 
		v->x_pos = gp.x;
 
		v->y_pos = gp.y;
 

	
 
		/* update the Z position of the vehicle */
 
		old_z = AfterSetTrainPos(v, (gp.new_tile != gp.old_tile));
 

	
 
		if (prev == NULL) {
 
			/* This is the first vehicle in the train */
 
			AffectSpeedByZChange(v, old_z);
 
		}
 
	}
 
	return;
 

	
 
invalid_rail:
 
	/* We've reached end of line?? */
 
	if (prev != NULL) error("!Disconnecting train");
 
	goto reverse_train_direction;
 

	
 
red_light: {
 
	/* We're in front of a red signal ?? */
 
		/* find the first set bit in ts. need to do it in 2 steps, since
 
		 * FIND_FIRST_BIT only handles 6 bits at a time. */
 
		i = FindFirstBit2x64(ts);
 

	
 
		if (!HasSignalOnTrackdir(gp.new_tile, ReverseTrackdir(i))) {
 
			v->cur_speed = 0;
 
			v->subspeed = 0;
 
			v->progress = 255 - 100;
 
			if (++v->load_unload_time_rem < _patches.wait_oneway_signal * 20) return;
 
		} else if (HasSignalOnTrackdir(gp.new_tile, i)){
 
			v->cur_speed = 0;
 
			v->subspeed = 0;
 
			v->progress = 255-10;
 
			if (++v->load_unload_time_rem < _patches.wait_twoway_signal * 73) {
 
				TileIndex o_tile = gp.new_tile + TileOffsByDiagDir(enterdir);
 
				VehicleAtSignalData vasd;
 
				vasd.tile = o_tile;
 
				vasd.direction = ReverseDir(dir);
 

	
 
				/* check if a train is waiting on the other side */
 
				if (VehicleFromPos(o_tile, &vasd, CheckVehicleAtSignal) == NULL) return;
 
			}
 
		}
 
	}
 

	
 
reverse_train_direction:
 
	v->load_unload_time_rem = 0;
 
	v->cur_speed = 0;
 
	v->subspeed = 0;
 
	ReverseTrainDirection(v);
 
}
 

	
 
extern TileIndex CheckTunnelBusy(TileIndex tile, uint *length);
 

	
 
/**
 
 * Deletes/Clears the last wagon of a crashed train. It takes the engine of the
 
 * train, then goes to the last wagon and deletes that. Each call to this function
 
 * will remove the last wagon of a crashed train. If this wagon was on a crossing,
 
 * or inside a tunnel, recalculate the signals as they might need updating
 
 * @param v the @Vehicle of which last wagon is to be removed
 
 */
 
static void DeleteLastWagon(Vehicle *v)
 
{
 
	Vehicle *u = v;
 

	
 
	/* Go to the last wagon and delete the link pointing there
 
	 * *u is then the one-before-last wagon, and *v the last
 
	 * one which will physicially be removed */
 
	for (; v->next != NULL; v = v->next) u = v;
 
	u->next = NULL;
 

	
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 
	RebuildVehicleLists();
 
	InvalidateWindow(WC_COMPANY, v->owner);
 

	
 
	BeginVehicleMove(v);
 
	EndVehicleMove(v);
 
	DeleteVehicle(v);
 

	
 
	if (!(v->u.rail.track & 0xC0))
 
		SetSignalsOnBothDir(v->tile, FIND_FIRST_BIT(v->u.rail.track));
 

	
 
	/* Check if the wagon was on a road/rail-crossing and disable it if no
 
	 * others are on it */
 
	DisableTrainCrossing(v->tile);
 

	
 
	if ( (v->u.rail.track == 0x40 && v->vehstatus & VS_HIDDEN) ) { // inside a tunnel
 
		TileIndex endtile = CheckTunnelBusy(v->tile, NULL);
 

	
 
		if (endtile == INVALID_TILE) return; // tunnel is busy (error returned)
 

	
 
		switch (v->direction) {
 
			case 1:
 
			case 5:
 
				SetSignalsOnBothDir(v->tile, 0);
 
				SetSignalsOnBothDir(endtile, 0);
 
				break;
 

	
 
			case 3:
 
			case 7:
 
				SetSignalsOnBothDir(v->tile, 1);
 
				SetSignalsOnBothDir(endtile, 1);
 
				break;
 

	
 
			default:
 
				break;
 
		}
 
	}
 
}
 

	
 
static void ChangeTrainDirRandomly(Vehicle *v)
 
{
 
	static const DirDiff delta[] = {
 
		DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
 
	};
 

	
 
	do {
 
		/* We don't need to twist around vehicles if they're not visible */
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]);
 
			BeginVehicleMove(v);
 
			UpdateTrainDeltaXY(v, v->direction);
 
			v->cur_image = GetTrainImage(v, v->direction);
 
			/* Refrain from updating the z position of the vehicle when on
 
			   a bridge, because AfterSetTrainPos will put the vehicle under
 
			   the bridge in that case */
 
			if (!(v->u.rail.track & 0x40)) AfterSetTrainPos(v, false);
 
		}
 
	} while ((v = v->next) != NULL);
 
}
 

	
 
static void HandleCrashedTrain(Vehicle *v)
 
{
 
	int state = ++v->u.rail.crash_anim_pos;
 
	uint32 r;
 
	Vehicle *u;
 

	
 
	if (state == 4 && !(v->u.rail.track & VS_HIDDEN)) {
 
		CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
 
	}
 

	
 
	if (state <= 200 && CHANCE16R(1, 7, r)) {
 
		int index = (r * 10 >> 16);
 

	
 
		u = v;
 
		do {
 
			if (--index < 0) {
 
				r = Random();
 

	
 
				CreateEffectVehicleRel(u,
 
					GB(r,  8, 3) + 2,
 
					GB(r, 16, 3) + 2,
 
					GB(r,  0, 3) + 5,
 
					EV_EXPLOSION_SMALL);
 
				break;
 
			}
 
		} while ((u = u->next) != NULL);
 
	}
 

	
 
	if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v);
 

	
 
	if (state >= 4440 && !(v->tick_counter&0x1F)) {
 
		DeleteLastWagon(v);
 
		InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train);
 
	}
 
}
 

	
 
static void HandleBrokenTrain(Vehicle *v)
 
{
 
	if (v->breakdown_ctr != 1) {
 
		v->breakdown_ctr = 1;
 
		v->cur_speed = 0;
 

	
 
		if (v->breakdowns_since_last_service != 255)
 
			v->breakdowns_since_last_service++;
 

	
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
 
			SndPlayVehicleFx((_opt.landscape != LT_CANDY) ?
 
				SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
 
		}
 

	
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
 
			if (u != NULL) u->u.special.unk0 = v->breakdown_delay * 2;
 
		}
 
	}
 

	
 
	if (!(v->tick_counter & 3)) {
 
		if (!--v->breakdown_delay) {
 
			v->breakdown_ctr = 0;
 
			InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		}
 
	}
 
}
 

	
 
static const byte _breakdown_speeds[16] = {
 
	225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
 
};
 

	
 
static bool TrainCheckIfLineEnds(Vehicle *v)
 
{
 
	TileIndex tile;
 
	uint x,y;
 
	uint16 break_speed;
 
	DiagDirection dir;
 
	int t;
 
	uint32 ts;
 

	
 
	t = v->breakdown_ctr;
 
	if (t > 1) {
 
		v->vehstatus |= VS_TRAIN_SLOWING;
 

	
 
		break_speed = _breakdown_speeds[GB(~t, 4, 4)];
 
		if (break_speed < v->cur_speed) v->cur_speed = break_speed;
 
	} else {
 
		v->vehstatus &= ~VS_TRAIN_SLOWING;
 
	}
 

	
 
	if (v->u.rail.track & 0x40) return true; // exit if inside a tunnel
 
	if (v->u.rail.track & 0x80) return true; // exit if inside a depot
 

	
 
	tile = v->tile;
 

	
 
	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
		DiagDirection dir;
 

	
 
		dir = IsTunnel(tile) ? GetTunnelDirection(tile) : GetBridgeRampDirection(tile);
 
		if (DiagDirToDir(dir) == v->direction) return true;
 
	}
 

	
 
	// depot?
 
	/* XXX -- When enabled, this makes it possible to crash trains of others
 
	     (by building a depot right against a station) */
 
/*	if (IsTileType(tile, MP_RAILWAY) && GetRailTileType(tile) == RAIL_TILE_DEPOT_WAYPOINT)
 
		return true;*/
 

	
 
	/* Determine the non-diagonal direction in which we will exit this tile */
 
	dir = DirToDiagDir(v->direction);
 
	if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[dir]) {
 
		dir = ChangeDiagDir(dir, DIAGDIRDIFF_90LEFT);
 
	}
 
	/* Calculate next tile */
 
	tile += TileOffsByDiagDir(dir);
 
	// determine the track status on the next tile.
 
	ts = GetTileTrackStatus(tile, TRANSPORT_RAIL) & _reachable_tracks[dir];
 

	
 
	/* Calc position within the current tile ?? */
 
	x = v->x_pos & 0xF;
 
	y = v->y_pos & 0xF;
 

	
 
	switch (v->direction) {
 
		case DIR_N : x = ~x + ~y + 24; break;
 
		case DIR_NW: x = y;            /* FALLTHROUGH */
 
		case DIR_NE: x = ~x + 16;      break;
 
		case DIR_E : x = ~x + y + 8;   break;
 
		case DIR_SE: x = y;            break;
 
		case DIR_S : x = x + y - 8;    break;
 
		case DIR_W : x = ~y + x + 8;   break;
 
	}
 

	
 
	if (GB(ts, 0, 16) != 0) {
 
		/* If we approach a rail-piece which we can't enter, or the back of a depot, don't enter it! */
 
		if (x + 4 >= TILE_SIZE &&
 
				(!CheckCompatibleRail(v, tile) ||
 
				(IsTileDepotType(tile, TRANSPORT_RAIL) &&
 
				GetRailDepotDirection(tile) == dir))) {
 
			v->cur_speed = 0;
 
			ReverseTrainDirection(v);
 
			return false;
 
		}
 
		if ((ts &= (ts >> 16)) == 0) {
 
			// make a rail/road crossing red
 
			if (IsLevelCrossingTile(tile)) {
 
				if (!IsCrossingBarred(tile)) {
 
					BarCrossing(tile);
 
					SndPlayVehicleFx(SND_0E_LEVEL_CROSSING, v);
 
					MarkTileDirtyByTile(tile);
 
				}
 
			}
 
			return true;
 
		}
 
	} else if (x + 4 >= TILE_SIZE) {
 
		v->cur_speed = 0;
 
		ReverseTrainDirection(v);
 
		return false;
 
	}
 

	
 
	// slow down
 
	v->vehstatus |= VS_TRAIN_SLOWING;
 
	break_speed = _breakdown_speeds[x & 0xF];
 
	if (!(v->direction & 1)) break_speed >>= 1;
 
	if (break_speed < v->cur_speed) v->cur_speed = break_speed;
 

	
 
	return true;
 
}
 

	
 
static void TrainLocoHandler(Vehicle *v, bool mode)
 
{
 
	int j;
 

	
 
	/* train has crashed? */
 
	if (v->u.rail.crash_anim_pos != 0) {
 
		if (!mode) HandleCrashedTrain(v);
 
		return;
 
	}
 

	
 
	if (v->u.rail.force_proceed != 0) v->u.rail.force_proceed--;
 

	
 
	/* train is broken down? */
 
	if (v->breakdown_ctr != 0) {
 
		if (v->breakdown_ctr <= 2) {
 
			HandleBrokenTrain(v);
 
			return;
 
		}
 
		v->breakdown_ctr--;
 
	}
 

	
 
	if (HASBIT(v->u.rail.flags, VRF_REVERSING) && v->cur_speed == 0) {
 
		ReverseTrainDirection(v);
 
	}
 

	
 
	/* exit if train is stopped */
 
	if (v->vehstatus & VS_STOPPED && v->cur_speed == 0) return;
 

	
 
	if (ProcessTrainOrder(v)) {
 
		v->load_unload_time_rem = 0;
 
		v->cur_speed = 0;
 
		v->subspeed = 0;
 
		ReverseTrainDirection(v);
 
		return;
 
	}
 

	
 
	HandleTrainLoading(v, mode);
 

	
 
	if (v->current_order.type == OT_LOADING) return;
 

	
 
	if (CheckTrainStayInDepot(v)) return;
 

	
 
	if (!mode) HandleLocomotiveSmokeCloud(v);
 

	
 
	j = UpdateTrainSpeed(v);
 
	if (j == 0) {
 
		// if the vehicle has speed 0, update the last_speed field.
 
		if (v->cur_speed != 0) return;
 
	} else {
 
		TrainCheckIfLineEnds(v);
 

	
 
		do {
 
			TrainController(v, true);
 
			CheckTrainCollision(v);
 
			if (v->cur_speed <= 0x100)
 
				break;
 
		} while (--j != 0);
 
	}
 

	
 
	SetLastSpeed(v, v->cur_speed);
 
}
 

	
 

	
 
void Train_Tick(Vehicle *v)
 
{
 
	if (_age_cargo_skip_counter == 0 && v->cargo_days != 0xff)
 
		v->cargo_days++;
 

	
 
	v->tick_counter++;
 

	
 
	if (IsFrontEngine(v)) {
 
		TrainLocoHandler(v, false);
 

	
 
		// make sure vehicle wasn't deleted.
 
		if (v->type == VEH_Train && IsFrontEngine(v))
 
			TrainLocoHandler(v, true);
 
	} else if (IsFreeWagon(v) && HASBITS(v->vehstatus, VS_CRASHED)) {
 
		// Delete flooded standalone wagon
 
		if (++v->u.rail.crash_anim_pos >= 4400)
 
			DeleteVehicle(v);
 
	}
 
}
 

	
 
#define MAX_ACCEPTABLE_DEPOT_DIST 16
 

	
 
static void CheckIfTrainNeedsService(Vehicle *v)
 
{
 
	const Depot* depot;
 
	TrainFindDepotData tfdd;
 

	
 
	if (_patches.servint_trains == 0)                   return;
 
	if (!VehicleNeedsService(v))                        return;
 
	if (v->vehstatus & VS_STOPPED)                      return;
 
	if (_patches.gotodepot && VehicleHasDepotOrders(v)) return;
 

	
 
	// Don't interfere with a depot visit scheduled by the user, or a
 
	// depot visit by the order list.
 
	if (v->current_order.type == OT_GOTO_DEPOT &&
 
			(v->current_order.flags & (OF_HALT_IN_DEPOT | OF_PART_OF_ORDERS)) != 0)
 
		return;
 

	
 
	if (CheckTrainIsInsideDepot(v)) {
 
		VehicleServiceInDepot(v);
 
		return;
 
	}
 

	
 
	tfdd = FindClosestTrainDepot(v, MAX_ACCEPTABLE_DEPOT_DIST);
 
	/* Only go to the depot if it is not too far out of our way. */
 
	if (tfdd.best_length == (uint)-1 || tfdd.best_length > MAX_ACCEPTABLE_DEPOT_DIST) {
 
		if (v->current_order.type == OT_GOTO_DEPOT) {
 
			/* If we were already heading for a depot but it has
 
			 * suddenly moved farther away, we continue our normal
 
			 * schedule? */
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
		return;
 
	}
 

	
 
	depot = GetDepotByTile(tfdd.tile);
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT &&
 
			v->current_order.dest != depot->index &&
 
			!CHANCE16(3, 16)) {
 
		return;
 
	}
 

	
 
	v->current_order.type = OT_GOTO_DEPOT;
 
	v->current_order.flags = OF_NON_STOP;
 
	v->current_order.dest = depot->index;
 
	v->dest_tile = tfdd.tile;
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
}
 

	
 
int32 GetTrainRunningCost(const Vehicle *v)
 
{
 
	int32 cost = 0;
 

	
 
	do {
 
		const RailVehicleInfo *rvi = RailVehInfo(v->engine_type);
 
		if (rvi->running_cost_base > 0)
 
			cost += rvi->running_cost_base * _price.running_rail[rvi->running_cost_class];
 
	} while ((v = GetNextVehicle(v)) != NULL);
 

	
 
	return cost;
 
}
 

	
 
void OnNewDay_Train(Vehicle *v)
 
{
 
	TileIndex tile;
 

	
 
	if ((++v->day_counter & 7) == 0) DecreaseVehicleValue(v);
 

	
 
	if (IsFrontEngine(v)) {
 
		CheckVehicleBreakdown(v);
 
		AgeVehicle(v);
 

	
 
		CheckIfTrainNeedsService(v);
 

	
 
		CheckOrders(v);
 

	
 
		/* update destination */
 
		if (v->current_order.type == OT_GOTO_STATION &&
 
				(tile = GetStation(v->current_order.dest)->train_tile) != 0) {
 
			v->dest_tile = tile;
 
		}
 

	
 
		if ((v->vehstatus & VS_STOPPED) == 0) {
 
			/* running costs */
 
			int32 cost = GetTrainRunningCost(v) / 364;
 

	
 
			v->profit_this_year -= cost >> 8;
 

	
 
			SET_EXPENSES_TYPE(EXPENSES_TRAIN_RUN);
 
			SubtractMoneyFromPlayerFract(v->owner, cost);
 

	
 
			InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
			InvalidateWindowClasses(WC_TRAINS_LIST);
 
		}
 
	}
 
}
 

	
 
void TrainsYearlyLoop(void)
 
{
 
	Vehicle *v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Train && IsFrontEngine(v)) {
 

	
 
			// show warning if train is not generating enough income last 2 years (corresponds to a red icon in the vehicle list)
 
			if (_patches.train_income_warn && v->owner == _local_player && v->age >= 730 && v->profit_this_year < 0) {
 
				SetDParam(1, v->profit_this_year);
 
				SetDParam(0, v->unitnumber);
 
				AddNewsItem(
 
					STR_TRAIN_IS_UNPROFITABLE,
 
					NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0),
 
					v->index,
 
					0);
 
			}
 

	
 
			v->profit_last_year = v->profit_this_year;
 
			v->profit_this_year = 0;
 
			InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		}
 
	}
 
}
 

	
 

	
 
void InitializeTrains(void)
 
{
 
	_age_cargo_skip_counter = 1;
 
}
 

	
 
/*
 
 * Link front and rear multiheaded engines to each other
 
 * This is done when loading a savegame
 
 */
 
void ConnectMultiheadedTrains(void)
 
{
 
	Vehicle *v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Train) {
 
			v->u.rail.other_multiheaded_part = NULL;
 
		}
 
	}
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Train && IsFrontEngine(v)) {
 
			Vehicle *u = v;
 

	
 
			BEGIN_ENUM_WAGONS(u) {
 
				if (u->u.rail.other_multiheaded_part != NULL) continue; // we already linked this one
 

	
 
				if (IsMultiheaded(u)) {
 
					if (!IsTrainEngine(u)) {
 
						/* we got a rear car without a front car. We will convert it to a front one */
 
						SetTrainEngine(u);
 
						u->spritenum--;
 
					}
 

	
 
					{
 
						Vehicle *w;
 

	
 
						for (w = u->next; w != NULL && (w->engine_type != u->engine_type || w->u.rail.other_multiheaded_part != NULL); w = GetNextVehicle(w));
 
						if (w != NULL) {
 
							/* we found a car to partner with this engine. Now we will make sure it face the right way */
 
							if (IsTrainEngine(w)) {
 
								ClearTrainEngine(w);
 
								w->spritenum++;
 
							}
 
						}
 

	
 
						if (w != NULL) {
 
							w->u.rail.other_multiheaded_part = u;
 
							u->u.rail.other_multiheaded_part = w;
 
						} else {
 
							/* we got a front car and no rear cars. We will fake this one for forget that it should have been multiheaded */
 
							ClearMultiheaded(u);
 
						}
 
					}
 
				}
 
			} END_ENUM_WAGONS(u)
 
		}
 
	}
 
}
 

	
 
/*
 
 *  Converts all trains to the new subtype format introduced in savegame 16.2
 
 *  It also links multiheaded engines or make them forget they are multiheaded if no suitable partner is found
 
 */
 
void ConvertOldMultiheadToNew(void)
 
{
 
	Vehicle *v;
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Train) {
 
			SETBIT(v->subtype, 7); // indicates that it's the old format and needs to be converted in the next loop
 
		}
 
	}
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Train) {
 
			if (HASBIT(v->subtype, 7) && ((v->subtype & ~0x80) == 0 || (v->subtype & ~0x80) == 4)) {
 
				Vehicle *u = v;
 

	
 
				BEGIN_ENUM_WAGONS(u) {
 
					const RailVehicleInfo *rvi = RailVehInfo(u->engine_type);
 

	
 
					CLRBIT(u->subtype, 7);
 
					switch (u->subtype) {
 
						case 0: /* TS_Front_Engine */
 
							if (rvi->flags & RVI_MULTIHEAD) SetMultiheaded(u);
 
							SetFrontEngine(u);
 
							SetTrainEngine(u);
 
							break;
 

	
 
						case 1: /* TS_Artic_Part */
 
							u->subtype = 0;
 
							SetArticulatedPart(u);
 
							break;
 

	
 
						case 2: /* TS_Not_First */
 
							u->subtype = 0;
 
							if (rvi->flags & RVI_WAGON) {
 
								// normal wagon
 
								SetTrainWagon(u);
 
								break;
 
							}
 
							if (rvi->flags & RVI_MULTIHEAD && rvi->image_index == u->spritenum - 1) {
 
								// rear end of a multiheaded engine
 
								SetMultiheaded(u);
 
								break;
 
							}
 
							if (rvi->flags & RVI_MULTIHEAD) SetMultiheaded(u);
 
							SetTrainEngine(u);
 
							break;
 

	
 
						case 4: /* TS_Free_Car */
 
							u->subtype = 0;
 
							SetTrainWagon(u);
 
							SetFreeWagon(u);
 
							break;
 
						default: NOT_REACHED(); break;
 
					}
 
				} END_ENUM_WAGONS(u)
 
			}
 
		}
 
	}
 
}
src/train_gui.c
Show inline comments
 
deleted file
src/train_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "rail_map.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "engine.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "vehicle.h"
 
#include "viewport.h"
 
#include "station.h"
 
#include "command.h"
 
#include "player.h"
 
#include "vehicle_gui.h"
 
#include "depot.h"
 
#include "train.h"
 
#include "newgrf_engine.h"
 
#include "date.h"
 
#include "strings.h"
 

	
 
enum BuildTrainWidgets {
 
	BUILD_TRAIN_WIDGET_CLOSEBOX = 0,
 
	BUILD_TRAIN_WIDGET_CAPTION,
 
	BUILD_TRAIN_WIDGET_SORT_ASCENDING_DESCENDING,
 
	BUILD_TRAIN_WIDGET_SORT_TEXT,
 
	BUILD_TRAIN_WIDGET_SORT_DROPDOWN,
 
	BUILD_TRAIN_WIDGET_LIST,
 
	BUILD_TRAIN_WIDGET_SCROLLBAR,
 
	BUILD_TRAIN_WIDGET_PANEL,
 
	BUILD_TRAIN_WIDGET_BUILD,
 
	BUILD_TRAIN_WIDGET_RENAME,
 
	BUILD_TRAIN_WIDGET_RESIZE,
 
};
 

	
 
static const Widget _new_rail_vehicle_widgets[] = {
 
	{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,               STR_018B_CLOSE_WINDOW},
 
	{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   227,     0,    13, STR_JUST_STRING,        STR_018C_WINDOW_TITLE_DRAG_THIS},
 
	{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    80,    14,    25, STR_SORT_BY,            STR_SORT_ORDER_TIP},
 
	{      WWT_PANEL,   RESIZE_NONE,    14,    81,   215,    14,    25, 0x0,                    STR_SORT_CRITERIA_TIP},
 
	{    WWT_TEXTBTN,   RESIZE_NONE,    14,   216,   227,    14,    25, STR_0225,               STR_SORT_CRITERIA_TIP},
 
	{     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   215,    26,   137, 0x801,                  STR_8843_TRAIN_VEHICLE_SELECTION},
 
	{  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   216,   227,    26,   137, 0x0,                    STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
	{      WWT_PANEL,     RESIZE_TB,    14,     0,   227,   138,   239, 0x0,                    STR_NULL},
 
	{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   107,   240,   251, STR_881F_BUILD_VEHICLE, STR_8844_BUILD_THE_HIGHLIGHTED_TRAIN},
 
	{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   108,   215,   240,   251, STR_8820_RENAME,        STR_8845_RENAME_TRAIN_VEHICLE_TYPE},
 
	{  WWT_RESIZEBOX,     RESIZE_TB,    14,   216,   227,   240,   251, 0x0,                    STR_RESIZE_BUTTON},
 
	{   WIDGETS_END},
 
};
 

	
 
static bool _internal_sort_order; // false = ascending, true = descending
 
static byte _last_sort_criteria = 0;
 
static bool _last_sort_order = false;
 

	
 
static int CDECL TrainEngineNumberSorter(const void *a, const void *b)
 
{
 
	const EngineID va = *(const EngineID*)a;
 
	const EngineID vb = *(const EngineID*)b;
 
	int r = ListPositionOfEngine(va) - ListPositionOfEngine(vb);
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL TrainEnginesThenWagonsSorter(const void *a, const void *b)
 
{
 
	EngineID va = *(const EngineID*)a;
 
	EngineID vb = *(const EngineID*)b;
 
	int val_a = ((RailVehInfo(va)->flags & RVI_WAGON) != 0) ? 1 : 0;
 
	int val_b = ((RailVehInfo(vb)->flags & RVI_WAGON) != 0) ? 1 : 0;
 
	int r = val_a - val_b;
 

	
 
	/* Use EngineID to sort instead since we want consistent sorting */
 
	if (r == 0) return TrainEngineNumberSorter(a, b);
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL TrainEngineCostSorter(const void *a, const void *b)
 
{
 
	int va = RailVehInfo(*(const EngineID*)a)->base_cost;
 
	int vb = RailVehInfo(*(const EngineID*)b)->base_cost;
 
	int r = va - vb;
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL TrainEngineSpeedSorter(const void *a, const void *b)
 
{
 
	int va = RailVehInfo(*(const EngineID*)a)->max_speed;
 
	int vb = RailVehInfo(*(const EngineID*)b)->max_speed;
 
	int r = va - vb;
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL TrainEnginePowerSorter(const void *a, const void *b)
 
{
 
	const RailVehicleInfo *rvi_a = RailVehInfo(*(const EngineID*)a);
 
	const RailVehicleInfo *rvi_b = RailVehInfo(*(const EngineID*)b);
 

	
 
	int va = rvi_a->power << (rvi_a->flags & RVI_MULTIHEAD ? 1 : 0);
 
	int vb = rvi_b->power << (rvi_b->flags & RVI_MULTIHEAD ? 1 : 0);
 
	int r = va - vb;
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL TrainEngineIntroDateSorter(const void *a, const void *b)
 
{
 
	int va = GetEngine(*(const EngineID*)a)->intro_date;
 
	int vb = GetEngine(*(const EngineID*)b)->intro_date;
 
	int r = va - vb;
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static EngineID _last_engine; // cached vehicle to hopefully speed up name-sorting
 

	
 
static char _bufcache[64]; // used together with _last_vehicle to hopefully speed up stringsorting
 
static int CDECL TrainEngineNameSorter(const void *a, const void *b)
 
{
 
	const EngineID va = *(const EngineID*)a;
 
	const EngineID vb = *(const EngineID*)b;
 
	char buf1[64];
 
	int r;
 

	
 
	GetString(buf1, GetCustomEngineName(va), lastof(buf1));
 

	
 
	if (vb != _last_engine) {
 
		_last_engine = vb;
 
		_bufcache[0] = '\0';
 

	
 
		GetString(_bufcache, GetCustomEngineName(vb), lastof(_bufcache));
 
	}
 

	
 
	r =  strcasecmp(buf1, _bufcache); // sort by name
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL TrainEngineRunningCostSorter(const void *a, const void *b)
 
{
 
	const RailVehicleInfo *rvi_a = RailVehInfo(*(const EngineID*)a);
 
	const RailVehicleInfo *rvi_b = RailVehInfo(*(const EngineID*)b);
 

	
 
	int va = rvi_a->running_cost_base * _price.running_rail[rvi_a->running_cost_class] * (rvi_a->flags & RVI_MULTIHEAD ? 2 : 1);
 
	int vb = rvi_b->running_cost_base * _price.running_rail[rvi_b->running_cost_class] * (rvi_b->flags & RVI_MULTIHEAD ? 2 : 1);
 
	int r = va - vb;
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL TrainEnginePowerVsRunningCostSorter(const void *a, const void *b)
 
{
 
	const RailVehicleInfo *rvi_a = RailVehInfo(*(const EngineID*)a);
 
	const RailVehicleInfo *rvi_b = RailVehInfo(*(const EngineID*)b);
 

	
 
	/* Here we are using a few tricks to get the right sort.
 
	 * We want power/running cost, but since we usually got higher running cost than power and we store the result in an int,
 
	 * we will actually calculate cunning cost/power (to make it more than 1).
 
	 * Because of this, the return value have to be reversed as well and we return b - a instead of a - b.
 
	 * Another thing is that both power and running costs should be doubled for multiheaded engines.
 
	 * Since it would be multipling with 2 in both numerator and denumerator, it will even themselves out and we skip checking for multiheaded. */
 
	int va = (rvi_a->running_cost_base * _price.running_rail[rvi_a->running_cost_class]) / max(1, rvi_a->power);
 
	int vb = (rvi_b->running_cost_base * _price.running_rail[rvi_b->running_cost_class]) / max(1, rvi_b->power);
 
	int r = vb - va;
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static int CDECL TrainEngineReliabilitySorter(const void *a, const void *b)
 
{
 
	int va = GetEngine(*(const EngineID*)a)->reliability;
 
	int vb = GetEngine(*(const EngineID*)b)->reliability;
 
	int r = va - vb;
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
static EngList_SortTypeFunction * const _engine_sorter[] = {
 
	&TrainEngineNumberSorter,
 
	&TrainEngineCostSorter,
 
	&TrainEngineSpeedSorter,
 
	&TrainEnginePowerSorter,
 
	&TrainEngineIntroDateSorter,
 
	&TrainEngineNameSorter,
 
	&TrainEngineRunningCostSorter,
 
	&TrainEnginePowerVsRunningCostSorter,
 
	&TrainEngineReliabilitySorter,
 
};
 

	
 
static const StringID _engine_sort_listing[] = {
 
	STR_ENGINE_SORT_ENGINE_ID,
 
	STR_ENGINE_SORT_COST,
 
	STR_SORT_BY_MAX_SPEED,
 
	STR_ENGINE_SORT_POWER,
 
	STR_ENGINE_SORT_INTRO_DATE,
 
	STR_SORT_BY_DROPDOWN_NAME,
 
	STR_ENGINE_SORT_RUNNING_COST,
 
	STR_ENGINE_SORT_POWER_VS_RUNNING_COST,
 
	STR_SORT_BY_RELIABILITY,
 
	INVALID_STRING_ID
 
};
 

	
 
/**
 
 * Draw the purchase info details of train engine at a given location.
 
 * @param x,y location where to draw the info
 
 * @param engine_number the engine of which to draw the info of
 
 */
 
void DrawTrainEnginePurchaseInfo(int x, int y, uint w, EngineID engine_number)
 
{
 
	const RailVehicleInfo *rvi = RailVehInfo(engine_number);
 
	const Engine *e = GetEngine(engine_number);
 
	int multihead = (rvi->flags&RVI_MULTIHEAD?1:0);
 
	YearMonthDay ymd;
 
	ConvertDateToYMD(e->intro_date, &ymd);
 

	
 
	/* Purchase Cost - Engine weight */
 
	SetDParam(0, rvi->base_cost * (_price.build_railvehicle >> 3) >> 5);
 
	SetDParam(1, rvi->weight << multihead);
 
	DrawString(x,y, STR_PURCHASE_INFO_COST_WEIGHT, 0);
 
	y += 10;
 

	
 
	/* Max speed - Engine power */
 
	SetDParam(0, rvi->max_speed);
 
	SetDParam(1, rvi->power << multihead);
 
	DrawString(x,y, STR_PURCHASE_INFO_SPEED_POWER, 0);
 
	y += 10;
 

	
 
	/* Max tractive effort - not applicable if old acceleration or maglev */
 
	if (_patches.realistic_acceleration && e->railtype != RAILTYPE_MAGLEV) {
 
		SetDParam(0, ((rvi->weight << multihead) * 10 * rvi->tractive_effort) / 256);
 
		DrawString(x, y, STR_PURCHASE_INFO_MAX_TE, 0);
 
		y += 10;
 
	}
 

	
 
	/* Running cost */
 
	SetDParam(0, (rvi->running_cost_base * _price.running_rail[rvi->running_cost_class] >> 8) << multihead);
 
	DrawString(x,y, STR_PURCHASE_INFO_RUNNINGCOST, 0);
 
	y += 10;
 

	
 
	/* Powered wagons power - Powered wagons extra weight */
 
	if (rvi->pow_wag_power != 0) {
 
		SetDParam(0, rvi->pow_wag_power);
 
		SetDParam(1, rvi->pow_wag_weight);
 
		DrawString(x,y, STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT, 0);
 
		y += 10;
 
	};
 

	
 
	/* Cargo type + capacity, or N/A */
 
	if (rvi->capacity == 0) {
 
		SetDParam(0, CT_INVALID);
 
		SetDParam(2, STR_EMPTY);
 
	} else {
 
		SetDParam(0, rvi->cargo_type);
 
		SetDParam(1, (rvi->capacity * (CountArticulatedParts(engine_number) + 1)) << multihead);
 
		SetDParam(2, STR_9842_REFITTABLE);
 
	}
 
	DrawString(x,y, STR_PURCHASE_INFO_CAPACITY, 0);
 
	y += 10;
 

	
 
	/* Design date - Life length */
 
	SetDParam(0, ymd.year);
 
	SetDParam(1, e->lifelength);
 
	DrawString(x,y, STR_PURCHASE_INFO_DESIGNED_LIFE, 0);
 
	y += 10;
 

	
 
	/* Reliability */
 
	SetDParam(0, e->reliability * 100 >> 16);
 
	DrawString(x,y, STR_PURCHASE_INFO_RELIABILITY, 0);
 
	y += 10;
 

	
 
	/* Additional text from NewGRF */
 
	y += ShowAdditionalText(x, y, w, engine_number);
 
	if (rvi->capacity > 0) y += ShowRefitOptionsList(x, y, w, engine_number);
 
}
 

	
 
/**
 
 * Draw the purchase info details of a train wagon at a given location.
 
 * @param x,y location where to draw the info
 
 * @param engine_number the engine of which to draw the info of
 
 */
 
void DrawTrainWagonPurchaseInfo(int x, int y, uint w, EngineID engine_number)
 
{
 
	const RailVehicleInfo *rvi = RailVehInfo(engine_number);
 
	bool refittable = (EngInfo(engine_number)->refit_mask != 0);
 

	
 
	/* Purchase cost */
 
	SetDParam(0, (rvi->base_cost * _price.build_railwagon) >> 8);
 
	DrawString(x, y, STR_PURCHASE_INFO_COST, 0);
 
	y += 10;
 

	
 
	/* Wagon weight - (including cargo) */
 
	SetDParam(0, rvi->weight);
 
	SetDParam(1, (_cargoc.weights[rvi->cargo_type] * rvi->capacity >> 4) + rvi->weight);
 
	DrawString(x, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT, 0);
 
	y += 10;
 

	
 
	/* Cargo type + capacity, or N/A */
 
	if (rvi->capacity == 0) {
 
		SetDParam(0, CT_INVALID);
 
		SetDParam(2, STR_EMPTY);
 
	} else {
 
		SetDParam(0, rvi->cargo_type);
 
		SetDParam(1, rvi->capacity * (CountArticulatedParts(engine_number) + 1));
 
		SetDParam(2, refittable ? STR_9842_REFITTABLE : STR_EMPTY);
 
	}
 
	DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, 0);
 
	y += 10;
 

	
 
	/* Wagon speed limit, displayed if above zero */
 
	if (rvi->max_speed > 0 && _patches.wagon_speed_limits) {
 
		SetDParam(0, rvi->max_speed);
 
		DrawString(x,y, STR_PURCHASE_INFO_SPEED, 0);
 
		y += 10;
 
	}
 

	
 
	/* Additional text from NewGRF */
 
	y += ShowAdditionalText(x, y, w, engine_number);
 
	y += ShowRefitOptionsList(x, y, w, engine_number);
 
}
 

	
 
void CcBuildWagon(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v, *found;
 

	
 
	if (!success) return;
 

	
 
	// find a locomotive in the depot.
 
	found = NULL;
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Train && IsFrontEngine(v) &&
 
				v->tile == tile &&
 
				v->u.rail.track == 0x80) {
 
			if (found != NULL) return; // must be exactly one.
 
			found = v;
 
		}
 
	}
 

	
 
	// if we found a loco,
 
	if (found != NULL) {
 
		found = GetLastVehicleInChain(found);
 
		// put the new wagon at the end of the loco.
 
		DoCommandP(0, _new_vehicle_id | (found->index << 16), 0, NULL, CMD_MOVE_RAIL_VEHICLE);
 
		RebuildVehicleLists();
 
	}
 
}
 

	
 
void CcBuildLoco(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	const Vehicle *v;
 

	
 
	if (!success) return;
 

	
 
	v = GetVehicle(_new_vehicle_id);
 
	if (tile == _backup_orders_tile) {
 
		_backup_orders_tile = 0;
 
		RestoreVehicleOrders(v, _backup_orders_data);
 
	}
 
	ShowTrainViewWindow(v);
 
}
 

	
 
void CcCloneTrain(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
{
 
	if (success) ShowTrainViewWindow(GetVehicle(_new_vehicle_id));
 
}
 

	
 
static void engine_drawing_loop(const EngineList *engines, int x, int *y, EngineID sel, EngineID position, int16 show_max)
 
{
 
	int count = min(EngList_Count(engines), show_max);
 
	for (; position < count; *y += 14, position++) {
 
		EngineID id = (*engines)[position];
 
		DrawString(x + 59, *y + 2, GetCustomEngineName(id), sel == id ? 0xC : 0x10);
 
		DrawTrainEngine(x + 29, *y + 6, id, GetEnginePalette(id, _local_player));
 
	}
 
}
 

	
 
static void GenerateBuildList(Window *w)
 
{
 
	EngineID eid, sel_id;
 
	int num_engines = 0;
 
	int num_wagons  = 0;
 
	buildvehicle_d *bv = &WP(w, buildvehicle_d);
 

	
 
	bv->filter.railtype = (w->window_number == 0) ? RAILTYPE_END : GetRailType(w->window_number);
 

	
 
	EngList_RemoveAll(&bv->eng_list);
 

	
 
	/* Make list of all available train engines and wagons.
 
	 * Also check to see if the previously selected engine is still available,
 
	 * and if not, reset selection to INVALID_ENGINE. This could be the case
 
	 * when engines become obsolete and are removed */
 
	for (sel_id = INVALID_ENGINE, eid = 0; eid < NUM_TRAIN_ENGINES; eid++) {
 
		const Engine *e = GetEngine(eid);
 
		const RailVehicleInfo *rvi = RailVehInfo(eid);
 

	
 
		if (bv->filter.railtype != RAILTYPE_END && !HasPowerOnRail(e->railtype, bv->filter.railtype)) continue;
 
		if (!IsEngineBuildable(eid, VEH_Train, _local_player)) continue;
 

	
 
		EngList_Add(&bv->eng_list, eid);
 
		if ((rvi->flags & RVI_WAGON) == 0) {
 
			num_engines++;
 
		} else {
 
			num_wagons++;
 
		}
 

	
 
		if (eid == bv->sel_engine) sel_id = eid;
 
	}
 

	
 
	bv->sel_engine = sel_id;
 

	
 
	// make engines first, and then wagons, sorted by ListPositionOfEngine()
 
	_internal_sort_order = false;
 
	EngList_Sort(&bv->eng_list, TrainEnginesThenWagonsSorter);
 

	
 
	// and then sort engines
 
	_internal_sort_order = bv->descending_sort_order;
 
	EngList_SortPartial(&bv->eng_list, _engine_sorter[bv->sort_criteria], 0, num_engines);
 

	
 
	// and finally sort wagons
 
	EngList_SortPartial(&bv->eng_list, _engine_sorter[bv->sort_criteria], num_engines, num_wagons);
 
}
 

	
 
static void DrawTrainBuildWindow(Window *w)
 
{
 
	const buildvehicle_d *bv = &WP(w, buildvehicle_d);
 
	int x = 1;
 
	int y = 27;
 
	EngineID selected_id = bv->sel_engine;
 
	int max = w->vscroll.pos + w->vscroll.cap;
 

	
 
	SetWindowWidgetDisabledState(w, BUILD_TRAIN_WIDGET_BUILD, w->window_number == 0); // Disable unless we got a depot to build in
 

	
 
	SetVScrollCount(w, EngList_Count(&bv->eng_list));
 
	SetDParam(0, bv->filter.railtype + STR_881C_NEW_RAIL_VEHICLES);
 
	DrawWindowWidgets(w);
 

	
 
	/* Draw the engines */
 
	engine_drawing_loop(&bv->eng_list, x, &y, selected_id, w->vscroll.pos, max);
 

	
 
	if (selected_id != INVALID_ENGINE) {
 
		const RailVehicleInfo *rvi = RailVehInfo(selected_id);
 
		const Widget *wi = &w->widget[BUILD_TRAIN_WIDGET_PANEL];
 

	
 
		if (rvi->flags & RVI_WAGON) {
 
			DrawTrainWagonPurchaseInfo(2, wi->top + 1, wi->right - wi->left - 2, selected_id);
 
		} else {
 
			DrawTrainEnginePurchaseInfo(2, wi->top + 1, wi->right - wi->left - 2, selected_id);
 
		}
 
	}
 
	DrawString(85, 15, _engine_sort_listing[bv->sort_criteria], 0x10);
 
	DoDrawString(bv->descending_sort_order ? DOWNARROW : UPARROW, 69, 15, 0x10);
 
}
 

	
 
static void NewRailVehicleWndProc(Window *w, WindowEvent *e)
 
{
 
	buildvehicle_d *bv = &WP(w, buildvehicle_d);
 
	switch (e->event) {
 
		case WE_CREATE:
 
			EngList_Create(&bv->eng_list);
 
			bv->sel_engine            = INVALID_ENGINE;
 
			bv->sort_criteria         = _last_sort_criteria;
 
			bv->descending_sort_order = _last_sort_order;
 
			GenerateBuildList(w);
 
			/* Select the first engine in the list as default when opening the window */
 
			if (EngList_Count(&bv->eng_list) > 0) bv->sel_engine = bv->eng_list[0];
 
			break;
 

	
 
		case WE_INVALIDATE_DATA:
 
			GenerateBuildList(w);
 
			break;
 

	
 
		case WE_DESTROY:
 
			EngList_Destroy(&bv->eng_list);
 
			break;
 

	
 
		case WE_PAINT:
 
			DrawTrainBuildWindow(w);
 
			break;
 

	
 
		case WE_CLICK: {
 
			switch (e->we.click.widget) {
 
				case BUILD_TRAIN_WIDGET_SORT_ASCENDING_DESCENDING:
 
					bv->descending_sort_order ^= true;
 
					_last_sort_order = bv->descending_sort_order;
 
					GenerateBuildList(w);
 
					SetWindowDirty(w);
 
					break;
 

	
 
				case BUILD_TRAIN_WIDGET_SORT_TEXT: case BUILD_TRAIN_WIDGET_SORT_DROPDOWN:/* Select sorting criteria dropdown menu */
 
					ShowDropDownMenu(w, _engine_sort_listing, bv->sort_criteria, BUILD_TRAIN_WIDGET_SORT_DROPDOWN, 0, 0);
 
					return;
 

	
 
				case BUILD_TRAIN_WIDGET_LIST: {
 
					uint i = ((e->we.click.pt.y - 26) / 14) + w->vscroll.pos;
 
					uint num_items = EngList_Count(&bv->eng_list);
 
					bv->sel_engine = (i < num_items) ? bv->eng_list[i] : INVALID_ENGINE;
 
					SetWindowDirty(w);
 
					break;
 
				}
 

	
 
				case BUILD_TRAIN_WIDGET_BUILD: {
 
					EngineID sel_eng = bv->sel_engine;
 
					if (sel_eng != INVALID_ENGINE)
 
						DoCommandP(w->window_number, sel_eng, 0, (RailVehInfo(sel_eng)->flags & RVI_WAGON) ? CcBuildWagon : CcBuildLoco, CMD_BUILD_RAIL_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE));
 
					break;
 
				}
 

	
 
				case BUILD_TRAIN_WIDGET_RENAME: {
 
					EngineID sel_eng = bv->sel_engine;
 
					if (sel_eng != INVALID_ENGINE) {
 
						bv->rename_engine = sel_eng;
 
						ShowQueryString(GetCustomEngineName(sel_eng), STR_886A_RENAME_TRAIN_VEHICLE_TYPE, 31, 160, w, CS_ALPHANUMERAL);
 
					}
 
					break;
 
				}
 
			}
 
		}
 
		break;
 

	
 
		case WE_ON_EDIT_TEXT: {
 
			if (e->we.edittext.str[0] != '\0') {
 
				_cmd_text = e->we.edittext.str;
 
				DoCommandP(0, bv->rename_engine, 0, NULL, CMD_RENAME_ENGINE | CMD_MSG(STR_886B_CAN_T_RENAME_TRAIN_VEHICLE));
 
			}
 
			break;
 
		}
 

	
 
		case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
			if (bv->sort_criteria != e->we.dropdown.index) {
 
				bv->sort_criteria = _last_sort_criteria = e->we.dropdown.index;
 
				GenerateBuildList(w);
 
				SetWindowDirty(w);
 
			}
 
			break;
 

	
 
		case WE_RESIZE: {
 
			if (e->we.sizing.diff.y == 0) break;
 

	
 
			w->vscroll.cap += e->we.sizing.diff.y / 14;
 
			w->widget[BUILD_TRAIN_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
 
			break;
 
		}
 
	}
 
}
 

	
 
static const WindowDesc _new_rail_vehicle_desc = {
 
	WDP_AUTO, WDP_AUTO, 228, 252,
 
	WC_BUILD_VEHICLE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_new_rail_vehicle_widgets,
 
	NewRailVehicleWndProc
 
};
 

	
 
void ShowBuildTrainWindow(TileIndex tile)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_BUILD_VEHICLE, tile);
 

	
 
	w = AllocateWindowDescFront(&_new_rail_vehicle_desc, tile);
 
	w->vscroll.cap = 8;
 
	w->widget[BUILD_TRAIN_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
 

	
 
	w->resize.step_height = 14;
 
	w->resize.height = w->height - 14 * 4; // Minimum of 4 vehicles in the display
 

	
 
	if (tile != 0) {
 
		w->caption_color = GetTileOwner(tile);
 
		WP(w,buildvehicle_d).filter.railtype = GetRailType(tile);
 
	} else {
 
		w->caption_color = _local_player;
 
		WP(w,buildvehicle_d).filter.railtype = RAILTYPE_END;
 
	}
 
}
 

	
 
/**
 
 * Get the number of pixels for the given wagon length.
 
 * @param len Length measured in 1/8ths of a standard wagon.
 
 * @return Number of pixels across.
 
 */
 
int WagonLengthToPixels(int len) {
 
	return (len * _traininfo_vehicle_width) / 8;
 
}
 

	
 
void DrawTrainImage(const Vehicle *v, int x, int y, int count, int skip, VehicleID selection)
 
{
 
	DrawPixelInfo tmp_dpi, *old_dpi;
 
	int dx = -(skip * 8) / _traininfo_vehicle_width;
 
	/* Position of highlight box */
 
	int highlight_l = 0;
 
	int highlight_r = 0;
 

	
 
	if (!FillDrawPixelInfo(&tmp_dpi, x - 2, y - 1, count + 1, 14)) return;
 

	
 
	count = (count * 8) / _traininfo_vehicle_width;
 

	
 
	old_dpi = _cur_dpi;
 
	_cur_dpi = &tmp_dpi;
 

	
 
	do {
 
		int width = v->u.rail.cached_veh_length;
 

	
 
		if (dx + width > 0) {
 
			if (dx <= count) {
 
				PalSpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
 
				DrawSprite(GetTrainImage(v, DIR_W) | pal, 16 + WagonLengthToPixels(dx), 7 + (is_custom_sprite(RailVehInfo(v->engine_type)->image_index) ? _traininfo_vehicle_pitch : 0));
 
				if (v->index == selection) {
 
					/* Set the highlight position */
 
					highlight_l = WagonLengthToPixels(dx) + 1;
 
					highlight_r = WagonLengthToPixels(dx + width) + 1;
 
				}
 
			}
 
		}
 
		dx += width;
 

	
 
		v = v->next;
 
	} while (dx < count && v != NULL);
 

	
 
	if (highlight_l != highlight_r) {
 
		/* Draw the highlight. Now done after drawing all the engines, as
 
		 * the next engine after the highlight could overlap it. */
 
		DrawFrameRect(highlight_l, 0, highlight_r, 13, 15, FR_BORDERONLY);
 
	}
 

	
 
	_cur_dpi = old_dpi;
 
}
 

	
 
static const Widget _train_view_widgets[] = {
 
{   WWT_CLOSEBOX,  RESIZE_NONE, 14,   0,  10,   0,  13, STR_00C5,                STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION, RESIZE_RIGHT, 14,  11, 237,   0,  13, STR_882E,                STR_018C_WINDOW_TITLE_DRAG_THIS },
 
{  WWT_STICKYBOX,    RESIZE_LR, 14, 238, 249,   0,  13, 0x0,                     STR_STICKY_BUTTON },
 
{      WWT_PANEL,    RESIZE_RB, 14,   0, 231,  14, 121, 0x0,                     STR_NULL },
 
{      WWT_INSET,    RESIZE_RB, 14,   2, 229,  16, 119, 0x0,                     STR_NULL },
 
{    WWT_PUSHBTN,   RESIZE_RTB, 14,   0, 237, 122, 133, 0x0,                     STR_8846_CURRENT_TRAIN_ACTION_CLICK },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR, 14, 232, 249,  14,  31, SPR_CENTRE_VIEW_VEHICLE, STR_8848_CENTER_MAIN_VIEW_ON_TRAIN },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR, 14, 232, 249,  32,  49, SPR_SEND_TRAIN_TODEPOT,  STR_8849_SEND_TRAIN_TO_DEPOT },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR, 14, 232, 249,  50,  67, SPR_IGNORE_SIGNALS,      STR_884A_FORCE_TRAIN_TO_PROCEED },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR, 14, 232, 249,  68,  85, SPR_FORCE_VEHICLE_TURN,  STR_884B_REVERSE_DIRECTION_OF_TRAIN },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR, 14, 232, 249,  86, 103, SPR_SHOW_ORDERS,         STR_8847_SHOW_TRAIN_S_ORDERS },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR, 14, 232, 249, 104, 121, SPR_SHOW_VEHICLE_DETAILS,STR_884C_SHOW_TRAIN_DETAILS },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR, 14, 232, 249,  68,  85, SPR_REFIT_VEHICLE,       STR_RAIL_REFIT_VEHICLE_TO_CARRY },
 
{ WWT_PUSHIMGBTN,    RESIZE_LR, 14, 232, 249,  32,  49, SPR_CLONE_TRAIN,         STR_CLONE_TRAIN_INFO },
 
{      WWT_PANEL,   RESIZE_LRB, 14, 232, 249, 122, 121, 0x0,                     STR_NULL },
 
{  WWT_RESIZEBOX,  RESIZE_LRTB, 14, 238, 249, 122, 133, 0x0,                     STR_NULL },
 
{ WIDGETS_END }
 
};
 

	
 
static void ShowTrainDetailsWindow(const Vehicle *v);
 

	
 
static void TrainViewWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const Vehicle *v, *u;
 
		StringID str;
 
		bool is_localplayer;
 

	
 
		v = GetVehicle(w->window_number);
 

	
 
		is_localplayer = v->owner == _local_player;
 
		SetWindowWidgetDisabledState(w,  7, !is_localplayer);
 
		SetWindowWidgetDisabledState(w,  8, !is_localplayer);
 
		SetWindowWidgetDisabledState(w,  9, !is_localplayer);
 
		SetWindowWidgetDisabledState(w, 13, !is_localplayer);
 

	
 
		/* Disable cargo refit button, until we know we can enable it below. */
 
		DisableWindowWidget(w, 12);
 

	
 
		if (is_localplayer) {
 
			/* See if any vehicle can be refitted */
 
			for (u = v; u != NULL; u = u->next) {
 
				if (EngInfo(u->engine_type)->refit_mask != 0 ||
 
						(!(RailVehInfo(v->engine_type)->flags & RVI_WAGON) && v->cargo_cap != 0)) {
 
					EnableWindowWidget(w, 12);
 
					/* We have a refittable carriage, bail out */
 
					break;
 
				}
 
			}
 
		}
 

	
 
		/* draw widgets & caption */
 
		SetDParam(0, v->string_id);
 
		SetDParam(1, v->unitnumber);
 
		DrawWindowWidgets(w);
 

	
 
		if (v->u.rail.crash_anim_pos != 0) {
 
			str = STR_8863_CRASHED;
 
		} else if (v->breakdown_ctr == 1) {
 
			str = STR_885C_BROKEN_DOWN;
 
		} else if (v->vehstatus & VS_STOPPED) {
 
			if (v->u.rail.last_speed == 0) {
 
				if (v->u.rail.cached_power == 0) {
 
					str = STR_TRAIN_NO_POWER;
 
				} else {
 
					str = STR_8861_STOPPED;
 
				}
 
			} else {
 
				SetDParam(0, v->u.rail.last_speed);
 
				str = STR_TRAIN_STOPPING + _patches.vehicle_speed;
 
			}
 
		} else {
 
			switch (v->current_order.type) {
 
			case OT_GOTO_STATION: {
 
				str = STR_HEADING_FOR_STATION + _patches.vehicle_speed;
 
				SetDParam(0, v->current_order.dest);
 
				SetDParam(1, v->u.rail.last_speed);
 
			} break;
 

	
 
			case OT_GOTO_DEPOT: {
 
				Depot *dep = GetDepot(v->current_order.dest);
 
				SetDParam(0, dep->town_index);
 
				if (HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT) && !HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) {
 
					str = STR_HEADING_FOR_TRAIN_DEPOT + _patches.vehicle_speed;
 
				} else {
 
					str = STR_HEADING_FOR_TRAIN_DEPOT_SERVICE + _patches.vehicle_speed;
 
				}
 
				SetDParam(1, v->u.rail.last_speed);
 
			} break;
 

	
 
			case OT_LOADING:
 
			case OT_LEAVESTATION:
 
				str = STR_882F_LOADING_UNLOADING;
 
				break;
 

	
 
			case OT_GOTO_WAYPOINT: {
 
				SetDParam(0, v->current_order.dest);
 
				str = STR_HEADING_FOR_WAYPOINT + _patches.vehicle_speed;
 
				SetDParam(1, v->u.rail.last_speed);
 
				break;
 
			}
 

	
 
			default:
 
				if (v->num_orders == 0) {
 
					str = STR_NO_ORDERS + _patches.vehicle_speed;
 
					SetDParam(0, v->u.rail.last_speed);
 
				} else {
 
					str = STR_EMPTY;
 
				}
 
				break;
 
			}
 
		}
 

	
 
		/* draw the flag plus orders */
 
		DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, 2, w->widget[5].top + 1);
 
		DrawStringCenteredTruncated(w->widget[5].left + 8, w->widget[5].right, w->widget[5].top + 1, str, 0);
 
		DrawWindowViewport(w);
 
	}	break;
 

	
 
	case WE_CLICK: {
 
		int wid = e->we.click.widget;
 
		Vehicle *v = GetVehicle(w->window_number);
 

	
 
		switch (wid) {
 
		case 5: /* start/stop train */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_TRAIN | CMD_MSG(STR_883B_CAN_T_STOP_START_TRAIN));
 
			break;
 
		case 6: /* center main view */
 
			ScrollMainWindowTo(v->x_pos, v->y_pos);
 
			break;
 
		case 7: /* goto depot */
 
			/* TrainGotoDepot has a nice randomizer in the pathfinder, which causes desyncs... */
 
			DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0, NULL, CMD_SEND_TRAIN_TO_DEPOT | CMD_NO_TEST_IF_IN_NETWORK | CMD_MSG(STR_8830_CAN_T_SEND_TRAIN_TO_DEPOT));
 
			break;
 
		case 8: /* force proceed */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_FORCE_TRAIN_PROCEED | CMD_MSG(STR_8862_CAN_T_MAKE_TRAIN_PASS_SIGNAL));
 
			break;
 
		case 9: /* reverse direction */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_8869_CAN_T_REVERSE_DIRECTION));
 
			break;
 
		case 10: /* show train orders */
 
			ShowOrdersWindow(v);
 
			break;
 
		case 11: /* show train details */
 
			ShowTrainDetailsWindow(v);
 
			break;
 
		case 12:
 
			ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID);
 
			break;
 
		case 13:
 
			DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, NULL, CMD_CLONE_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE));
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_RESIZE:
 
		w->viewport->width          += e->we.sizing.diff.x;
 
		w->viewport->height         += e->we.sizing.diff.y;
 
		w->viewport->virtual_width  += e->we.sizing.diff.x;
 
		w->viewport->virtual_height += e->we.sizing.diff.y;
 
		break;
 

	
 
	case WE_DESTROY:
 
		DeleteWindowById(WC_VEHICLE_REFIT, w->window_number);
 
		DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number);
 
		DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number);
 
		break;
 

	
 
	case WE_MOUSELOOP: {
 
		const Vehicle *v = GetVehicle(w->window_number);
 
		bool train_stopped = CheckTrainStoppedInDepot(v)  >= 0;
 

	
 
		/* Widget 7 (send to depot) must be hidden if the train is already stopped in hangar.
 
		 * Widget 13 (clone) should then be shown, since cloning is allowed only while in depot and stopped.
 
		 * This sytem allows to have two buttons, on top of each other.
 
		 * The same system applies to widget 9 and 12, reverse direction and refit*/
 
		if (train_stopped != IsWindowWidgetHidden(w, 7) || train_stopped == IsWindowWidgetHidden(w, 13)) {
 
			SetWindowWidgetHiddenState(w,  7, train_stopped);  // send to depot
 
			SetWindowWidgetHiddenState(w,  9, train_stopped);  // reverse direction
 
			SetWindowWidgetHiddenState(w, 12, !train_stopped); // refit
 
			SetWindowWidgetHiddenState(w, 13, !train_stopped); // clone
 
			SetWindowDirty(w);
 
		}
 
		break;
 
	}
 

	
 
	}
 
}
 

	
 
static const WindowDesc _train_view_desc = {
 
	WDP_AUTO, WDP_AUTO, 250, 134,
 
	WC_VEHICLE_VIEW,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_train_view_widgets,
 
	TrainViewWndProc
 
};
 

	
 
void ShowTrainViewWindow(const Vehicle *v)
 
{
 
	Window *w = AllocateWindowDescFront(&_train_view_desc,v->index);
 

	
 
	if (w != NULL) {
 
		w->caption_color = v->owner;
 
		AssignWindowViewport(w, 3, 17, 0xE2, 0x66, w->window_number | (1 << 31), 0);
 
	}
 
}
 

	
 
static void TrainDetailsCargoTab(const Vehicle *v, int x, int y)
 
{
 
	if (v->cargo_cap != 0) {
 
		uint num = v->cargo_count;
 
		StringID str = STR_8812_EMPTY;
 

	
 
		if (num != 0) {
 
			SetDParam(0, v->cargo_type);
 
			SetDParam(1, num);
 
			SetDParam(2, v->cargo_source);
 
			SetDParam(3, _patches.freight_trains);
 
			str = FreightWagonMult(v->cargo_type) > 1 ? STR_FROM_MULT : STR_8813_FROM;
 
		}
 
		DrawString(x, y, str, 0);
 
	}
 
}
 

	
 
static void TrainDetailsInfoTab(const Vehicle *v, int x, int y)
 
{
 
	if (RailVehInfo(v->engine_type)->flags & RVI_WAGON) {
 
		SetDParam(0, GetCustomEngineName(v->engine_type));
 
		SetDParam(1, v->value);
 
		DrawString(x, y, STR_882D_VALUE, 0x10);
 
	} else {
 
		SetDParam(0, GetCustomEngineName(v->engine_type));
 
		SetDParam(1, v->build_year);
 
		SetDParam(2, v->value);
 
		DrawString(x, y, STR_882C_BUILT_VALUE, 0x10);
 
	}
 
}
 

	
 
static void TrainDetailsCapacityTab(const Vehicle *v, int x, int y)
 
{
 
	if (v->cargo_cap != 0) {
 
		SetDParam(0, v->cargo_type);
 
		SetDParam(1, v->cargo_cap);
 
		SetDParam(2, _patches.freight_trains);
 
		DrawString(x, y, FreightWagonMult(v->cargo_type) > 1 ? STR_CAPACITY_MULT : STR_013F_CAPACITY, 0);
 
	}
 
}
 

	
 

	
 
static void DrawTrainDetailsWindow(Window *w)
 
{
 
	byte det_tab = WP(w, traindetails_d).tab;
 
	const Vehicle *v;
 
	const Vehicle *u;
 
	AcceptedCargo act_cargo;
 
	AcceptedCargo max_cargo;
 
	uint i;
 
	int num;
 
	int x;
 
	int y;
 
	int sel;
 

	
 
	num = 0;
 
	u = v = GetVehicle(w->window_number);
 
	if (det_tab == 3) { // Total cargo tab
 
		for (i = 0; i < lengthof(act_cargo); i++) {
 
			act_cargo[i] = 0;
 
			max_cargo[i] = 0;
 
		}
 

	
 
		do {
 
			act_cargo[u->cargo_type] += u->cargo_count;
 
			max_cargo[u->cargo_type] += u->cargo_cap;
 
		} while ((u = u->next) != NULL);
 

	
 
		/* Set scroll-amount seperately from counting, as to not compute num double
 
		 * for more carriages of the same type
 
		 */
 
		for (i = 0; i != NUM_CARGO; i++) {
 
			if (max_cargo[i] > 0) num++; // only count carriages that the train has
 
		}
 
		num++; // needs one more because first line is description string
 
	} else {
 
		do {
 
			if (!IsArticulatedPart(u) || u->cargo_cap != 0) num++;
 
		} while ((u = u->next) != NULL);
 
	}
 

	
 
	SetVScrollCount(w, num);
 

	
 
	DisableWindowWidget(w, det_tab + 9);
 
	SetWindowWidgetDisabledState(w, 2, v->owner != _local_player);
 

	
 
	/* disable service-scroller when interval is set to disabled */
 
	SetWindowWidgetDisabledState(w, 6, !_patches.servint_trains);
 
	SetWindowWidgetDisabledState(w, 7, !_patches.servint_trains);
 

	
 
	SetDParam(0, v->string_id);
 
	SetDParam(1, v->unitnumber);
 
	DrawWindowWidgets(w);
 

	
 
	SetDParam(1, v->age / 366);
 

	
 
	x = 2;
 

	
 
	SetDParam(0, (v->age + 365 < v->max_age) ? STR_AGE : STR_AGE_RED);
 
	SetDParam(2, v->max_age / 366);
 
	SetDParam(3, GetTrainRunningCost(v) >> 8);
 
	DrawString(x, 15, STR_885D_AGE_RUNNING_COST_YR, 0);
 

	
 
	SetDParam(2, v->u.rail.cached_max_speed);
 
	SetDParam(1, v->u.rail.cached_power);
 
	SetDParam(0, v->u.rail.cached_weight);
 
	SetDParam(3, v->u.rail.cached_max_te / 1000);
 
	DrawString(x, 25, (_patches.realistic_acceleration && v->u.rail.railtype != RAILTYPE_MAGLEV) ?
 
		STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :
 
		STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, 0);
 

	
 
	SetDParam(0, v->profit_this_year);
 
	SetDParam(1, v->profit_last_year);
 
	DrawString(x, 35, STR_885F_PROFIT_THIS_YEAR_LAST_YEAR, 0);
 

	
 
	SetDParam(0, 100 * (v->reliability>>8) >> 8);
 
	SetDParam(1, v->breakdowns_since_last_service);
 
	DrawString(x, 45, STR_8860_RELIABILITY_BREAKDOWNS, 0);
 

	
 
	SetDParam(0, v->service_interval);
 
	SetDParam(1, v->date_of_last_service);
 
	DrawString(x + 11, 57 + (w->vscroll.cap * 14), _patches.servint_ispercent ? STR_SERVICING_INTERVAL_PERCENT : STR_883C_SERVICING_INTERVAL_DAYS, 0);
 

	
 
	y = 57;
 
	sel = w->vscroll.pos;
 

	
 
	// draw the first 3 details tabs
 
	if (det_tab != 3) {
 
		x = 1;
 
		for (;;) {
 
			if (--sel < 0 && sel >= -w->vscroll.cap) {
 
				int dx = 0;
 
				int px;
 
				int py;
 

	
 
				u = v;
 
				do {
 
					PalSpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
 
					DrawSprite(GetTrainImage(u, DIR_W) | pal, x + WagonLengthToPixels(4 + dx), y + 6 + (is_custom_sprite(RailVehInfo(u->engine_type)->image_index) ? _traininfo_vehicle_pitch : 0));
 
					dx += u->u.rail.cached_veh_length;
 
					u = u->next;
 
				} while (u != NULL && IsArticulatedPart(u) && u->cargo_cap == 0);
 

	
 
				px = x + WagonLengthToPixels(dx) + 2;
 
				py = y + 2;
 
				switch (det_tab) {
 
					default: NOT_REACHED();
 
					case 0: TrainDetailsCargoTab(   v, px, py); break;
 
					case 1:
 
						// Only show name and value for the 'real' part
 
						if (!IsArticulatedPart(v)) {
 
							TrainDetailsInfoTab(v, px, py);
 
						}
 
						break;
 
					case 2: TrainDetailsCapacityTab(v, px, py); break;
 
				}
 
				y += 14;
 

	
 
				v = u;
 
			} else {
 
				// Move to the next line
 
				do {
 
					v = v->next;
 
				} while (v != NULL && IsArticulatedPart(v) && v->cargo_cap == 0);
 
			}
 
			if (v == NULL) return;
 
		}
 
	} else {
 
		// draw total cargo tab
 
		DrawString(x, y + 2, STR_013F_TOTAL_CAPACITY_TEXT, 0);
 
		for (i = 0; i != NUM_CARGO; i++) {
 
			if (max_cargo[i] > 0 && --sel < 0 && sel > -w->vscroll.cap) {
 
				y += 14;
 
				SetDParam(0, i);            // {CARGO} #1
 
				SetDParam(1, act_cargo[i]); // {CARGO} #2
 
				SetDParam(2, i);            // {SHORTCARGO} #1
 
				SetDParam(3, max_cargo[i]); // {SHORTCARGO} #2
 
				SetDParam(4, _patches.freight_trains);
 
				DrawString(x, y + 2, FreightWagonMult(i) > 1 ? STR_TOTAL_CAPACITY_MULT : STR_013F_TOTAL_CAPACITY, 0);
 
			}
 
		}
 
	}
 
}
 

	
 
static void TrainDetailsWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT:
 
		DrawTrainDetailsWindow(w);
 
		break;
 
	case WE_CLICK: {
 
		int mod;
 
		const Vehicle *v;
 
		switch (e->we.click.widget) {
 
		case 2: /* name train */
 
			v = GetVehicle(w->window_number);
 
			SetDParam(0, v->unitnumber);
 
			ShowQueryString(v->string_id, STR_8865_NAME_TRAIN, 31, 150, w, CS_ALPHANUMERAL);
 
			break;
 
		case 6: /* inc serv interval */
 
			mod = _ctrl_pressed? 5 : 10;
 
			goto do_change_service_int;
 

	
 
		case 7: /* dec serv interval */
 
			mod = _ctrl_pressed? -5 : -10;
 
do_change_service_int:
 
			v = GetVehicle(w->window_number);
 

	
 
			mod = GetServiceIntervalClamped(mod + v->service_interval);
 
			if (mod == v->service_interval) return;
 

	
 
			DoCommandP(v->tile, v->index, mod, NULL, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING));
 
			break;
 
		/* details buttons*/
 
		case 9:  // Cargo
 
		case 10: // Information
 
		case 11: // Capacities
 
		case 12: // Total cargo
 
			EnableWindowWidget(w,  9);
 
			EnableWindowWidget(w, 10);
 
			EnableWindowWidget(w, 11);
 
			EnableWindowWidget(w, 12);
 
			EnableWindowWidget(w, e->we.click.widget);
 
			WP(w,traindetails_d).tab = e->we.click.widget - 9;
 
			SetWindowDirty(w);
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_ON_EDIT_TEXT:
 
		if (e->we.edittext.str[0] != '\0') {
 
			_cmd_text = e->we.edittext.str;
 
			DoCommandP(0, w->window_number, 0, NULL,
 
				CMD_NAME_VEHICLE | CMD_MSG(STR_8866_CAN_T_NAME_TRAIN));
 
		}
 
		break;
 

	
 
	case WE_RESIZE:
 
		if (e->we.sizing.diff.y == 0) break;
 

	
 
		w->vscroll.cap += e->we.sizing.diff.y / 14;
 
		w->widget[4].data = (w->vscroll.cap << 8) + 1;
 
		break;
 
	}
 
}
 

	
 
static const Widget _train_details_widgets[] = {
 
{   WWT_CLOSEBOX, RESIZE_NONE,   14,   0,  10,   0,  13, STR_00C5,             STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION, RESIZE_NONE,   14,  11, 329,   0,  13, STR_8802_DETAILS,     STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN, RESIZE_NONE,   14, 330, 369,   0,  13, STR_01AA_NAME,        STR_8867_NAME_TRAIN},
 
{      WWT_PANEL, RESIZE_NONE,   14,   0, 369,  14,  55, 0x0,                  STR_NULL},
 
{     WWT_MATRIX, RESIZE_BOTTOM, 14,   0, 357,  56, 139, 0x601,                STR_NULL},
 
{  WWT_SCROLLBAR, RESIZE_BOTTOM, 14, 358, 369,  56, 139, 0x0,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHTXTBTN, RESIZE_TB,     14,   0,  10, 140, 145, STR_0188,             STR_884D_INCREASE_SERVICING_INTERVAL},
 
{ WWT_PUSHTXTBTN, RESIZE_TB,     14,   0,  10, 146, 151, STR_0189,             STR_884E_DECREASE_SERVICING_INTERVAL},
 
{      WWT_PANEL, RESIZE_TB,     14,  11, 369, 140, 151, 0x0,                  STR_NULL},
 
{ WWT_PUSHTXTBTN, RESIZE_TB,     14,   0,  89, 152, 163, STR_013C_CARGO,       STR_884F_SHOW_DETAILS_OF_CARGO_CARRIED},
 
{ WWT_PUSHTXTBTN, RESIZE_TB,     14,  90, 178, 152, 163, STR_013D_INFORMATION, STR_8850_SHOW_DETAILS_OF_TRAIN_VEHICLES},
 
{ WWT_PUSHTXTBTN, RESIZE_TB,     14, 179, 268, 152, 163, STR_013E_CAPACITIES,  STR_8851_SHOW_CAPACITIES_OF_EACH},
 
{ WWT_PUSHTXTBTN, RESIZE_TB,     14, 269, 357, 152, 163, STR_013E_TOTAL_CARGO, STR_8852_SHOW_TOTAL_CARGO},
 
{  WWT_RESIZEBOX, RESIZE_TB,     14, 358, 369, 152, 163, 0x0,                  STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 

	
 
static const WindowDesc _train_details_desc = {
 
	WDP_AUTO, WDP_AUTO, 370, 164,
 
	WC_VEHICLE_DETAILS,WC_VEHICLE_VIEW,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_train_details_widgets,
 
	TrainDetailsWndProc
 
};
 

	
 

	
 
static void ShowTrainDetailsWindow(const Vehicle *v)
 
{
 
	Window *w;
 
	VehicleID veh = v->index;
 

	
 
	DeleteWindowById(WC_VEHICLE_ORDERS, veh);
 
	DeleteWindowById(WC_VEHICLE_DETAILS, veh);
 

	
 
	w = AllocateWindowDescFront(&_train_details_desc, veh);
 

	
 
	w->caption_color = v->owner;
 
	w->vscroll.cap = 6;
 
	w->widget[4].data = (w->vscroll.cap << 8) + 1;
 

	
 
	w->resize.step_height = 14;
 
	w->resize.height = w->height - 14 * 2; /* Minimum of 4 wagons in the display */
 

	
 
	WP(w,traindetails_d).tab = 0;
 
}
src/tree_cmd.c
Show inline comments
 
deleted file
src/tree_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "clear_map.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "table/tree_land.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "tree_map.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "town.h"
 
#include "sound.h"
 
#include "variables.h"
 
#include "genworld.h"
 

	
 
enum TreePlacer {
 
	TP_NONE,
 
	TP_ORIGINAL,
 
	TP_IMPROVED,
 
};
 

	
 
static TreeType GetRandomTreeType(TileIndex tile, uint seed)
 
{
 
	switch (_opt.landscape) {
 
		case LT_NORMAL:
 
			return seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE;
 

	
 
		case LT_HILLY:
 
			return seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC;
 

	
 
		case LT_DESERT:
 
			switch (GetTropicZone(tile)) {
 
				case TROPICZONE_INVALID: return seed * TREE_COUNT_SUB_TROPICAL / 256 + TREE_SUB_TROPICAL;
 
				case TROPICZONE_DESERT:  return (seed > 12) ? TREE_INVALID : TREE_CACTUS;
 
				default:                 return seed * TREE_COUNT_RAINFOREST / 256 + TREE_RAINFOREST;
 
			}
 

	
 
		default:
 
			return seed * TREE_COUNT_TOYLAND / 256 + TREE_TOYLAND;
 
	}
 
}
 

	
 
static void PlaceTree(TileIndex tile, uint32 r)
 
{
 
	TreeType tree = GetRandomTreeType(tile, GB(r, 24, 8));
 

	
 
	if (tree != TREE_INVALID) {
 
		MakeTree(tile, tree, GB(r, 22, 2), min(GB(r, 16, 3), 6), TREE_GROUND_GRASS, 0);
 

	
 
		// above snowline?
 
		if (_opt.landscape == LT_HILLY && GetTileZ(tile) > _opt.snow_line) {
 
			SetTreeGroundDensity(tile, TREE_GROUND_SNOW_DESERT, 3);
 
			SetTreeCounter(tile, GB(r, 24, 3));
 
		} else {
 
			SetTreeGroundDensity(tile, GB(r, 28, 1), 0);
 
			SetTreeCounter(tile, GB(r, 24, 4));
 
		}
 
	}
 
}
 

	
 
static void DoPlaceMoreTrees(TileIndex tile)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < 1000; i++) {
 
		uint32 r = Random();
 
		int x = GB(r, 0, 5) - 16;
 
		int y = GB(r, 8, 5) - 16;
 
		uint dist = myabs(x) + myabs(y);
 
		TileIndex cur_tile = TILE_MASK(tile + TileDiffXY(x, y));
 

	
 
		if (dist <= 13 &&
 
				IsTileType(cur_tile, MP_CLEAR) &&
 
				!IsBridgeAbove(cur_tile) &&
 
				!IsClearGround(cur_tile, CLEAR_FIELDS) &&
 
				!IsClearGround(cur_tile, CLEAR_ROCKS)) {
 
			PlaceTree(cur_tile, r);
 
		}
 
	}
 
}
 

	
 
static void PlaceMoreTrees(void)
 
{
 
	uint i = ScaleByMapSize(GB(Random(), 0, 5) + 25);
 
	do {
 
		DoPlaceMoreTrees(RandomTile());
 
	} while (--i);
 
}
 

	
 
/**
 
 * Place a tree at the same height as an existing tree.
 
 *  This gives cool effects to the map.
 
 */
 
void PlaceTreeAtSameHeight(TileIndex tile, uint height)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < 1000; i++) {
 
		uint32 r = Random();
 
		int x = GB(r, 0, 5) - 16;
 
		int y = GB(r, 8, 5) - 16;
 
		TileIndex cur_tile = TILE_MASK(tile + TileDiffXY(x, y));
 

	
 
		/* Keep in range of the existing tree */
 
		if (myabs(x) + myabs(y) > 16) continue;
 

	
 
		/* Clear tile, no farm-tiles or rocks */
 
		if (!IsTileType(cur_tile, MP_CLEAR) ||
 
				IsClearGround(cur_tile, CLEAR_FIELDS) ||
 
				IsClearGround(cur_tile, CLEAR_ROCKS))
 
			continue;
 

	
 
		/* Not too much height difference */
 
		if (myabs(GetTileZ(cur_tile) - height) > 2) continue;
 

	
 
		/* Place one tree and quit */
 
		PlaceTree(cur_tile, r);
 
		break;
 
	}
 
}
 

	
 
void PlaceTreesRandomly(void)
 
{
 
	uint i, j, ht;
 

	
 
	i = ScaleByMapSize(1000);
 
	do {
 
		uint32 r = Random();
 
		TileIndex tile = RandomTileSeed(r);
 

	
 
		IncreaseGeneratingWorldProgress(GWP_TREE);
 

	
 
		if (IsTileType(tile, MP_CLEAR) &&
 
				!IsBridgeAbove(tile) &&
 
				!IsClearGround(tile, CLEAR_FIELDS) &&
 
				!IsClearGround(tile, CLEAR_ROCKS)) {
 
			PlaceTree(tile, r);
 
			if (_patches.tree_placer != TP_IMPROVED) continue;
 

	
 
			/* Place a number of trees based on the tile height.
 
			 *  This gives a cool effect of multiple trees close together.
 
			 *  It is almost real life ;) */
 
			ht = GetTileZ(tile);
 
			/* The higher we get, the more trees we plant */
 
			j = GetTileZ(tile) / TILE_HEIGHT * 2;
 
			while (j--) {
 
				/* Above snowline more trees! */
 
				if (_opt.landscape == LT_HILLY && ht > _opt.snow_line) {
 
					PlaceTreeAtSameHeight(tile, ht);
 
					PlaceTreeAtSameHeight(tile, ht);
 
				};
 

	
 
				PlaceTreeAtSameHeight(tile, ht);
 
			}
 
		}
 
	} while (--i);
 

	
 
	/* place extra trees at rainforest area */
 
	if (_opt.landscape == LT_DESERT) {
 
		i = ScaleByMapSize(15000);
 

	
 
		do {
 
			uint32 r = Random();
 
			TileIndex tile = RandomTileSeed(r);
 

	
 
			IncreaseGeneratingWorldProgress(GWP_TREE);
 

	
 
			if (IsTileType(tile, MP_CLEAR) &&
 
					!IsBridgeAbove(tile) &&
 
					!IsClearGround(tile, CLEAR_FIELDS) &&
 
					GetTropicZone(tile) == TROPICZONE_RAINFOREST) {
 
				PlaceTree(tile, r);
 
			}
 
		} while (--i);
 
	}
 
}
 

	
 
void GenerateTrees(void)
 
{
 
	uint i, total;
 

	
 
	if (_patches.tree_placer == TP_NONE) return;
 

	
 
	if (_opt.landscape != LT_CANDY) PlaceMoreTrees();
 

	
 
	switch (_patches.tree_placer) {
 
		case TP_ORIGINAL: i = _opt.landscape == LT_HILLY ? 15 : 6; break;
 
		case TP_IMPROVED: i = _opt.landscape == LT_HILLY ?  4 : 2; break;
 
		default: NOT_REACHED(); return;
 
	}
 

	
 
	total = ScaleByMapSize(1000);
 
	if (_opt.landscape == LT_DESERT) total += ScaleByMapSize(15000);
 
	total *= i;
 
	SetGeneratingWorldProgress(GWP_TREE, total);
 

	
 
	for (; i != 0; i--) {
 
		PlaceTreesRandomly();
 
	}
 
}
 

	
 
/** Plant a tree.
 
 * @param tile start tile of area-drag of tree plantation
 
 * @param p1 tree type, -1 means random.
 
 * @param p2 end tile of area-drag
 
 */
 
int32 CmdPlantTree(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	StringID msg = INVALID_STRING_ID;
 
	int32 cost;
 
	int ex;
 
	int ey;
 
	int sx, sy, x, y;
 

	
 
	if (p2 >= MapSize()) return CMD_ERROR;
 
	/* Check the tree type. It can be random or some valid value within the current climate */
 
	if (p1 != (uint)-1 && p1 - _tree_base_by_landscape[_opt.landscape] >= _tree_count_by_landscape[_opt.landscape]) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_OTHER);
 

	
 
	// make sure sx,sy are smaller than ex,ey
 
	ex = TileX(tile);
 
	ey = TileY(tile);
 
	sx = TileX(p2);
 
	sy = TileY(p2);
 
	if (ex < sx) intswap(ex, sx);
 
	if (ey < sy) intswap(ey, sy);
 

	
 
	cost = 0; // total cost
 

	
 
	for (x = sx; x <= ex; x++) {
 
		for (y = sy; y <= ey; y++) {
 
			TileIndex tile = TileXY(x, y);
 

	
 
			if (!EnsureNoVehicle(tile)) continue;
 

	
 
			switch (GetTileType(tile)) {
 
				case MP_TREES:
 
					// no more space for trees?
 
					if (_game_mode != GM_EDITOR && GetTreeCount(tile) == 3) {
 
						msg = STR_2803_TREE_ALREADY_HERE;
 
						continue;
 
					}
 

	
 
					if (flags & DC_EXEC) {
 
						AddTreeCount(tile, 1);
 
						MarkTileDirtyByTile(tile);
 
					}
 
					// 2x as expensive to add more trees to an existing tile
 
					cost += _price.build_trees * 2;
 
					break;
 

	
 
				case MP_CLEAR:
 
					if (!IsTileOwner(tile, OWNER_NONE) ||
 
							IsBridgeAbove(tile)) {
 
						msg = STR_2804_SITE_UNSUITABLE;
 
						continue;
 
					}
 

	
 
					switch (GetClearGround(tile)) {
 
						case CLEAR_FIELDS: cost += _price.clear_3; break;
 
						case CLEAR_ROCKS:  cost += _price.clear_2; break;
 
						default: break;
 
					}
 

	
 
					if (flags & DC_EXEC) {
 
						TreeType treetype;
 
						uint growth;
 

	
 
						if (_game_mode != GM_EDITOR && IsValidPlayer(_current_player)) {
 
							Town *t = ClosestTownFromTile(tile, _patches.dist_local_authority);
 
							if (t != NULL)
 
								ChangeTownRating(t, RATING_TREE_UP_STEP, RATING_TREE_MAXIMUM);
 
						}
 

	
 
						treetype = p1;
 
						if (treetype == TREE_INVALID) {
 
							treetype = GetRandomTreeType(tile, GB(Random(), 24, 8));
 
							if (treetype == TREE_INVALID) treetype = TREE_CACTUS;
 
						}
 

	
 
						growth = _game_mode == GM_EDITOR ? 3 : 0;
 
						switch (GetClearGround(tile)) {
 
							case CLEAR_ROUGH: MakeTree(tile, treetype, 0, growth, TREE_GROUND_ROUGH, 0); break;
 
							case CLEAR_SNOW:  MakeTree(tile, treetype, 0, growth, TREE_GROUND_SNOW_DESERT, GetClearDensity(tile)); break;
 
							default:          MakeTree(tile, treetype, 0, growth, TREE_GROUND_GRASS, 0); break;
 
						}
 
						MarkTileDirtyByTile(tile);
 

	
 
						if (_game_mode == GM_EDITOR && IS_INT_INSIDE(treetype, TREE_RAINFOREST, TREE_CACTUS))
 
							SetTropicZone(tile, TROPICZONE_RAINFOREST);
 
					}
 
					cost += _price.build_trees;
 
					break;
 

	
 
				default:
 
					msg = STR_2804_SITE_UNSUITABLE;
 
					break;
 
			}
 
		}
 
	}
 

	
 
	if (cost == 0) {
 
		return_cmd_error(msg);
 
	} else {
 
		return cost;
 
	}
 
}
 

	
 
typedef struct TreeListEnt {
 
	uint32 image;
 
	byte x,y;
 
} TreeListEnt;
 

	
 
static void DrawTile_Trees(TileInfo *ti)
 
{
 
	const uint32 *s;
 
	const TreePos* d;
 
	byte z;
 

	
 
	switch (GetTreeGround(ti->tile)) {
 
		case TREE_GROUND_GRASS: DrawClearLandTile(ti, 3); break;
 
		case TREE_GROUND_ROUGH: DrawHillyLandTile(ti); break;
 
		default: DrawGroundSprite(_tree_sprites_1[GetTreeDensity(ti->tile)] + _tileh_to_sprite[ti->tileh]); break;
 
	}
 

	
 
	DrawClearLandFence(ti);
 

	
 
	z = ti->z;
 
	if (ti->tileh != SLOPE_FLAT) {
 
		z += 4;
 
		if (IsSteepSlope(ti->tileh)) z += 4;
 
	}
 

	
 
	{
 
		uint16 tmp = ti->x;
 
		uint index;
 

	
 
		tmp = ROR(tmp, 2);
 
		tmp -= ti->y;
 
		tmp = ROR(tmp, 3);
 
		tmp -= ti->x;
 
		tmp = ROR(tmp, 1);
 
		tmp += ti->y;
 

	
 
		d = _tree_layout_xy[GB(tmp, 4, 2)];
 

	
 
		index = GB(tmp, 6, 2) + (GetTreeType(ti->tile) << 2);
 

	
 
		/* different tree styles above one of the grounds */
 
		if (GetTreeGround(ti->tile) == TREE_GROUND_SNOW_DESERT &&
 
				GetTreeDensity(ti->tile) >= 2 &&
 
				IS_INT_INSIDE(index, TREE_SUB_ARCTIC << 2, TREE_RAINFOREST << 2)) {
 
			index += 164 - (TREE_SUB_ARCTIC << 2);
 
		}
 

	
 
		assert(index < lengthof(_tree_layout_sprite));
 
		s = _tree_layout_sprite[index];
 
	}
 

	
 
	StartSpriteCombine();
 

	
 
	if (!(_display_opt & DO_TRANS_BUILDINGS) || !_patches.invisible_trees) {
 
		TreeListEnt te[4];
 
		uint i;
 

	
 
		/* put the trees to draw in a list */
 
		i = GetTreeCount(ti->tile) + 1;
 
		do {
 
			uint32 image = s[0] + (--i == 0 ? GetTreeGrowth(ti->tile) : 3);
 
			if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 
			te[i].image = image;
 
			te[i].x = d->x;
 
			te[i].y = d->y;
 
			s++;
 
			d++;
 
		} while (i);
 

	
 
		/* draw them in a sorted way */
 
		for (;;) {
 
			byte min = 0xFF;
 
			TreeListEnt *tep = NULL;
 

	
 
			i = GetTreeCount(ti->tile) + 1;
 
			do {
 
				if (te[--i].image != 0 && te[i].x + te[i].y < min) {
 
					min = te[i].x + te[i].y;
 
					tep = &te[i];
 
				}
 
			} while (i);
 

	
 
			if (tep == NULL) break;
 

	
 
			AddSortableSpriteToDraw(tep->image, ti->x + tep->x, ti->y + tep->y, 5, 5, 0x10, z);
 
			tep->image = 0;
 
		}
 
	}
 

	
 
	EndSpriteCombine();
 
}
 

	
 

	
 
static uint GetSlopeZ_Trees(TileIndex tile, uint x, uint y)
 
{
 
	uint z;
 
	uint tileh = GetTileSlope(tile, &z);
 

	
 
	return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
 
}
 

	
 
static Slope GetSlopeTileh_Trees(TileIndex tile, Slope tileh)
 
{
 
	return tileh;
 
}
 

	
 
static int32 ClearTile_Trees(TileIndex tile, byte flags)
 
{
 
	uint num;
 

	
 
	if ((flags & DC_EXEC) && IsValidPlayer(_current_player)) {
 
		Town *t = ClosestTownFromTile(tile, _patches.dist_local_authority);
 
		if (t != NULL)
 
			ChangeTownRating(t, RATING_TREE_DOWN_STEP, RATING_TREE_MINIMUM);
 
	}
 

	
 
	num = GetTreeCount(tile) + 1;
 
	if (IS_INT_INSIDE(GetTreeType(tile), TREE_RAINFOREST, TREE_CACTUS)) num *= 4;
 

	
 
	if (flags & DC_EXEC) DoClearSquare(tile);
 

	
 
	return num * _price.remove_trees;
 
}
 

	
 
static void GetAcceptedCargo_Trees(TileIndex tile, AcceptedCargo ac)
 
{
 
	/* not used */
 
}
 

	
 
static void GetTileDesc_Trees(TileIndex tile, TileDesc *td)
 
{
 
	TreeType tt = GetTreeType(tile);
 

	
 
	if (IS_INT_INSIDE(tt, TREE_RAINFOREST, TREE_CACTUS)) {
 
		td->str = STR_280F_RAINFOREST;
 
	} else if (tt == TREE_CACTUS) {
 
		td->str = STR_2810_CACTUS_PLANTS;
 
	} else {
 
		td->str = STR_280E_TREES;
 
	}
 

	
 
	td->owner = GetTileOwner(tile);
 
}
 

	
 
static void AnimateTile_Trees(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static void TileLoopTreesDesert(TileIndex tile)
 
{
 
	switch (GetTropicZone(tile)) {
 
		case TROPICZONE_DESERT:
 
			if (GetTreeGround(tile) != TREE_GROUND_SNOW_DESERT) {
 
				SetTreeGroundDensity(tile, TREE_GROUND_SNOW_DESERT, 3);
 
				MarkTileDirtyByTile(tile);
 
			}
 
			break;
 

	
 
		case TROPICZONE_RAINFOREST: {
 
			static const SoundFx forest_sounds[] = {
 
				SND_42_LOON_BIRD,
 
				SND_43_LION,
 
				SND_44_MONKEYS,
 
				SND_48_DISTANT_BIRD
 
			};
 
			uint32 r = Random();
 

	
 
			if (CHANCE16I(1, 200, r)) SndPlayTileFx(forest_sounds[GB(r, 16, 2)], tile);
 
			break;
 
		}
 

	
 
		default: break;
 
	}
 
}
 

	
 
static void TileLoopTreesAlps(TileIndex tile)
 
{
 
	int k = GetTileZ(tile) - _opt.snow_line + TILE_HEIGHT;
 

	
 
	if (k < 0) {
 
		if (GetTreeGround(tile) != TREE_GROUND_SNOW_DESERT) return;
 
		SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 0);
 
	} else {
 
		uint density = min((uint)k / TILE_HEIGHT, 3);
 

	
 
		if (GetTreeGround(tile) != TREE_GROUND_SNOW_DESERT ||
 
				GetTreeDensity(tile) != density) {
 
			SetTreeGroundDensity(tile, TREE_GROUND_SNOW_DESERT, density);
 
		} else {
 
			if (GetTreeDensity(tile) == 3) {
 
				uint32 r = Random();
 
				if (CHANCE16I(1, 200, r)) {
 
					SndPlayTileFx((r & 0x80000000) ? SND_39_HEAVY_WIND : SND_34_WIND, tile);
 
				}
 
			}
 
			return;
 
		}
 
	}
 
	MarkTileDirtyByTile(tile);
 
}
 

	
 
static void TileLoop_Trees(TileIndex tile)
 
{
 
	switch (_opt.landscape) {
 
		case LT_DESERT: TileLoopTreesDesert(tile); break;
 
		case LT_HILLY:  TileLoopTreesAlps(tile);   break;
 
	}
 

	
 
	TileLoopClearHelper(tile);
 

	
 
	if (GetTreeCounter(tile) < 15) {
 
		AddTreeCounter(tile, 1);
 
		return;
 
	}
 
	SetTreeCounter(tile, 0);
 

	
 
	switch (GetTreeGrowth(tile)) {
 
		case 3: /* regular sized tree */
 
			if (_opt.landscape == LT_DESERT &&
 
					GetTreeType(tile) != TREE_CACTUS &&
 
					GetTropicZone(tile) == TROPICZONE_DESERT) {
 
				AddTreeGrowth(tile, 1);
 
			} else {
 
				switch (GB(Random(), 0, 3)) {
 
					case 0: /* start destructing */
 
						AddTreeGrowth(tile, 1);
 
						break;
 

	
 
					case 1: /* add a tree */
 
						if (GetTreeCount(tile) < 3) {
 
							AddTreeCount(tile, 1);
 
							SetTreeGrowth(tile, 0);
 
							break;
 
						}
 
						/* FALL THROUGH */
 

	
 
					case 2: { /* add a neighbouring tree */
 
						TreeType treetype = GetTreeType(tile);
 

	
 
						tile += TileOffsByDir(Random() & 7);
 

	
 
						if (!IsTileType(tile, MP_CLEAR) || IsBridgeAbove(tile)) return;
 

	
 
						switch (GetClearGround(tile)) {
 
							case CLEAR_GRASS:
 
								if (GetClearDensity(tile) != 3) return;
 
								MakeTree(tile, treetype, 0, 0, TREE_GROUND_GRASS, 0);
 
								break;
 

	
 
							case CLEAR_ROUGH: MakeTree(tile, treetype, 0, 0, TREE_GROUND_ROUGH, 0); break;
 
							case CLEAR_SNOW:  MakeTree(tile, treetype, 0, 0, TREE_GROUND_SNOW_DESERT, GetClearDensity(tile)); break;
 
							default: return;
 
						}
 
						break;
 
					}
 

	
 
					default:
 
						return;
 
				}
 
			}
 
			break;
 

	
 
		case 6: /* final stage of tree destruction */
 
			if (GetTreeCount(tile) > 0) {
 
				/* more than one tree, delete it */
 
				AddTreeCount(tile, -1);
 
				SetTreeGrowth(tile, 3);
 
			} else {
 
				/* just one tree, change type into MP_CLEAR */
 
				switch (GetTreeGround(tile)) {
 
					case TREE_GROUND_GRASS: MakeClear(tile, CLEAR_GRASS, 3); break;
 
					case TREE_GROUND_ROUGH: MakeClear(tile, CLEAR_ROUGH, 3); break;
 
					default: MakeClear(tile, CLEAR_SNOW, GetTreeDensity(tile)); break;
 
				}
 
			}
 
			break;
 

	
 
		default:
 
			AddTreeGrowth(tile, 1);
 
			break;
 
	}
 

	
 
	MarkTileDirtyByTile(tile);
 
}
 

	
 
void OnTick_Trees(void)
 
{
 
	uint32 r;
 
	TileIndex tile;
 
	ClearGround ct;
 
	TreeType tree;
 

	
 
	/* place a tree at a random rainforest spot */
 
	if (_opt.landscape == LT_DESERT &&
 
			(r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) &&
 
			IsTileType(tile, MP_CLEAR) &&
 
			!IsBridgeAbove(tile) &&
 
			(ct = GetClearGround(tile), ct == CLEAR_GRASS || ct == CLEAR_ROUGH) &&
 
			(tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) {
 
		MakeTree(tile, tree, 0, 0, ct == CLEAR_ROUGH ? TREE_GROUND_ROUGH : TREE_GROUND_GRASS, 0);
 
	}
 

	
 
	// byte underflow
 
	if (--_trees_tick_ctr != 0) return;
 

	
 
	/* place a tree at a random spot */
 
	r = Random();
 
	tile = TILE_MASK(r);
 
	if (IsTileType(tile, MP_CLEAR) &&
 
			!IsBridgeAbove(tile) &&
 
			(ct = GetClearGround(tile), ct == CLEAR_GRASS || ct == CLEAR_ROUGH || ct == CLEAR_SNOW) &&
 
			(tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) {
 
		switch (ct) {
 
			case CLEAR_GRASS: MakeTree(tile, tree, 0, 0, TREE_GROUND_GRASS, 0); break;
 
			case CLEAR_ROUGH: MakeTree(tile, tree, 0, 0, TREE_GROUND_ROUGH, 0); break;
 
			default: MakeTree(tile, tree, 0, 0, TREE_GROUND_SNOW_DESERT, GetClearDensity(tile)); break;
 
		}
 
	}
 
}
 

	
 
static void ClickTile_Trees(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static uint32 GetTileTrackStatus_Trees(TileIndex tile, TransportType mode)
 
{
 
	return 0;
 
}
 

	
 
static void ChangeTileOwner_Trees(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	/* not used */
 
}
 

	
 
void InitializeTrees(void)
 
{
 
	_trees_tick_ctr = 0;
 
}
 

	
 

	
 
const TileTypeProcs _tile_type_trees_procs = {
 
	DrawTile_Trees,           /* draw_tile_proc */
 
	GetSlopeZ_Trees,          /* get_slope_z_proc */
 
	ClearTile_Trees,          /* clear_tile_proc */
 
	GetAcceptedCargo_Trees,   /* get_accepted_cargo_proc */
 
	GetTileDesc_Trees,        /* get_tile_desc_proc */
 
	GetTileTrackStatus_Trees, /* get_tile_track_status_proc */
 
	ClickTile_Trees,          /* click_tile_proc */
 
	AnimateTile_Trees,        /* animate_tile_proc */
 
	TileLoop_Trees,           /* tile_loop_clear */
 
	ChangeTileOwner_Trees,    /* change_tile_owner_clear */
 
	NULL,                     /* get_produced_cargo_proc */
 
	NULL,                     /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_Trees,      /* get_slope_tileh_proc */
 
};
src/tunnel_map.c
Show inline comments
 
deleted file
src/tunnel_map.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "tile.h"
 
#include "tunnel_map.h"
 

	
 
TileIndex GetOtherTunnelEnd(TileIndex tile)
 
{
 
	DiagDirection dir = GetTunnelDirection(tile);
 
	TileIndexDiff delta = TileOffsByDiagDir(dir);
 
	uint z = GetTileZ(tile);
 

	
 
	dir = ReverseDiagDir(dir);
 
	do {
 
		tile += delta;
 
	} while (
 
		!IsTunnelTile(tile) ||
 
		GetTunnelDirection(tile) != dir ||
 
		GetTileZ(tile) != z
 
	);
 

	
 
	return tile;
 
}
 

	
 

	
 
static bool IsTunnelInWayDir(TileIndex tile, uint z, DiagDirection dir)
 
{
 
	TileIndexDiff delta = TileOffsByDiagDir(dir);
 
	uint height;
 

	
 
	do {
 
		tile -= delta;
 
		height = GetTileZ(tile);
 
	} while (z < height);
 

	
 
	return
 
		z == height &&
 
		IsTunnelTile(tile) &&
 
		GetTunnelDirection(tile) == dir;
 
}
 

	
 
bool IsTunnelInWay(TileIndex tile, uint z)
 
{
 
	return
 
		IsTunnelInWayDir(tile, z, DIAGDIR_NE) ||
 
		IsTunnelInWayDir(tile, z, DIAGDIR_SE) ||
 
		IsTunnelInWayDir(tile, z, DIAGDIR_SW) ||
 
		IsTunnelInWayDir(tile, z, DIAGDIR_NW);
 
}
src/tunnelbridge_cmd.c
Show inline comments
 
deleted file
src/tunnelbridge_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
/** @file tunnelbridge_cmd.c
 
 * This file deals with tunnels and bridges (non-gui stuff)
 
 * @todo seperate this file into two
 
 */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "rail_map.h"
 
#include "road_map.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "tunnel_map.h"
 
#include "unmovable_map.h"
 
#include "vehicle.h"
 
#include "viewport.h"
 
#include "command.h"
 
#include "player.h"
 
#include "town.h"
 
#include "sound.h"
 
#include "variables.h"
 
#include "bridge.h"
 
#include "train.h"
 
#include "water_map.h"
 
#include "yapf/yapf.h"
 
#include "date.h"
 
#include "newgrf_sound.h"
 

	
 
#include "table/bridge_land.h"
 

	
 
const Bridge orig_bridge[] = {
 
/*
 
	     year of availablity
 
	     |  minimum length
 
	     |  |   maximum length
 
	     |  |   |    price
 
	     |  |   |    |    maximum speed
 
	     |  |   |    |    |  sprite to use in GUI                string with description
 
	     |  |   |    |    |  |                                   |                            */
 
	{    0, 0, 16,  80,  32, 0xA24                             , STR_5012_WOODEN             , NULL, 0 },
 
	{    0, 0,  2, 112,  48, 0xA26 | PALETTE_TO_STRUCT_RED     , STR_5013_CONCRETE           , NULL, 0 },
 
	{ 1930, 0,  5, 144,  64, 0xA25                             , STR_500F_GIRDER_STEEL       , NULL, 0 },
 
	{    0, 2, 10, 168,  80, 0xA22 | PALETTE_TO_STRUCT_CONCRETE, STR_5011_SUSPENSION_CONCRETE, NULL, 0 },
 
	{ 1930, 3, 16, 185,  96, 0xA22                             , STR_500E_SUSPENSION_STEEL   , NULL, 0 },
 
	{ 1930, 3, 16, 192, 112, 0xA22 | PALETTE_TO_STRUCT_YELLOW  , STR_500E_SUSPENSION_STEEL   , NULL, 0 },
 
	{ 1930, 3,  7, 224, 160, 0xA23                             , STR_5010_CANTILEVER_STEEL   , NULL, 0 },
 
	{ 1930, 3,  8, 232, 208, 0xA23 | PALETTE_TO_STRUCT_BROWN   , STR_5010_CANTILEVER_STEEL   , NULL, 0 },
 
	{ 1930, 3,  9, 248, 240, 0xA23 | PALETTE_TO_STRUCT_RED     , STR_5010_CANTILEVER_STEEL   , NULL, 0 },
 
	{ 1930, 0,  2, 240, 256, 0xA27                             , STR_500F_GIRDER_STEEL       , NULL, 0 },
 
	{ 1995, 2, 16, 255, 320, 0xA28                             , STR_5014_TUBULAR_STEEL      , NULL, 0 },
 
	{ 2005, 2, 32, 380, 512, 0xA28 | PALETTE_TO_STRUCT_YELLOW  , STR_5014_TUBULAR_STEEL      , NULL, 0 },
 
	{ 2010, 2, 32, 510, 608, 0xA28 | PALETTE_TO_STRUCT_GREY    , STR_BRIDGE_TUBULAR_SILICON  , NULL, 0 }
 
};
 

	
 
Bridge _bridge[MAX_BRIDGES];
 

	
 

	
 
// calculate the price factor for building a long bridge.
 
// basically the cost delta is 1,1, 1, 2,2, 3,3,3, 4,4,4,4, 5,5,5,5,5, 6,6,6,6,6,6,  7,7,7,7,7,7,7,  8,8,8,8,8,8,8,8,
 
int CalcBridgeLenCostFactor(int x)
 
{
 
	int n;
 
	int r;
 

	
 
	if (x < 2) return x;
 
	x -= 2;
 
	for (n = 0, r = 2;; n++) {
 
		if (x <= n) return r + x * n;
 
		r += n * n;
 
		x -= n;
 
	}
 
}
 

	
 
#define M(x) (1 << (x))
 
enum {
 
	// foundation, whole tile is leveled up --> 3 corners raised
 
	BRIDGE_FULL_LEVELED_FOUNDATION = M(SLOPE_WSE) | M(SLOPE_NWS) | M(SLOPE_ENW) | M(SLOPE_SEN),
 
	// foundation, tile is partly leveled up --> 1 corner raised
 
	BRIDGE_PARTLY_LEVELED_FOUNDATION = M(SLOPE_W) | M(SLOPE_S) | M(SLOPE_E) | M(SLOPE_N),
 
	// no foundations (X,Y direction)
 
	BRIDGE_NO_FOUNDATION = M(SLOPE_FLAT) | M(SLOPE_SW) | M(SLOPE_SE) | M(SLOPE_NW) | M(SLOPE_NE),
 
	BRIDGE_HORZ_RAMP = (BRIDGE_PARTLY_LEVELED_FOUNDATION | BRIDGE_NO_FOUNDATION) & ~M(SLOPE_FLAT)
 
};
 
#undef M
 

	
 
static inline const PalSpriteID *GetBridgeSpriteTable(int index, byte table)
 
{
 
	const Bridge *bridge = &_bridge[index];
 
	assert(table < 7);
 
	if (bridge->sprite_table == NULL || bridge->sprite_table[table] == NULL) {
 
		return _bridge_sprite_table[index][table];
 
	} else {
 
		return bridge->sprite_table[table];
 
	}
 
}
 

	
 
static inline byte GetBridgeFlags(int index) { return _bridge[index].flags;}
 

	
 

	
 
/** Check the slope at the bridge ramps in three easy steps:
 
 * - valid slopes without foundation
 
 * - valid slopes with foundation
 
 * - rest is invalid
 
 */
 
#define M(x) (1 << (x))
 
static int32 CheckBridgeSlopeNorth(Axis axis, Slope tileh)
 
{
 
	uint32 valid;
 

	
 
	valid = M(SLOPE_FLAT) | (axis == AXIS_X ? M(SLOPE_NE) : M(SLOPE_NW));
 
	if (HASBIT(valid, tileh)) return 0;
 

	
 
	valid =
 
		BRIDGE_FULL_LEVELED_FOUNDATION | M(SLOPE_N) | M(SLOPE_STEEP_N) |
 
		(axis == AXIS_X ? M(SLOPE_E) | M(SLOPE_STEEP_E) : M(SLOPE_W) | M(SLOPE_STEEP_W));
 
	if (HASBIT(valid, tileh)) return _price.terraform;
 

	
 
	return CMD_ERROR;
 
}
 

	
 
static int32 CheckBridgeSlopeSouth(Axis axis, Slope tileh)
 
{
 
	uint32 valid;
 

	
 
	valid = M(SLOPE_FLAT) | (axis == AXIS_X ? M(SLOPE_SW) : M(SLOPE_SE));
 
	if (HASBIT(valid, tileh)) return 0;
 

	
 
	valid =
 
		BRIDGE_FULL_LEVELED_FOUNDATION | M(SLOPE_S) | M(SLOPE_STEEP_S) |
 
		(axis == AXIS_X ? M(SLOPE_W) | M(SLOPE_STEEP_W) : M(SLOPE_E) | M(SLOPE_STEEP_E));
 
	if (HASBIT(valid, tileh)) return _price.terraform;
 

	
 
	return CMD_ERROR;
 
}
 
#undef M
 

	
 

	
 
uint32 GetBridgeLength(TileIndex begin, TileIndex end)
 
{
 
	int x1 = TileX(begin);
 
	int y1 = TileY(begin);
 
	int x2 = TileX(end);
 
	int y2 = TileY(end);
 

	
 
	return abs(x2 + y2 - x1 - y1) - 1;
 
}
 

	
 
bool CheckBridge_Stuff(byte bridge_type, uint bridge_len)
 
{
 
	const Bridge *b = &_bridge[bridge_type];
 
	uint max; // max possible length of a bridge (with patch 100)
 

	
 
	if (bridge_type >= MAX_BRIDGES) return false;
 
	if (b->avail_year > _cur_year) return false;
 

	
 
	max = b->max_length;
 
	if (max >= 16 && _patches.longbridges) max = 100;
 

	
 
	return b->min_length <= bridge_len && bridge_len <= max;
 
}
 

	
 
/** Build a Bridge
 
 * @param end_tile end tile
 
 * @param p1 packed start tile coords (~ dx)
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit 0- 7) - bridge type (hi bh)
 
 * - p2 = (bit 8-..) - rail type. bit15 ((x>>8)&0x80) means road bridge.
 
 */
 
int32 CmdBuildBridge(TileIndex end_tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int bridge_type;
 
	TransportType transport;
 
	RailType railtype;
 
	uint x;
 
	uint y;
 
	uint sx;
 
	uint sy;
 
	TileIndex tile_start;
 
	TileIndex tile_end;
 
	Slope tileh_start;
 
	Slope tileh_end;
 
	uint z_start;
 
	uint z_end;
 
	TileIndex tile;
 
	TileIndexDiff delta;
 
	uint bridge_len;
 
	Axis direction;
 
	int32 cost, terraformcost, ret;
 
	bool allow_on_slopes;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* unpack parameters */
 
	bridge_type = GB(p2, 0, 8);
 

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

	
 
	// type of bridge
 
	if (HASBIT(p2, 15)) {
 
		railtype = 0;
 
		transport = TRANSPORT_ROAD;
 
	} else {
 
		if (!ValParamRailtype(GB(p2, 8, 8))) return CMD_ERROR;
 
		railtype = GB(p2, 8, 8);
 
		transport = TRANSPORT_RAIL;
 
	}
 

	
 
	x = TileX(end_tile);
 
	y = TileY(end_tile);
 
	sx = TileX(p1);
 
	sy = TileY(p1);
 

	
 
	/* check if valid, and make sure that (x,y) are smaller than (sx,sy) */
 
	if (x == sx) {
 
		if (y == sy) return_cmd_error(STR_5008_CANNOT_START_AND_END_ON);
 
		direction = AXIS_Y;
 
		if (y > sy) uintswap(y,sy);
 
	} else if (y == sy) {
 
		direction = AXIS_X;
 
		if (x > sx) uintswap(x,sx);
 
	} else {
 
		return_cmd_error(STR_500A_START_AND_END_MUST_BE_IN);
 
	}
 

	
 
	/* set and test bridge length, availability */
 
	bridge_len = sx + sy - x - y - 1;
 
	if (!CheckBridge_Stuff(bridge_type, bridge_len)) return_cmd_error(STR_5015_CAN_T_BUILD_BRIDGE_HERE);
 

	
 
	/* retrieve landscape height and ensure it's on land */
 
	tile_start = TileXY(x, y);
 
	tile_end = TileXY(sx, sy);
 
	if (IsClearWaterTile(tile_start) || IsClearWaterTile(tile_end)) {
 
		return_cmd_error(STR_02A0_ENDS_OF_BRIDGE_MUST_BOTH);
 
	}
 

	
 
	tileh_start = GetTileSlope(tile_start, &z_start);
 
	tileh_end = GetTileSlope(tile_end, &z_end);
 

	
 
	if (IsSteepSlope(tileh_start)) z_start += TILE_HEIGHT;
 
	if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh_start)) {
 
		z_start += TILE_HEIGHT;
 
		tileh_start = SLOPE_FLAT;
 
	}
 

	
 
	if (IsSteepSlope(tileh_end)) z_end += TILE_HEIGHT;
 
	if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh_end)) {
 
		z_end += TILE_HEIGHT;
 
		tileh_end = SLOPE_FLAT;
 
	}
 

	
 
	if (z_start != z_end) return_cmd_error(STR_5009_LEVEL_LAND_OR_WATER_REQUIRED);
 

	
 
	// Towns are not allowed to use bridges on slopes.
 
	allow_on_slopes = (!_is_old_ai_player
 
	                   && _current_player != OWNER_TOWN && _patches.build_on_slopes);
 

	
 
	/* Try and clear the start landscape */
 

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

	
 
	terraformcost = CheckBridgeSlopeNorth(direction, tileh_start);
 
	if (CmdFailed(terraformcost) || (terraformcost != 0 && !allow_on_slopes))
 
		return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 
	cost += terraformcost;
 

	
 
	/* Try and clear the end landscape */
 

	
 
	ret = DoCommand(tile_end, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(ret)) return ret;
 
	cost += ret;
 

	
 
	// false - end tile slope check
 
	terraformcost = CheckBridgeSlopeSouth(direction, tileh_end);
 
	if (CmdFailed(terraformcost) || (terraformcost != 0 && !allow_on_slopes))
 
		return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 
	cost += terraformcost;
 

	
 
	{
 
		TileIndex Heads[] = {tile_start, tile_end};
 
		int i;
 

	
 
		for (i = 0; i < 2; i++) {
 
			if (MayHaveBridgeAbove(Heads[i])) {
 
				if (IsBridgeAbove(Heads[i])) {
 
					TileIndex north_head = GetNorthernBridgeEnd(Heads[i]);
 

	
 
					if (direction == GetBridgeAxis(Heads[i])) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 

	
 
					if (z_start + TILE_HEIGHT == GetBridgeHeight(north_head)) {
 
						return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 
					}
 
				}
 
			}
 
		}
 
	}
 

	
 
	/* do the drill? */
 
	if (flags & DC_EXEC) {
 
		DiagDirection dir = AxisToDiagDir(direction);
 

	
 
		if (transport == TRANSPORT_RAIL) {
 
			MakeRailBridgeRamp(tile_start, _current_player, bridge_type, dir, railtype);
 
			MakeRailBridgeRamp(tile_end,   _current_player, bridge_type, ReverseDiagDir(dir), railtype);
 
		} else {
 
			MakeRoadBridgeRamp(tile_start, _current_player, bridge_type, dir);
 
			MakeRoadBridgeRamp(tile_end,   _current_player, bridge_type, ReverseDiagDir(dir));
 
		}
 
		MarkTileDirtyByTile(tile_start);
 
		MarkTileDirtyByTile(tile_end);
 
	}
 

	
 
	delta = (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
 
	for (tile = tile_start + delta; tile != tile_end; tile += delta) {
 
		uint z;
 

	
 
		if (GetTileSlope(tile, &z) != SLOPE_FLAT && z >= z_start) return_cmd_error(STR_5009_LEVEL_LAND_OR_WATER_REQUIRED);
 

	
 
		if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) {
 
			/* Disallow crossing bridges for the time being */
 
			return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 
		}
 

	
 
		switch (GetTileType(tile)) {
 
			case MP_WATER:
 
				if (!EnsureNoVehicle(tile)) return_cmd_error(STR_980E_SHIP_IN_THE_WAY);
 
				if (!IsWater(tile) && !IsCoast(tile)) goto not_valid_below;
 
				break;
 

	
 
			case MP_RAILWAY:
 
				if (!IsPlainRailTile(tile)) goto not_valid_below;
 
				break;
 

	
 
			case MP_STREET:
 
				if (GetRoadTileType(tile) == ROAD_TILE_DEPOT) goto not_valid_below;
 
				break;
 

	
 
			case MP_TUNNELBRIDGE:
 
				if (IsTunnel(tile)) break;
 
				if (direction == DiagDirToAxis(GetBridgeRampDirection(tile))) goto not_valid_below;
 
				if (z_start < GetBridgeHeight(tile)) goto not_valid_below;
 
				break;
 

	
 
			case MP_UNMOVABLE:
 
				if (!IsOwnedLand(tile)) goto not_valid_below;
 
				break;
 

	
 
			case MP_CLEAR:
 
				if (IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 
				break;
 

	
 
			default:
 
not_valid_below:;
 
				/* try and clear the middle landscape */
 
				ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
				if (CmdFailed(ret)) return ret;
 
				cost += ret;
 
				break;
 
		}
 

	
 
		if (flags & DC_EXEC) {
 
			SetBridgeMiddle(tile, direction);
 
			MarkTileDirtyByTile(tile);
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		Track track = AxisToTrack(direction);
 
		SetSignalsOnBothDir(tile_start, track);
 
		YapfNotifyTrackLayoutChange(tile_start, track);
 
	}
 

	
 
	/* for human player that builds the bridge he gets a selection to choose from bridges (DC_QUERY_COST)
 
	 * It's unnecessary to execute this command every time for every bridge. So it is done only
 
	 * and cost is computed in "bridge_gui.c". For AI, Towns this has to be of course calculated
 
	 */
 
	if (!(flags & DC_QUERY_COST)) {
 
		const Bridge *b = &_bridge[bridge_type];
 

	
 
		bridge_len += 2; // begin and end tiles/ramps
 

	
 
		if (IsValidPlayer(_current_player) && !_is_old_ai_player)
 
			bridge_len = CalcBridgeLenCostFactor(bridge_len);
 

	
 
		cost += (int64)bridge_len * _price.build_bridge * b->price >> 8;
 
	}
 

	
 
	return cost;
 
}
 

	
 

	
 
/** Build Tunnel.
 
 * @param tile start tile of tunnel
 
 * @param p1 railtype, 0x200 for road tunnel
 
 * @param p2 unused
 
 */
 
int32 CmdBuildTunnel(TileIndex start_tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	TileIndexDiff delta;
 
	TileIndex end_tile;
 
	DiagDirection direction;
 
	Slope start_tileh;
 
	Slope end_tileh;
 
	uint start_z;
 
	uint end_z;
 
	int32 cost;
 
	int32 ret;
 

	
 
	_build_tunnel_endtile = 0;
 

	
 
	if (p1 != 0x200 && !ValParamRailtype(p1)) return CMD_ERROR;
 

	
 
	start_tileh = GetTileSlope(start_tile, &start_z);
 

	
 
	switch (start_tileh) {
 
		case SLOPE_SW: direction = DIAGDIR_SW; break;
 
		case SLOPE_SE: direction = DIAGDIR_SE; break;
 
		case SLOPE_NW: direction = DIAGDIR_NW; break;
 
		case SLOPE_NE: direction = DIAGDIR_NE; break;
 
		default: return_cmd_error(STR_500B_SITE_UNSUITABLE_FOR_TUNNEL);
 
	}
 

	
 
	ret = DoCommand(start_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
	if (CmdFailed(ret)) return ret;
 

	
 
	/* XXX - do NOT change 'ret' in the loop, as it is used as the price
 
	 * for the clearing of the entrance of the tunnel. Assigning it to
 
	 * cost before the loop will yield different costs depending on start-
 
	 * position, because of increased-cost-by-length: 'cost += cost >> 3' */
 
	cost = 0;
 
	delta = TileOffsByDiagDir(direction);
 
	end_tile = start_tile;
 
	for (;;) {
 
		end_tile += delta;
 
		end_tileh = GetTileSlope(end_tile, &end_z);
 

	
 
		if (start_z == end_z) break;
 

	
 
		if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z)) {
 
			return_cmd_error(STR_5003_ANOTHER_TUNNEL_IN_THE_WAY);
 
		}
 

	
 
		cost += _price.build_tunnel;
 
		cost += cost >> 3; // add a multiplier for longer tunnels
 
		if (cost >= 400000000) cost = 400000000;
 
	}
 

	
 
	/* Add the cost of the entrance */
 
	cost += _price.build_tunnel + ret;
 

	
 
	// if the command fails from here on we want the end tile to be highlighted
 
	_build_tunnel_endtile = end_tile;
 

	
 
	// slope of end tile must be complementary to the slope of the start tile
 
	if (end_tileh != ComplementSlope(start_tileh)) {
 
		ret = DoCommand(end_tile, end_tileh & start_tileh, 0, flags, CMD_TERRAFORM_LAND);
 
		if (CmdFailed(ret)) return_cmd_error(STR_5005_UNABLE_TO_EXCAVATE_LAND);
 
	} else {
 
		ret = DoCommand(end_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
		if (CmdFailed(ret)) return ret;
 
	}
 
	cost += _price.build_tunnel + ret;
 

	
 
	if (flags & DC_EXEC) {
 
		if (GB(p1, 9, 1) == TRANSPORT_RAIL) {
 
			MakeRailTunnel(start_tile, _current_player, direction,                 GB(p1, 0, 4));
 
			MakeRailTunnel(end_tile,   _current_player, ReverseDiagDir(direction), GB(p1, 0, 4));
 
			UpdateSignalsOnSegment(start_tile, direction);
 
			YapfNotifyTrackLayoutChange(start_tile, AxisToTrack(DiagDirToAxis(direction)));
 
		} else {
 
			MakeRoadTunnel(start_tile, _current_player, direction);
 
			MakeRoadTunnel(end_tile,   _current_player, ReverseDiagDir(direction));
 
		}
 
	}
 

	
 
	return cost;
 
}
 

	
 
TileIndex CheckTunnelBusy(TileIndex tile, uint *length)
 
{
 
	uint z = GetTileZ(tile);
 
	DiagDirection dir = GetTunnelDirection(tile);
 
	TileIndexDiff delta = TileOffsByDiagDir(dir);
 
	uint len = 0;
 
	TileIndex starttile = tile;
 
	Vehicle *v;
 

	
 
	do {
 
		tile += delta;
 
		len++;
 
	} while (
 
		!IsTunnelTile(tile) ||
 
		ReverseDiagDir(GetTunnelDirection(tile)) != dir ||
 
		GetTileZ(tile) != z
 
	);
 

	
 
	v = FindVehicleBetween(starttile, tile, z);
 
	if (v != NULL) {
 
		_error_message = v->type == VEH_Train ?
 
			STR_5000_TRAIN_IN_TUNNEL : STR_5001_ROAD_VEHICLE_IN_TUNNEL;
 
		return INVALID_TILE;
 
	}
 

	
 
	if (length != NULL) *length = len;
 
	return tile;
 
}
 

	
 
static inline bool CheckAllowRemoveTunnelBridge(TileIndex tile)
 
{
 
	/* Floods can remove anything as well as the scenario editor */
 
	if (_current_player == OWNER_WATER || _game_mode == GM_EDITOR) return true;
 
	/* Obviously if the bridge/tunnel belongs to us, or no-one, we can remove it */
 
	if (CheckTileOwnership(tile) || IsTileOwner(tile, OWNER_NONE)) return true;
 
	/* Otherwise we can only remove town-owned stuff with extra patch-settings, or cheat */
 
	if (IsTileOwner(tile, OWNER_TOWN) && (_patches.extra_dynamite || _cheats.magic_bulldozer.value)) return true;
 
	return false;
 
}
 

	
 
static int32 DoClearTunnel(TileIndex tile, uint32 flags)
 
{
 
	Town *t = NULL;
 
	TileIndex endtile;
 
	uint length;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!CheckAllowRemoveTunnelBridge(tile)) return CMD_ERROR;
 

	
 
	endtile = CheckTunnelBusy(tile, &length);
 
	if (endtile == INVALID_TILE) return CMD_ERROR;
 

	
 
	_build_tunnel_endtile = endtile;
 

	
 
	if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
 
		t = ClosestTownFromTile(tile, (uint)-1); // town penalty rating
 

	
 
		/* Check if you are allowed to remove the tunnel owned by a town
 
		 * Removal depends on difficulty settings */
 
		if (!CheckforTownRating(flags, t, TUNNELBRIDGE_REMOVE)) {
 
			SetDParam(0, t->index);
 
			return_cmd_error(STR_2009_LOCAL_AUTHORITY_REFUSES);
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		// We first need to request the direction before calling DoClearSquare
 
		//  else the direction is always 0.. dah!! ;)
 
		DiagDirection dir = GetTunnelDirection(tile);
 
		Track track;
 

	
 
		// Adjust the town's player rating. Do this before removing the tile owner info.
 
		if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR)
 
			ChangeTownRating(t, RATING_TUNNEL_BRIDGE_DOWN_STEP, RATING_TUNNEL_BRIDGE_MINIMUM);
 

	
 
		DoClearSquare(tile);
 
		DoClearSquare(endtile);
 
		UpdateSignalsOnSegment(tile, ReverseDiagDir(dir));
 
		UpdateSignalsOnSegment(endtile, dir);
 
		track = AxisToTrack(DiagDirToAxis(dir));
 
		YapfNotifyTrackLayoutChange(tile, track);
 
		YapfNotifyTrackLayoutChange(endtile, track);
 
	}
 
	return _price.clear_tunnel * (length + 1);
 
}
 

	
 

	
 
static bool IsVehicleOnBridge(TileIndex starttile, TileIndex endtile, uint z)
 
{
 
	const Vehicle *v;
 
	FOR_ALL_VEHICLES(v) {
 
		if ((v->tile == starttile || v->tile == endtile) && v->z_pos == z) {
 
			_error_message = VehicleInTheWayErrMsg(v);
 
			return true;
 
		}
 
	}
 
	return false;
 
}
 

	
 
static int32 DoClearBridge(TileIndex tile, uint32 flags)
 
{
 
	DiagDirection direction;
 
	TileIndexDiff delta;
 
	TileIndex endtile;
 
	Town *t = NULL;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!CheckAllowRemoveTunnelBridge(tile)) return CMD_ERROR;
 

	
 
	endtile = GetOtherBridgeEnd(tile);
 

	
 
	if (!EnsureNoVehicle(tile) ||
 
			!EnsureNoVehicle(endtile) ||
 
			IsVehicleOnBridge(tile, endtile, GetBridgeHeight(tile))) {
 
		return CMD_ERROR;
 
	}
 

	
 
	direction = GetBridgeRampDirection(tile);
 
	delta = TileOffsByDiagDir(direction);
 

	
 
	if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
 
		t = ClosestTownFromTile(tile, (uint)-1); // town penalty rating
 

	
 
		/* Check if you are allowed to remove the bridge owned by a town
 
		 * Removal depends on difficulty settings */
 
		if (!CheckforTownRating(flags, t, TUNNELBRIDGE_REMOVE)) {
 
			SetDParam(0, t->index);
 
			return_cmd_error(STR_2009_LOCAL_AUTHORITY_REFUSES);
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		TileIndex c;
 
		Track track;
 

	
 
		//checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
 
		// you have a "Poor" (0) town rating
 
		if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR)
 
			ChangeTownRating(t, RATING_TUNNEL_BRIDGE_DOWN_STEP, RATING_TUNNEL_BRIDGE_MINIMUM);
 

	
 
		DoClearSquare(tile);
 
		DoClearSquare(endtile);
 
		for (c = tile + delta; c != endtile; c += delta) {
 
				ClearBridgeMiddle(c);
 
			MarkTileDirtyByTile(c);
 
		}
 

	
 
		UpdateSignalsOnSegment(tile, ReverseDiagDir(direction));
 
		UpdateSignalsOnSegment(endtile, direction);
 
		track = AxisToTrack(DiagDirToAxis(direction));
 
		YapfNotifyTrackLayoutChange(tile, track);
 
		YapfNotifyTrackLayoutChange(endtile, track);
 
	}
 

	
 
	return (DistanceManhattan(tile, endtile) + 1) * _price.clear_bridge;
 
}
 

	
 
static int32 ClearTile_TunnelBridge(TileIndex tile, byte flags)
 
{
 
	if (IsTunnel(tile)) {
 
		if (flags & DC_AUTO) return_cmd_error(STR_5006_MUST_DEMOLISH_TUNNEL_FIRST);
 
		return DoClearTunnel(tile, flags);
 
	} else if (IsBridge(tile)) { // XXX Is this necessary?
 
		if (flags & DC_AUTO) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
 
		return DoClearBridge(tile, flags);
 
	}
 

	
 
	return CMD_ERROR;
 
}
 

	
 
int32 DoConvertTunnelBridgeRail(TileIndex tile, RailType totype, bool exec)
 
{
 
	TileIndex endtile;
 

	
 
	if (IsTunnel(tile) && GetTunnelTransportType(tile) == TRANSPORT_RAIL) {
 
		uint length;
 

	
 
		if (!CheckTileOwnership(tile)) return CMD_ERROR;
 

	
 
		if (GetRailType(tile) == totype) return CMD_ERROR;
 

	
 
		// 'hidden' elrails can't be downgraded to normal rail when elrails are disabled
 
		if (_patches.disable_elrails && totype == RAILTYPE_RAIL && GetRailType(tile) == RAILTYPE_ELECTRIC) return CMD_ERROR;
 

	
 
		endtile = CheckTunnelBusy(tile, &length);
 
		if (endtile == INVALID_TILE) return CMD_ERROR;
 

	
 
		if (exec) {
 
			Track track;
 
			SetRailType(tile, totype);
 
			SetRailType(endtile, totype);
 
			MarkTileDirtyByTile(tile);
 
			MarkTileDirtyByTile(endtile);
 

	
 
			track = AxisToTrack(DiagDirToAxis(GetTunnelDirection(tile)));
 
			YapfNotifyTrackLayoutChange(tile, track);
 
			YapfNotifyTrackLayoutChange(endtile, track);
 
		}
 
		return (length + 1) * (_price.build_rail >> 1);
 
	} else if (IsBridge(tile) && GetBridgeTransportType(tile) == TRANSPORT_RAIL) {
 

	
 
		if (!CheckTileOwnership(tile)) return CMD_ERROR;
 

	
 
		endtile = GetOtherBridgeEnd(tile);
 

	
 
		if (!EnsureNoVehicle(tile) ||
 
				!EnsureNoVehicle(endtile) ||
 
				IsVehicleOnBridge(tile, endtile, GetBridgeHeight(tile))) {
 
			return CMD_ERROR;
 
		}
 

	
 
		if (GetRailType(tile) == totype) return CMD_ERROR;
 

	
 
		if (exec) {
 
			TileIndexDiff delta;
 
			Track track;
 

	
 
			SetRailType(tile, totype);
 
			SetRailType(endtile, totype);
 
			MarkTileDirtyByTile(tile);
 
			MarkTileDirtyByTile(endtile);
 

	
 
			track = AxisToTrack(DiagDirToAxis(GetBridgeRampDirection(tile)));
 
			YapfNotifyTrackLayoutChange(tile, track);
 
			YapfNotifyTrackLayoutChange(endtile, track);
 

	
 
			delta = TileOffsByDiagDir(GetBridgeRampDirection(tile));
 
			for (tile += delta; tile != endtile; tile += delta) {
 
				MarkTileDirtyByTile(tile); // TODO encapsulate this into a function
 
			}
 
		}
 

	
 
		return (DistanceManhattan(tile, endtile) + 1) * (_price.build_rail >> 1);
 
	} else {
 
		return CMD_ERROR;
 
	}
 
}
 

	
 

	
 
static void DrawBridgePillars(PalSpriteID image, const TileInfo* ti, Axis axis, uint type, int x, int y, int z)
 
{
 
	if (image != 0) {
 
		bool drawfarpillar = !HASBIT(GetBridgeFlags(type), 0);
 
		int back_height, front_height;
 
		int i = z;
 
		const byte *p;
 

	
 
		static const byte _tileh_bits[4][8] = {
 
			{ 2, 1, 8, 4,  16,  2, 0, 9 },
 
			{ 1, 8, 4, 2,   2, 16, 9, 0 },
 
			{ 4, 8, 1, 2,  16,  2, 0, 9 },
 
			{ 2, 4, 8, 1,   2, 16, 9, 0 }
 
		};
 

	
 
		if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 

	
 
		p = _tileh_bits[(image & 1) * 2 + (axis == AXIS_X ? 0 : 1)];
 
		front_height = ti->z + (ti->tileh & p[0] ? TILE_HEIGHT : 0);
 
		back_height  = ti->z + (ti->tileh & p[1] ? TILE_HEIGHT : 0);
 

	
 
		if (IsSteepSlope(ti->tileh)) {
 
			if (!(ti->tileh & p[2])) front_height += TILE_HEIGHT;
 
			if (!(ti->tileh & p[3])) back_height  += TILE_HEIGHT;
 
		}
 

	
 
		for (; z >= front_height || z >= back_height; z -= TILE_HEIGHT) {
 
			/* HACK set height of the BB of pillars to 1, because the origin of the
 
			 * sprites is at the top
 
			 */
 
			if (z >= front_height) { // front facing pillar
 
				AddSortableSpriteToDraw(image, x, y, p[4], p[5], 1, z);
 
			}
 

	
 
			if (drawfarpillar && z >= back_height && z < i - TILE_HEIGHT) { // back facing pillar
 
				AddSortableSpriteToDraw(image, x - p[6], y - p[7], p[4], p[5], 1, z);
 
			}
 
		}
 
	}
 
}
 

	
 
uint GetBridgeFoundation(Slope tileh, Axis axis)
 
{
 
	uint i;
 

	
 
	if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh)) return tileh;
 

	
 
	// inclined sloped building
 
	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;
 
	}
 
	if (axis != AXIS_X) ++i;
 
	return i + 15;
 
}
 

	
 
/**
 
 * Draws a tunnel of bridge tile.
 
 * For tunnels, this is rather simple, as you only needa draw the entrance.
 
 * Bridges are a bit more complex. base_offset is where the sprite selection comes into play
 
 * and it works a bit like a bitmask.<p> For bridge heads:
 
 * <ul><li>Bit 0: direction</li>
 
 * <li>Bit 1: northern or southern heads</li>
 
 * <li>Bit 2: Set if the bridge head is sloped</li>
 
 * <li>Bit 3 and more: Railtype Specific subset</li>
 
 * </ul>
 
 * Please note that in this code, "roads" are treated as railtype 1, whilst the real railtypes are 0, 2 and 3
 
 */
 
static void DrawTile_TunnelBridge(TileInfo *ti)
 
{
 
	uint32 image;
 

	
 
	if (IsTunnel(ti->tile)) {
 
		if (GetTunnelTransportType(ti->tile) == TRANSPORT_RAIL) {
 
			image = GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.tunnel;
 
		} else {
 
			image = SPR_TUNNEL_ENTRY_REAR_ROAD;
 
		}
 

	
 
		if (HasTunnelSnowOrDesert(ti->tile)) image += 32;
 

	
 
		image += GetTunnelDirection(ti->tile) * 2;
 
		DrawGroundSprite(image);
 
		if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti);
 

	
 
		AddSortableSpriteToDraw(image+1, ti->x + TILE_SIZE - 1, ti->y + TILE_SIZE - 1, 1, 1, 8, (byte)ti->z);
 
		DrawBridgeMiddle(ti);
 
	} else if (IsBridge(ti->tile)) { // XXX is this necessary?
 
		int base_offset;
 
		bool ice = HasBridgeSnowOrDesert(ti->tile);
 

	
 
		if (GetBridgeTransportType(ti->tile) == TRANSPORT_RAIL) {
 
			base_offset = GetRailTypeInfo(GetRailType(ti->tile))->bridge_offset;
 
			assert(base_offset != 8); /* This one is used for roads */
 
		} else {
 
			base_offset = 8;
 
		}
 

	
 
		/* as the lower 3 bits are used for other stuff, make sure they are clear */
 
		assert( (base_offset & 0x07) == 0x00);
 

	
 
		if (!HASBIT(BRIDGE_NO_FOUNDATION, ti->tileh)) {
 
			int f = GetBridgeFoundation(ti->tileh, DiagDirToAxis(GetBridgeRampDirection(ti->tile)));
 
			if (f != 0) DrawFoundation(ti, f);
 
		}
 

	
 
		// HACK Wizardry to convert the bridge ramp direction into a sprite offset
 
		base_offset += (6 - GetBridgeRampDirection(ti->tile)) % 4;
 

	
 
		if (ti->tileh == SLOPE_FLAT) base_offset += 4; // sloped bridge head
 

	
 
		/* Table number 6 always refers to the bridge heads for any bridge type */
 
		image = GetBridgeSpriteTable(GetBridgeType(ti->tile), 6)[base_offset];
 

	
 
		if (!ice) {
 
			DrawClearLandTile(ti, 3);
 
		} else {
 
			DrawGroundSprite(SPR_FLAT_SNOWY_TILE + _tileh_to_sprite[ti->tileh]);
 
		}
 

	
 
		if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti);
 

	
 
		// draw ramp
 
		if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 
		/* HACK set the height of the BB of a sloped ramp to 1 so a vehicle on
 
		 * it doesn't disappear behind it
 
		 */
 
		AddSortableSpriteToDraw(
 
			image, ti->x, ti->y, 16, 16, ti->tileh == SLOPE_FLAT ? 1 : 8, ti->z
 
		);
 

	
 
		DrawBridgeMiddle(ti);
 
	}
 
}
 

	
 

	
 
/** Compute bridge piece. Computes the bridge piece to display depending on the position inside the bridge.
 
 * bridges pieces sequence (middle parts)
 
 * bridge len 1: 0
 
 * bridge len 2: 0 1
 
 * bridge len 3: 0 4 1
 
 * bridge len 4: 0 2 3 1
 
 * bridge len 5: 0 2 5 3 1
 
 * bridge len 6: 0 2 3 2 3 1
 
 * bridge len 7: 0 2 3 4 2 3 1
 
 * #0 - always as first, #1 - always as last (if len>1)
 
 * #2,#3 are to pair in order
 
 * for odd bridges: #5 is going in the bridge middle if on even position, #4 on odd (counting from 0)
 
 * @param north Northernmost tile of bridge
 
 * @param south Southernmost tile of bridge
 
 * @return Index of bridge piece
 
 */
 
static uint CalcBridgePiece(uint north, uint south)
 
{
 
	if (north == 1) {
 
		return 0;
 
	} else if (south == 1) {
 
		return 1;
 
	} else if (north < south) {
 
		return north & 1 ? 3 : 2;
 
	} else if (north > south) {
 
		return south & 1 ? 2 : 3;
 
	} else {
 
		return north & 1 ? 5 : 4;
 
	}
 
}
 

	
 

	
 
void DrawBridgeMiddle(const TileInfo* ti)
 
{
 
	const PalSpriteID* b;
 
	PalSpriteID image;
 
	uint base_offset;
 
	TileIndex rampnorth;
 
	TileIndex rampsouth;
 
	Axis axis;
 
	uint piece;
 
	uint type;
 
	int x;
 
	int y;
 
	uint z;
 

	
 
	if (!IsBridgeAbove(ti->tile)) return;
 

	
 
	rampnorth = GetNorthernBridgeEnd(ti->tile);
 
	rampsouth = GetSouthernBridgeEnd(ti->tile);
 

	
 
	axis = GetBridgeAxis(ti->tile);
 
	piece = CalcBridgePiece(
 
		DistanceManhattan(ti->tile, rampnorth),
 
		DistanceManhattan(ti->tile, rampsouth)
 
	);
 
	type = GetBridgeType(rampsouth);
 

	
 
	if (GetBridgeTransportType(rampsouth) == TRANSPORT_RAIL) {
 
		base_offset = GetRailTypeInfo(GetRailType(rampsouth))->bridge_offset;
 
	} else {
 
		base_offset = 8;
 
	}
 

	
 
	b = base_offset + GetBridgeSpriteTable(type, piece);
 
	if (axis != AXIS_X) b += 4;
 

	
 
	x = ti->x;
 
	y = ti->y;
 
	z = GetBridgeHeight(rampsouth) - 3;
 

	
 
	image = b[0];
 
	if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 
	if (axis == AXIS_X) {
 
		AddSortableSpriteToDraw(image, x, y, 16, 11, 1, z);
 
	} else {
 
		AddSortableSpriteToDraw(image, x, y, 11, 16, 1, z);
 
	}
 

	
 
	image = b[1];
 
	if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 

	
 
	// draw roof, the component of the bridge which is logically between the vehicle and the camera
 
	if (axis == AXIS_X) {
 
		y += 12;
 
		if (image & SPRITE_MASK) AddSortableSpriteToDraw(image, x, y, 16, 1, 0x28, z);
 
	} else {
 
		x += 12;
 
		if (image & SPRITE_MASK) AddSortableSpriteToDraw(image, x, y, 1, 16, 0x28, z);
 
	}
 

	
 
	if (GetRailType(rampsouth) == RAILTYPE_ELECTRIC) DrawCatenary(ti);
 

	
 
	if (ti->z + 5 == z) {
 
		// draw poles below for small bridges
 
		image = b[2];
 
		if (image != 0) {
 
			if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 
			DrawGroundSpriteAt(image, x, y, z);
 
		}
 
	} else if (_patches.bridge_pillars) {
 
		// draw pillars below for high bridges
 
		DrawBridgePillars(b[2], ti, axis, type, x, y, z);
 
	}
 
}
 

	
 

	
 
uint SetSpeedLimitOnBridge(Vehicle *v)
 
{
 
	uint bridge_speed;
 
	if (v->vehstatus & VS_HIDDEN) return v->max_speed; /* in tunnel */
 

	
 
	bridge_speed = _bridge[GetBridgeType(v->tile)].speed;
 

	
 
	if (v->type == VEH_Road) bridge_speed *= 2; /* XXX give vehicles proper speeds */
 

	
 
	if (v->cur_speed > bridge_speed) v->cur_speed = bridge_speed;
 
	return bridge_speed;
 
}
 

	
 

	
 

	
 
static uint GetSlopeZ_TunnelBridge(TileIndex tile, uint x, uint y)
 
{
 
	uint z;
 
	Slope tileh = GetTileSlope(tile, &z);
 

	
 
	x &= 0xF;
 
	y &= 0xF;
 

	
 
	if (IsTunnel(tile)) {
 
		uint pos = (DiagDirToAxis(GetTunnelDirection(tile)) == AXIS_X ? y : x);
 

	
 
		// In the tunnel entrance?
 
		if (5 <= pos && pos <= 10) return z;
 
	} else {
 
		DiagDirection dir = GetBridgeRampDirection(tile);
 
		uint pos = (DiagDirToAxis(dir) == AXIS_X ? y : x);
 

	
 
		// On the bridge ramp?
 
		if (5 <= pos && pos <= 10) {
 
			uint delta;
 

	
 
			if (IsSteepSlope(tileh)) return z + TILE_HEIGHT * 2;
 
			if (HASBIT(BRIDGE_HORZ_RAMP, tileh)) return z + TILE_HEIGHT;
 

	
 
			if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh)) z += TILE_HEIGHT;
 
			switch (dir) {
 
				default: NOT_REACHED();
 
				case DIAGDIR_NE: delta = (TILE_SIZE - 1 - x) / 2; break;
 
				case DIAGDIR_SE: delta = y / 2; break;
 
				case DIAGDIR_SW: delta = x / 2; break;
 
				case DIAGDIR_NW: delta = (TILE_SIZE - 1 - y) / 2; break;
 
			}
 
			return z + 1 + delta;
 
		} else {
 
			uint f = GetBridgeFoundation(tileh, DiagDirToAxis(dir));
 

	
 
			if (f != 0) {
 
				if (IsSteepSlope(tileh)) {
 
					z += TILE_HEIGHT;
 
				} else if (f < 15) {
 
					return z + TILE_HEIGHT;
 
				}
 
				tileh = _inclined_tileh[f - 15];
 
			}
 
		}
 
	}
 

	
 
	return z + GetPartialZ(x, y, tileh);
 
}
 

	
 
static Slope GetSlopeTileh_TunnelBridge(TileIndex tile, Slope tileh)
 
{
 
	if (IsTunnel(tile)) {
 
		return tileh;
 
	} else {
 
		if (HASBIT(BRIDGE_NO_FOUNDATION, tileh)) {
 
			return tileh;
 
		} else {
 
			uint f = GetBridgeFoundation(tileh, DiagDirToAxis(GetBridgeRampDirection(tile)));
 

	
 
			if (f == 0) return tileh;
 
			if (f < 15) return SLOPE_FLAT;
 
			return _inclined_tileh[f - 15];
 
		}
 
	}
 
}
 

	
 

	
 
static void GetAcceptedCargo_TunnelBridge(TileIndex tile, AcceptedCargo ac)
 
{
 
	/* not used */
 
}
 

	
 
static const StringID _bridge_tile_str[(MAX_BRIDGES + 3) + (MAX_BRIDGES + 3)] = {
 
	STR_501F_WOODEN_RAIL_BRIDGE,
 
	STR_5020_CONCRETE_RAIL_BRIDGE,
 
	STR_501C_STEEL_GIRDER_RAIL_BRIDGE,
 
	STR_501E_REINFORCED_CONCRETE_SUSPENSION,
 
	STR_501B_STEEL_SUSPENSION_RAIL_BRIDGE,
 
	STR_501B_STEEL_SUSPENSION_RAIL_BRIDGE,
 
	STR_501D_STEEL_CANTILEVER_RAIL_BRIDGE,
 
	STR_501D_STEEL_CANTILEVER_RAIL_BRIDGE,
 
	STR_501D_STEEL_CANTILEVER_RAIL_BRIDGE,
 
	STR_501C_STEEL_GIRDER_RAIL_BRIDGE,
 
	STR_5027_TUBULAR_RAIL_BRIDGE,
 
	STR_5027_TUBULAR_RAIL_BRIDGE,
 
	STR_5027_TUBULAR_RAIL_BRIDGE,
 
	0, 0, 0,
 

	
 
	STR_5025_WOODEN_ROAD_BRIDGE,
 
	STR_5026_CONCRETE_ROAD_BRIDGE,
 
	STR_5022_STEEL_GIRDER_ROAD_BRIDGE,
 
	STR_5024_REINFORCED_CONCRETE_SUSPENSION,
 
	STR_5021_STEEL_SUSPENSION_ROAD_BRIDGE,
 
	STR_5021_STEEL_SUSPENSION_ROAD_BRIDGE,
 
	STR_5023_STEEL_CANTILEVER_ROAD_BRIDGE,
 
	STR_5023_STEEL_CANTILEVER_ROAD_BRIDGE,
 
	STR_5023_STEEL_CANTILEVER_ROAD_BRIDGE,
 
	STR_5022_STEEL_GIRDER_ROAD_BRIDGE,
 
	STR_5028_TUBULAR_ROAD_BRIDGE,
 
	STR_5028_TUBULAR_ROAD_BRIDGE,
 
	STR_5028_TUBULAR_ROAD_BRIDGE,
 
	0, 0, 0,
 
};
 

	
 
static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td)
 
{
 
	if (IsTunnel(tile)) {
 
		td->str = (GetTunnelTransportType(tile) == TRANSPORT_RAIL) ?
 
			STR_5017_RAILROAD_TUNNEL : STR_5018_ROAD_TUNNEL;
 
	} else {
 
		td->str = _bridge_tile_str[GetBridgeTransportType(tile) << 4 | GetBridgeType(tile)];
 
	}
 
	td->owner = GetTileOwner(tile);
 
}
 

	
 

	
 
static void AnimateTile_TunnelBridge(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static void TileLoop_TunnelBridge(TileIndex tile)
 
{
 
	bool snow_or_desert = IsTunnelTile(tile) ? HasTunnelSnowOrDesert(tile) : HasBridgeSnowOrDesert(tile);
 
	switch (_opt.landscape) {
 
		case LT_HILLY:
 
			if (snow_or_desert != (GetTileZ(tile) > _opt.snow_line)) {
 
				if (IsTunnelTile(tile)) {
 
					SetTunnelSnowOrDesert(tile, !snow_or_desert);
 
				} else {
 
					SetBridgeSnowOrDesert(tile, !snow_or_desert);
 
				}
 
				MarkTileDirtyByTile(tile);
 
			}
 
			break;
 

	
 
		case LT_DESERT:
 
			if (GetTropicZone(tile) == TROPICZONE_DESERT && !snow_or_desert) {
 
				if (IsTunnelTile(tile)) {
 
					SetTunnelSnowOrDesert(tile, true);
 
				} else {
 
					SetBridgeSnowOrDesert(tile, true);
 
				}
 
				MarkTileDirtyByTile(tile);
 
			}
 
			break;
 
	}
 
}
 

	
 
static void ClickTile_TunnelBridge(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 

	
 
static uint32 GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode)
 
{
 
	if (IsTunnel(tile)) {
 
		if (GetTunnelTransportType(tile) != mode) return 0;
 
		return AxisToTrackBits(DiagDirToAxis(GetTunnelDirection(tile))) * 0x101;
 
	} else {
 
		if (GetBridgeTransportType(tile) != mode) return 0;
 
		return AxisToTrackBits(DiagDirToAxis(GetBridgeRampDirection(tile))) * 0x101;
 
	}
 
}
 

	
 
static void ChangeTileOwner_TunnelBridge(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	if (!IsTileOwner(tile, old_player)) return;
 

	
 
	if (new_player != PLAYER_SPECTATOR) {
 
		SetTileOwner(tile, new_player);
 
	} else {
 
		DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
	}
 
}
 

	
 

	
 
static const byte _tunnel_fractcoord_1[4]    = {0x8E, 0x18, 0x81, 0xE8};
 
static const byte _tunnel_fractcoord_2[4]    = {0x81, 0x98, 0x87, 0x38};
 
static const byte _tunnel_fractcoord_3[4]    = {0x82, 0x88, 0x86, 0x48};
 
static const byte _exit_tunnel_track[4]      = {1, 2, 1, 2};
 

	
 
static const byte _road_exit_tunnel_state[4] = {8, 9, 0, 1};
 
static const byte _road_exit_tunnel_frame[4] = {2, 7, 9, 4};
 

	
 
static const byte _tunnel_fractcoord_4[4]    = {0x52, 0x85, 0x98, 0x29};
 
static const byte _tunnel_fractcoord_5[4]    = {0x92, 0x89, 0x58, 0x25};
 
static const byte _tunnel_fractcoord_6[4]    = {0x92, 0x89, 0x56, 0x45};
 
static const byte _tunnel_fractcoord_7[4]    = {0x52, 0x85, 0x96, 0x49};
 

	
 
static uint32 VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y)
 
{
 
	int z = GetSlopeZ(x, y) - v->z_pos;
 

	
 
	if (myabs(z) > 2) return 8;
 

	
 
	if (IsTunnel(tile)) {
 
		byte fc;
 
		DiagDirection dir;
 
		DiagDirection vdir;
 

	
 
		if (v->type == VEH_Train) {
 
			fc = (x & 0xF) + (y << 4);
 

	
 
			dir = GetTunnelDirection(tile);
 
			vdir = DirToDiagDir(v->direction);
 

	
 
			if (v->u.rail.track != 0x40 && dir == vdir) {
 
				if (IsFrontEngine(v) && fc == _tunnel_fractcoord_1[dir]) {
 
					if (!PlayVehicleSound(v, VSE_TUNNEL) && v->spritenum < 4) {
 
						SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v);
 
					}
 
					return 0;
 
				}
 
				if (fc == _tunnel_fractcoord_2[dir]) {
 
					v->tile = tile;
 
					v->u.rail.track = 0x40;
 
					v->vehstatus |= VS_HIDDEN;
 
					return 4;
 
				}
 
			}
 

	
 
			if (dir == ReverseDiagDir(vdir) && fc == _tunnel_fractcoord_3[dir] && z == 0) {
 
				/* We're at the tunnel exit ?? */
 
				v->tile = tile;
 
				v->u.rail.track = _exit_tunnel_track[dir];
 
				assert(v->u.rail.track);
 
				v->vehstatus &= ~VS_HIDDEN;
 
				return 4;
 
			}
 
		} else if (v->type == VEH_Road) {
 
			fc = (x & 0xF) + (y << 4);
 
			dir = GetTunnelDirection(tile);
 
			vdir = DirToDiagDir(v->direction);
 

	
 
			// Enter tunnel?
 
			if (v->u.road.state != 0xFF && dir == vdir) {
 
				if (fc == _tunnel_fractcoord_4[dir] ||
 
						fc == _tunnel_fractcoord_5[dir]) {
 
					v->tile = tile;
 
					v->u.road.state = 0xFF;
 
					v->vehstatus |= VS_HIDDEN;
 
					return 4;
 
				} else {
 
					return 0;
 
				}
 
			}
 

	
 
			if (dir == ReverseDiagDir(vdir) && (
 
						/* We're at the tunnel exit ?? */
 
						fc == _tunnel_fractcoord_6[dir] ||
 
						fc == _tunnel_fractcoord_7[dir]
 
					) &&
 
					z == 0) {
 
				v->tile = tile;
 
				v->u.road.state = _road_exit_tunnel_state[dir];
 
				v->u.road.frame = _road_exit_tunnel_frame[dir];
 
				v->vehstatus &= ~VS_HIDDEN;
 
				return 4;
 
			}
 
		}
 
	} else if (IsBridge(tile)) { // XXX is this necessary?
 
		DiagDirection dir;
 

	
 
		if (v->type == VEH_Road || (v->type == VEH_Train && IsFrontEngine(v))) {
 
			/* modify speed of vehicle */
 
			uint16 spd = _bridge[GetBridgeType(tile)].speed;
 

	
 
			if (v->type == VEH_Road) spd *= 2;
 
			if (v->cur_speed > spd) v->cur_speed = spd;
 
		}
 

	
 
		dir = GetBridgeRampDirection(tile);
 
		if (DirToDiagDir(v->direction) == dir) {
 
			switch (dir) {
 
				default: NOT_REACHED();
 
				case DIAGDIR_NE: if ((x & 0xF) != 0)             return 0; break;
 
				case DIAGDIR_SE: if ((y & 0xF) != TILE_SIZE - 1) return 0; break;
 
				case DIAGDIR_SW: if ((x & 0xF) != TILE_SIZE - 1) return 0; break;
 
				case DIAGDIR_NW: if ((y & 0xF) != 0)             return 0; break;
 
			}
 
			if (v->type == VEH_Train) {
 
				v->u.rail.track = 0x40;
 
				CLRBIT(v->u.rail.flags, VRF_GOINGUP);
 
				CLRBIT(v->u.rail.flags, VRF_GOINGDOWN);
 
			} else {
 
				v->u.road.state = 0xFF;
 
			}
 
			return 4;
 
		} else if (DirToDiagDir(v->direction) == ReverseDiagDir(dir)) {
 
			v->tile = tile;
 
			if (v->type == VEH_Train) {
 
				if (v->u.rail.track == 0x40) {
 
					v->u.rail.track = (DiagDirToAxis(dir) == AXIS_X ? 1 : 2);
 
					return 4;
 
				}
 
			} else {
 
				if (v->u.road.state == 0xFF) {
 
					v->u.road.state = _road_exit_tunnel_state[dir];
 
					v->u.road.frame = 0;
 
					return 4;
 
				}
 
			}
 
			return 0;
 
		}
 
	}
 
	return 0;
 
}
 

	
 
const TileTypeProcs _tile_type_tunnelbridge_procs = {
 
	DrawTile_TunnelBridge,           /* draw_tile_proc */
 
	GetSlopeZ_TunnelBridge,          /* get_slope_z_proc */
 
	ClearTile_TunnelBridge,          /* clear_tile_proc */
 
	GetAcceptedCargo_TunnelBridge,   /* get_accepted_cargo_proc */
 
	GetTileDesc_TunnelBridge,        /* get_tile_desc_proc */
 
	GetTileTrackStatus_TunnelBridge, /* get_tile_track_status_proc */
 
	ClickTile_TunnelBridge,          /* click_tile_proc */
 
	AnimateTile_TunnelBridge,        /* animate_tile_proc */
 
	TileLoop_TunnelBridge,           /* tile_loop_clear */
 
	ChangeTileOwner_TunnelBridge,    /* change_tile_owner_clear */
 
	NULL,                            /* get_produced_cargo_proc */
 
	VehicleEnter_TunnelBridge,       /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_TunnelBridge,      /* get_slope_tileh_proc */
 
};
src/unix.c
Show inline comments
 
deleted file
src/unix.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "window.h"
 
#include "string.h"
 
#include "table/strings.h"
 
#include "variables.h"
 

	
 
#include <dirent.h>
 
#include <unistd.h>
 
#include <sys/stat.h>
 
#include <time.h>
 
#include <signal.h>
 

	
 
#ifdef USE_HOMEDIR
 
#include <pwd.h>
 
#endif
 

	
 
#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) || defined(__GLIBC__)
 
	#define HAS_STATVFS
 
#endif
 

	
 
#ifdef HAS_STATVFS
 
#include <sys/statvfs.h>
 
#endif
 

	
 

	
 
#ifdef __MORPHOS__
 
#include <exec/types.h>
 
ULONG __stack = (1024*1024)*2; // maybe not that much is needed actually ;)
 

	
 
// The system supplied definition of SIG_IGN does not match
 
#undef SIG_IGN
 
#define SIG_IGN (void (*)(int))1
 
#endif /* __MORPHOS__ */
 

	
 
#ifdef __AMIGA__
 
#warning add stack symbol to avoid that user needs to set stack manually (tokai)
 
// ULONG __stack =
 
#endif
 

	
 
#if defined(__APPLE__)
 
	#if defined(WITH_SDL)
 
		//the mac implementation needs this file included in the same file as main()
 
		#include <SDL.h>
 
	#endif
 
#endif
 

	
 
bool FiosIsRoot(const char *path)
 
{
 
#if !defined(__MORPHOS__) && !defined(__AMIGAOS__)
 
	return path[1] == '\0';
 
#else
 
	/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
 
	const char *s = strchr(path, ':');
 
	return s[1] == '\0';
 
#endif
 
}
 

	
 
void FiosGetDrives(void)
 
{
 
	return;
 
}
 

	
 
bool FiosGetDiskFreeSpace(const char *path, uint32 *tot)
 
{
 
	uint32 free = 0;
 

	
 
#ifdef HAS_STATVFS
 
	{
 
		struct statvfs s;
 

	
 
		if (statvfs(path, &s) != 0) return false;
 
		free = (uint64)s.f_frsize * s.f_bavail >> 20;
 
	}
 
#endif
 
	if (tot != NULL) *tot = free;
 
	return true;
 
}
 

	
 
bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb)
 
{
 
	char filename[MAX_PATH];
 

	
 
#if defined(__MORPHOS__) || defined(__AMIGAOS__)
 
	/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
 
	if (FiosIsRoot(path)) {
 
		snprintf(filename, lengthof(filename), "%s:%s", path, ent->d_name);
 
	} else // XXX - only next line!
 
#endif
 
	snprintf(filename, lengthof(filename), "%s" PATHSEP "%s", path, ent->d_name);
 

	
 
	if (stat(filename, sb) != 0) return false;
 

	
 
	return (ent->d_name[0] != '.'); // hidden file
 
}
 

	
 
#if defined(__BEOS__) || defined(__linux__)
 
static void ChangeWorkingDirectory(char *exe)
 
{
 
	char *s = strrchr(exe, '/');
 
	if (s != NULL) {
 
		*s = '\0';
 
		chdir(exe);
 
		*s = '/';
 
	}
 
}
 
#endif
 

	
 
void ShowInfo(const char *str)
 
{
 
	fprintf(stderr, str);
 
}
 

	
 
void ShowOSErrorBox(const char *buf)
 
{
 
#if defined(__APPLE__)
 
	// this creates an NSAlertPanel with the contents of 'buf'
 
	// this is the native and nicest way to do this on OSX
 
	ShowMacDialog( buf, "See readme for more info\nMost likely you are missing files from the original TTD", "Quit" );
 
#else
 
	// all systems, but OSX
 
	fprintf(stderr, "\033[1;31mError: %s\033[0;39m\n", buf);
 
#endif
 
}
 

	
 
#ifdef WITH_COCOA
 
void cocoaSetWorkingDirectory(void);
 
void cocoaSetupAutoreleasePool(void);
 
void cocoaReleaseAutoreleasePool(void);
 
#endif
 

	
 
int CDECL main(int argc, char* argv[])
 
{
 
	int ret;
 

	
 
#ifdef WITH_COCOA
 
	cocoaSetupAutoreleasePool();
 
	/* This is passed if we are launched by double-clicking */
 
	if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0) {
 
		argv[1] = NULL;
 
		argc = 1;
 
		cocoaSetWorkingDirectory();
 
	}
 
#endif
 

	
 
	// change the working directory to enable doubleclicking in UIs
 
#if defined(__BEOS__) || defined(__linux__)
 
	ChangeWorkingDirectory(argv[0]);
 
#endif
 

	
 
	_random_seeds[1][1] = _random_seeds[1][0] = _random_seeds[0][1] = _random_seeds[0][0] = time(NULL);
 
	SeedMT(_random_seeds[0][1]);
 

	
 
	signal(SIGPIPE, SIG_IGN);
 

	
 
	ret = ttd_main(argc, argv);
 

	
 
#ifdef WITH_COCOA
 
	cocoaReleaseAutoreleasePool();
 
#endif
 

	
 
	return ret;
 
}
 

	
 
void DeterminePaths(void)
 
{
 
	char *s;
 

	
 
	_paths.game_data_dir = malloc(MAX_PATH);
 
	ttd_strlcpy(_paths.game_data_dir, GAME_DATA_DIR, MAX_PATH);
 
	#if defined SECOND_DATA_DIR
 
	_paths.second_data_dir = malloc(MAX_PATH);
 
	ttd_strlcpy(_paths.second_data_dir, SECOND_DATA_DIR, MAX_PATH);
 
	#endif
 

	
 
#if defined(USE_HOMEDIR)
 
	{
 
		const char *homedir = getenv("HOME");
 

	
 
		if (homedir == NULL) {
 
			const struct passwd *pw = getpwuid(getuid());
 
			if (pw != NULL) homedir = pw->pw_dir;
 
		}
 

	
 
		_paths.personal_dir = str_fmt("%s" PATHSEP "%s", homedir, PERSONAL_DIR);
 
	}
 

	
 
#else /* not defined(USE_HOMEDIR) */
 

	
 
	_paths.personal_dir = malloc(MAX_PATH);
 
	ttd_strlcpy(_paths.personal_dir, PERSONAL_DIR, MAX_PATH);
 

	
 
	// check if absolute or relative path
 
	s = strchr(_paths.personal_dir, '/');
 

	
 
	// add absolute path
 
	if (s == NULL || _paths.personal_dir != s) {
 
		getcwd(_paths.personal_dir, MAX_PATH);
 
		s = strchr(_paths.personal_dir, 0);
 
		*s++ = '/';
 
		ttd_strlcpy(s, PERSONAL_DIR, MAX_PATH);
 
	}
 

	
 
#endif /* defined(USE_HOMEDIR) */
 

	
 
	s = strchr(_paths.personal_dir, 0);
 

	
 
	// append a / ?
 
	if (s[-1] != '/') strcpy(s, "/");
 

	
 
	_paths.save_dir = str_fmt("%ssave", _paths.personal_dir);
 
	_paths.autosave_dir = str_fmt("%s/autosave", _paths.save_dir);
 
	_paths.scenario_dir = str_fmt("%sscenario", _paths.personal_dir);
 
	_paths.heightmap_dir = str_fmt("%sscenario/heightmap", _paths.personal_dir);
 
	_paths.gm_dir = str_fmt("%sgm/", _paths.game_data_dir);
 
	_paths.data_dir = str_fmt("%sdata/", _paths.game_data_dir);
 

	
 
	if (_config_file == NULL)
 
		_config_file = str_fmt("%sopenttd.cfg", _paths.personal_dir);
 

	
 
	_highscore_file = str_fmt("%shs.dat", _paths.personal_dir);
 
	_log_file = str_fmt("%sopenttd.log", _paths.personal_dir);
 

	
 
#if defined CUSTOM_LANG_DIR
 
	// sets the search path for lng files to the custom one
 
	_paths.lang_dir = malloc( MAX_PATH );
 
	ttd_strlcpy( _paths.lang_dir, CUSTOM_LANG_DIR, MAX_PATH);
 
#else
 
	_paths.lang_dir = str_fmt("%slang/", _paths.game_data_dir);
 
#endif
 

	
 
	// create necessary folders
 
	mkdir(_paths.personal_dir, 0755);
 
	mkdir(_paths.save_dir, 0755);
 
	mkdir(_paths.autosave_dir, 0755);
 
	mkdir(_paths.scenario_dir, 0755);
 
	mkdir(_paths.heightmap_dir, 0755);
 
}
 

	
 
bool InsertTextBufferClipboard(Textbuf *tb)
 
{
 
	return false;
 
}
 

	
 

	
 
// multi os compatible sleep function
 

	
 
#ifdef __AMIGA__
 
// usleep() implementation
 
#	include <devices/timer.h>
 
#	include <dos/dos.h>
 

	
 
	extern struct Device      *TimerBase    = NULL;
 
	extern struct MsgPort     *TimerPort    = NULL;
 
	extern struct timerequest *TimerRequest = NULL;
 
#endif // __AMIGA__
 

	
 
void CSleep(int milliseconds)
 
{
 
	#if !defined(__BEOS__) && !defined(__AMIGA__)
 
		usleep(milliseconds * 1000);
 
	#endif
 
	#ifdef __BEOS__
 
		snooze(milliseconds * 1000);
 
	#endif
 
	#if defined(__AMIGA__)
 
	{
 
		ULONG signals;
 
		ULONG TimerSigBit = 1 << TimerPort->mp_SigBit;
 

	
 
		// send IORequest
 
		TimerRequest->tr_node.io_Command = TR_ADDREQUEST;
 
		TimerRequest->tr_time.tv_secs    = (milliseconds * 1000) / 1000000;
 
		TimerRequest->tr_time.tv_micro   = (milliseconds * 1000) % 1000000;
 
		SendIO((struct IORequest *)TimerRequest);
 

	
 
		if (!((signals = Wait(TimerSigBit | SIGBREAKF_CTRL_C)) & TimerSigBit) ) {
 
			AbortIO((struct IORequest *)TimerRequest);
 
		}
 
		WaitIO((struct IORequest *)TimerRequest);
 
	}
 
	#endif // __AMIGA__
 
}
 

	
 
#ifdef WITH_ICONV
 

	
 
#include <iconv.h>
 
#include <errno.h>
 
#include "debug.h"
 

	
 
#define INTERNALCODE "UTF-8"
 

	
 
/** Try and try to decipher the current locale from environmental
 
 * variables. MacOSX is hardcoded, other OS's are dynamic. If no suitable
 
 * locale can be found, don't do any conversion "" */
 
static const char *GetLocalCode(void)
 
{
 
#if defined(__APPLE__)
 
	return "UTF-8-MAC";
 
#else
 
	/* Strip locale (eg en_US.UTF-8) to only have UTF-8 */
 
	const char *locale = GetCurrentLocale("LC_CTYPE");
 
	if (locale != NULL) locale = strchr(locale, '.');
 

	
 
	return (locale == NULL) ? "" : locale + 1;
 
#endif
 
}
 

	
 
/** FYI: This is not thread-safe.
 
 * convert between locales, which from and which to is set in the calling
 
 * functions OTTD2FS() and FS2OTTD(). You should NOT use this function directly
 
 * NOTE: iconv was added in OSX 10.3. 10.2.x will still have the invalid char
 
 * issues. There aren't any easy fix for this */
 
static const char *convert_tofrom_fs(iconv_t convd, const char *name)
 
{
 
	static char buf[1024];
 
	/* Work around buggy iconv implementation where inbuf is wrongly typed as
 
	 * non-const. Correct implementation is at
 
	 * http://www.opengroup.org/onlinepubs/007908799/xsh/iconv.html */
 
#if defined (__GLIBC__) || defined (__GNU_LIBRARY__)
 
	char *inbuf = (char*)name;
 
#else
 
	const char *inbuf = name;
 
#endif
 

	
 
	char *outbuf  = buf;
 
	size_t outlen = sizeof(buf) - 1;
 
	size_t inlen  = strlen(name);
 

	
 
	ttd_strlcpy(outbuf, name, sizeof(buf));
 

	
 
	iconv(convd, NULL, NULL, NULL, NULL);
 
	if (iconv(convd, &inbuf, &inlen, &outbuf, &outlen) == (size_t)(-1)) {
 
		DEBUG(misc, 0, "[iconv] error converting '%s'. Errno %d", name, errno);
 
	}
 

	
 
	*outbuf = '\0';
 
	// FIX: invalid characters will abort conversion, but they shouldn't occur?
 
	return buf;
 
}
 

	
 
/** Convert from OpenTTD's encoding to that of the local environment
 
 * @param name pointer to a valid string that will be converted
 
 * @return pointer to a new stringbuffer that contains the converted string */
 
const char *OTTD2FS(const char *name)
 
{
 
	static iconv_t convd = (iconv_t)(-1);
 

	
 
	if (convd == (iconv_t)(-1)) {
 
		const char *env = GetLocalCode();
 
		convd = iconv_open(env, INTERNALCODE);
 
		if (convd == (iconv_t)(-1)) {
 
			DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", INTERNALCODE, env);
 
			return name;
 
		}
 
	}
 

	
 
	return convert_tofrom_fs(convd, name);
 
}
 

	
 
/** Convert to OpenTTD's encoding from that of the local environment
 
 * @param name pointer to a valid string that will be converted
 
 * @return pointer to a new stringbuffer that contains the converted string */
 
const char *FS2OTTD(const char *name)
 
{
 
	static iconv_t convd = (iconv_t)(-1);
 

	
 
	if (convd == (iconv_t)(-1)) {
 
		const char *env = GetLocalCode();
 
		convd = iconv_open(INTERNALCODE, env);
 
		if (convd == (iconv_t)(-1)) {
 
			DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", env, INTERNALCODE);
 
			return name;
 
		}
 
	}
 

	
 
	return convert_tofrom_fs(convd, name);
 
}
 

	
 
#else
 
const char *FS2OTTD(const char *name) {return name;}
 
const char *OTTD2FS(const char *name) {return name;}
 
#endif /* WITH_ICONV */
src/unmovable_cmd.c
Show inline comments
 
deleted file
src/unmovable_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.h"
 
#include "table/strings.h"
 
#include "table/sprites.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "command.h"
 
#include "viewport.h"
 
#include "player.h"
 
#include "gui.h"
 
#include "station.h"
 
#include "economy.h"
 
#include "town.h"
 
#include "sprite.h"
 
#include "unmovable_map.h"
 
#include "variables.h"
 
#include "table/unmovable_land.h"
 
#include "genworld.h"
 

	
 
/** Destroy a HQ.
 
 * During normal gameplay you can only implicitely destroy a HQ when you are
 
 * rebuilding it. Otherwise, only water can destroy it.
 
 * @param tile tile coordinates where HQ is located to destroy
 
 * @param flags docommand flags of calling function
 
 */
 
static int32 DestroyCompanyHQ(PlayerID pid, uint32 flags)
 
{
 
	Player* p = GetPlayer(pid);
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_PROPERTY);
 

	
 
	if (flags & DC_EXEC) {
 
		TileIndex t = p->location_of_house;
 

	
 
		DoClearSquare(t + TileDiffXY(0, 0));
 
		DoClearSquare(t + TileDiffXY(0, 1));
 
		DoClearSquare(t + TileDiffXY(1, 0));
 
		DoClearSquare(t + TileDiffXY(1, 1));
 
		p->location_of_house = 0; // reset HQ position
 
		InvalidateWindow(WC_COMPANY, pid);
 
	}
 

	
 
	// cost of relocating company is 1% of company value
 
	return CalculateCompanyValue(p) / 100;
 
}
 

	
 
void UpdateCompanyHQ(Player *p, uint score)
 
{
 
	byte val;
 
	TileIndex tile = p->location_of_house;
 

	
 
	if (tile == 0) return;
 

	
 
	(val = 0, score < 170) ||
 
	(val++, score < 350) ||
 
	(val++, score < 520) ||
 
	(val++, score < 720) ||
 
	(val++, true);
 

	
 
	EnlargeCompanyHQ(tile, val);
 

	
 
	MarkTileDirtyByTile(tile + TileDiffXY(0, 0));
 
	MarkTileDirtyByTile(tile + TileDiffXY(0, 1));
 
	MarkTileDirtyByTile(tile + TileDiffXY(1, 0));
 
	MarkTileDirtyByTile(tile + TileDiffXY(1, 1));
 
}
 

	
 
/** Build or relocate the HQ. This depends if the HQ is already built or not
 
 * @param tile tile where the HQ will be built or relocated to
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
extern int32 CheckFlatLandBelow(TileIndex tile, uint w, uint h, uint flags, uint invalid_dirs, int *);
 
int32 CmdBuildCompanyHQ(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Player *p = GetPlayer(_current_player);
 
	int cost;
 
	int32 ret;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_PROPERTY);
 

	
 
	ret = CheckFlatLandBelow(tile, 2, 2, flags, 0, NULL);
 
	if (CmdFailed(ret)) return ret;
 
	cost = ret;
 

	
 
	if (p->location_of_house != 0) { /* Moving HQ */
 
		cost += DestroyCompanyHQ(_current_player, flags);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		int score = UpdateCompanyRatingAndValue(p, false);
 

	
 
		p->location_of_house = tile;
 

	
 
		MakeCompanyHQ(tile, _current_player);
 

	
 
		UpdateCompanyHQ(p, score);
 
		InvalidateWindow(WC_COMPANY, p->index);
 
	}
 

	
 
	return cost;
 
}
 

	
 
static void DrawTile_Unmovable(TileInfo *ti)
 
{
 
	uint32 image, ormod;
 

	
 
	switch (GetUnmovableType(ti->tile)) {
 
		case UNMOVABLE_TRANSMITTER:
 
		case UNMOVABLE_LIGHTHOUSE: {
 
			const DrawTileUnmovableStruct* dtus;
 

	
 
			if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh);
 
			DrawClearLandTile(ti, 2);
 

	
 
			dtus = &_draw_tile_unmovable_data[GetUnmovableType(ti->tile)];
 

	
 
			image = dtus->image;
 
			if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 

	
 
			AddSortableSpriteToDraw(
 
				image, ti->x | dtus->subcoord_x, ti->y | dtus->subcoord_y,
 
				dtus->width, dtus->height, dtus->z_size, ti->z
 
			);
 
			break;
 
		}
 

	
 
		case UNMOVABLE_STATUE:
 
			DrawGroundSprite(SPR_CONCRETE_GROUND);
 

	
 
			image = PLAYER_SPRITE_COLOR(GetTileOwner(ti->tile));
 
			image += PALETTE_MODIFIER_COLOR | SPR_STATUE_COMPANY;
 
			if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 
			AddSortableSpriteToDraw(image, ti->x, ti->y, 16, 16, 25, ti->z);
 
			break;
 

	
 
		case UNMOVABLE_OWNED_LAND:
 
			DrawClearLandTile(ti, 0);
 

	
 
			AddSortableSpriteToDraw(
 
				PLAYER_SPRITE_COLOR(GetTileOwner(ti->tile)) + PALETTE_MODIFIER_COLOR + SPR_BOUGHT_LAND,
 
				ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2, 1, 1, 10, GetSlopeZ(ti->x + TILE_SIZE / 2, ti->y + TILE_SIZE / 2)
 
			);
 
			DrawBridgeMiddle(ti);
 
			break;
 

	
 
		default: {
 
			const DrawTileSeqStruct* dtss;
 
			const DrawTileSprites* t;
 

	
 
			assert(IsCompanyHQ(ti->tile));
 
			if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh);
 

	
 
			ormod = PLAYER_SPRITE_COLOR(GetTileOwner(ti->tile));
 

	
 
			t = &_unmovable_display_datas[GetCompanyHQSection(ti->tile)];
 
			DrawGroundSprite(t->ground_sprite | ormod);
 

	
 
			foreach_draw_tile_seq(dtss, t->seq) {
 
				image = dtss->image;
 
				if (_display_opt & DO_TRANS_BUILDINGS) {
 
					MAKE_TRANSPARENT(image);
 
				} else {
 
					image |= ormod;
 
				}
 
				AddSortableSpriteToDraw(
 
					image,
 
					ti->x + dtss->delta_x, ti->y + dtss->delta_y,
 
					dtss->size_x, dtss->size_y,
 
					dtss->size_z, ti->z + dtss->delta_z
 
				);
 
			}
 
			break;
 
		}
 
	}
 
}
 

	
 
static uint GetSlopeZ_Unmovable(TileIndex tile, uint x, uint y)
 
{
 
	if (IsOwnedLand(tile)) {
 
		uint z;
 
		uint tileh = GetTileSlope(tile, &z);
 

	
 
		return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
 
	} else {
 
		return GetTileMaxZ(tile);
 
	}
 
}
 

	
 
static Slope GetSlopeTileh_Unmovable(TileIndex tile, Slope tileh)
 
{
 
	return IsOwnedLand(tile) ? tileh : SLOPE_FLAT;
 
}
 

	
 
static int32 ClearTile_Unmovable(TileIndex tile, byte flags)
 
{
 
	if (IsCompanyHQ(tile)) {
 
		if (_current_player == OWNER_WATER) {
 
			return DestroyCompanyHQ(GetTileOwner(tile), DC_EXEC);
 
		} else {
 
			return_cmd_error(STR_5804_COMPANY_HEADQUARTERS_IN);
 
		}
 
	}
 

	
 
	if (IsOwnedLand(tile)) {
 
		return DoCommand(tile, 0, 0, flags, CMD_SELL_LAND_AREA);
 
	}
 

	
 
	// checks if you're allowed to remove unmovable things
 
	if (_game_mode != GM_EDITOR && _current_player != OWNER_WATER && ((flags & DC_AUTO || !_cheats.magic_bulldozer.value)) )
 
		return_cmd_error(STR_5800_OBJECT_IN_THE_WAY);
 

	
 
	if (flags & DC_EXEC) {
 
		DoClearSquare(tile);
 
	}
 

	
 
	return 0;
 
}
 

	
 
static void GetAcceptedCargo_Unmovable(TileIndex tile, AcceptedCargo ac)
 
{
 
	uint level; // HQ level (depends on company performance) in the range 1..5.
 

	
 
	if (!IsCompanyHQ(tile)) return;
 

	
 
	/* HQ accepts passenger and mail; but we have to divide the values
 
	 * between 4 tiles it occupies! */
 

	
 
	level = GetCompanyHQSize(tile) + 1;
 

	
 
	// Top town building generates 10, so to make HQ interesting, the top
 
	// type makes 20.
 
	ac[CT_PASSENGERS] = max(1, level);
 

	
 
	// Top town building generates 4, HQ can make up to 8. The
 
	// proportion passengers:mail is different because such a huge
 
	// commercial building generates unusually high amount of mail
 
	// correspondence per physical visitor.
 
	ac[CT_MAIL] = max(1, level / 2);
 
}
 

	
 

	
 
static void GetTileDesc_Unmovable(TileIndex tile, TileDesc *td)
 
{
 
	switch (GetUnmovableType(tile)) {
 
		case UNMOVABLE_TRANSMITTER: td->str = STR_5801_TRANSMITTER; break;
 
		case UNMOVABLE_LIGHTHOUSE:  td->str = STR_5802_LIGHTHOUSE; break;
 
		case UNMOVABLE_STATUE:      td->str = STR_2016_STATUE; break;
 
		case UNMOVABLE_OWNED_LAND:  td->str = STR_5805_COMPANY_OWNED_LAND; break;
 
		default:                    td->str = STR_5803_COMPANY_HEADQUARTERS; break;
 
	}
 
	td->owner = GetTileOwner(tile);
 
}
 

	
 
static void AnimateTile_Unmovable(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static void TileLoop_Unmovable(TileIndex tile)
 
{
 
	uint level; // HQ level (depends on company performance) in the range 1..5.
 
	uint32 r;
 

	
 
	if (!IsCompanyHQ(tile)) return;
 

	
 
	/* HQ accepts passenger and mail; but we have to divide the values
 
	 * between 4 tiles it occupies! */
 

	
 
	level = GetCompanyHQSize(tile) + 1;
 
	assert(level < 6);
 

	
 
	r = Random();
 
	// Top town buildings generate 250, so the top HQ type makes 256.
 
	if (GB(r, 0, 8) < (256 / 4 / (6 - level))) {
 
		uint amt = GB(r, 0, 8) / 8 / 4 + 1;
 
		if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
 
		MoveGoodsToStation(tile, 2, 2, CT_PASSENGERS, amt);
 
	}
 

	
 
	// Top town building generates 90, HQ can make up to 196. The
 
	// proportion passengers:mail is about the same as in the acceptance
 
	// equations.
 
	if (GB(r, 8, 8) < (196 / 4 / (6 - level))) {
 
		uint amt = GB(r, 8, 8) / 8 / 4 + 1;
 
		if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
 
		MoveGoodsToStation(tile, 2, 2, CT_MAIL, amt);
 
	}
 
}
 

	
 

	
 
static uint32 GetTileTrackStatus_Unmovable(TileIndex tile, TransportType mode)
 
{
 
	return 0;
 
}
 

	
 
static void ClickTile_Unmovable(TileIndex tile)
 
{
 
	if (IsCompanyHQ(tile)) ShowPlayerCompany(GetTileOwner(tile));
 
}
 

	
 

	
 
/* checks, if a radio tower is within a 9x9 tile square around tile */
 
static bool IsRadioTowerNearby(TileIndex tile)
 
{
 
	TileIndex tile_s = tile - TileDiffXY(4, 4);
 

	
 
	BEGIN_TILE_LOOP(tile, 9, 9, tile_s)
 
		if (IsTransmitterTile(tile)) return true;
 
	END_TILE_LOOP(tile, 9, 9, tile_s)
 

	
 
	return false;
 
}
 

	
 
void GenerateUnmovables(void)
 
{
 
	int i, li, j, loop_count;
 
	TileIndex tile;
 
	uint h;
 
	uint maxx;
 
	uint maxy;
 

	
 
	if (_opt.landscape == LT_CANDY) return;
 

	
 
	/* add radio tower */
 
	i = ScaleByMapSize(1000);
 
	j = ScaleByMapSize(15); // maximum number of radio towers on the map
 
	li = ScaleByMapSize1D((Random() & 3) + 7);
 
	SetGeneratingWorldProgress(GWP_UNMOVABLE, j + li);
 

	
 
	do {
 
		tile = RandomTile();
 
		if (IsTileType(tile, MP_CLEAR) && GetTileSlope(tile, &h) == SLOPE_FLAT && h >= TILE_HEIGHT * 4) {
 
			if (IsRadioTowerNearby(tile)) continue;
 
			MakeTransmitter(tile);
 
			IncreaseGeneratingWorldProgress(GWP_UNMOVABLE);
 
			if (--j == 0) break;
 
		}
 
	} while (--i);
 

	
 
	if (_opt.landscape == LT_DESERT) return;
 

	
 
	/* add lighthouses */
 
	i = li;
 
	maxx = MapMaxX();
 
	maxy = MapMaxY();
 
	loop_count = 0;
 
	do {
 
		uint32 r;
 
		DiagDirection dir;
 
		int perimeter;
 

	
 
restart:
 
		/* Avoid infinite loops */
 
		if (++loop_count > 1000) break;
 

	
 
		r = Random();
 

	
 
		/* Scatter the lighthouses more evenly around the perimeter */
 
		perimeter = (GB(r, 16, 16) % (2 * (maxx + maxy))) - maxy;
 
		for (dir = DIAGDIR_NE; perimeter > 0; dir++) {
 
			perimeter -= (DiagDirToAxis(dir) == AXIS_X) ? maxx : maxy;
 
		}
 

	
 
		switch (dir) {
 
			default:
 
			case DIAGDIR_NE: tile = TileXY(maxx,     r % maxy); break;
 
			case DIAGDIR_SE: tile = TileXY(r % maxx, 0);        break;
 
			case DIAGDIR_SW: tile = TileXY(0,        r % maxy); break;
 
			case DIAGDIR_NW: tile = TileXY(r % maxx, maxy);     break;
 
		}
 
		j = 20;
 
		do {
 
			if (--j == 0) goto restart;
 
			tile = TILE_MASK(tile + TileOffsByDiagDir(dir));
 
		} while (!(IsTileType(tile, MP_CLEAR) && GetTileSlope(tile, &h) == SLOPE_FLAT && h <= TILE_HEIGHT * 2));
 

	
 
		assert(tile == TILE_MASK(tile));
 

	
 
		MakeLighthouse(tile);
 
		IncreaseGeneratingWorldProgress(GWP_UNMOVABLE);
 
	} while (--i);
 
}
 

	
 
static void ChangeTileOwner_Unmovable(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	if (!IsTileOwner(tile, old_player)) return;
 

	
 
	if (IsOwnedLand(tile) && new_player != PLAYER_SPECTATOR) {
 
		SetTileOwner(tile, new_player);
 
	} else {
 
		DoClearSquare(tile);
 
	}
 
}
 

	
 
const TileTypeProcs _tile_type_unmovable_procs = {
 
	DrawTile_Unmovable,             /* draw_tile_proc */
 
	GetSlopeZ_Unmovable,            /* get_slope_z_proc */
 
	ClearTile_Unmovable,            /* clear_tile_proc */
 
	GetAcceptedCargo_Unmovable,     /* get_accepted_cargo_proc */
 
	GetTileDesc_Unmovable,          /* get_tile_desc_proc */
 
	GetTileTrackStatus_Unmovable,   /* get_tile_track_status_proc */
 
	ClickTile_Unmovable,            /* click_tile_proc */
 
	AnimateTile_Unmovable,          /* animate_tile_proc */
 
	TileLoop_Unmovable,             /* tile_loop_clear */
 
	ChangeTileOwner_Unmovable,      /* change_tile_owner_clear */
 
	NULL,                           /* get_produced_cargo_proc */
 
	NULL,                           /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_Unmovable,        /* get_slope_tileh_proc */
 
};
src/vehicle.c
Show inline comments
 
deleted file
src/vehicle.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "road_map.h"
 
#include "roadveh.h"
 
#include "ship.h"
 
#include "spritecache.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "tile.h"
 
#include "vehicle.h"
 
#include "gfx.h"
 
#include "viewport.h"
 
#include "news.h"
 
#include "command.h"
 
#include "saveload.h"
 
#include "player.h"
 
#include "engine.h"
 
#include "sound.h"
 
#include "debug.h"
 
#include "vehicle_gui.h"
 
#include "depot.h"
 
#include "station.h"
 
#include "rail.h"
 
#include "train.h"
 
#include "aircraft.h"
 
#include "industry_map.h"
 
#include "station_map.h"
 
#include "water_map.h"
 
#include "network/network.h"
 
#include "yapf/yapf.h"
 
#include "date.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_sound.h"
 

	
 
#define INVALID_COORD (-0x8000)
 
#define GEN_HASH(x, y) ((GB((y), 6, 6) << 6) + GB((x), 7, 6))
 

	
 
/*
 
 * These command macros are used to call vehicle type specific commands with non type specific commands
 
 * it should be used like: DoCommandP(x, y, p1, p2, flags, CMD_STARTSTOP_VEH(v->type))
 
 * that line will start/stop a vehicle nomatter what type it is
 
 * VEH_Train is used as an offset because the vehicle type values doesn't start with 0
 
 */
 

	
 
#define CMD_BUILD_VEH(x) _veh_build_proc_table[ x - VEH_Train]
 
#define CMD_SELL_VEH(x)  _veh_sell_proc_table [ x - VEH_Train]
 
#define CMD_REFIT_VEH(x) _veh_refit_proc_table[ x - VEH_Train]
 

	
 
static const uint32 _veh_build_proc_table[] = {
 
	CMD_BUILD_RAIL_VEHICLE,
 
	CMD_BUILD_ROAD_VEH,
 
	CMD_BUILD_SHIP,
 
	CMD_BUILD_AIRCRAFT,
 
};
 
static const uint32 _veh_sell_proc_table[] = {
 
	CMD_SELL_RAIL_WAGON,
 
	CMD_SELL_ROAD_VEH,
 
	CMD_SELL_SHIP,
 
	CMD_SELL_AIRCRAFT,
 
};
 

	
 
static const uint32 _veh_refit_proc_table[] = {
 
	CMD_REFIT_RAIL_VEHICLE,
 
	CMD_REFIT_ROAD_VEH,
 
	CMD_REFIT_SHIP,
 
	CMD_REFIT_AIRCRAFT,
 
};
 

	
 
const uint32 _send_to_depot_proc_table[] = {
 
	CMD_SEND_TRAIN_TO_DEPOT,
 
	CMD_SEND_ROADVEH_TO_DEPOT,
 
	CMD_SEND_SHIP_TO_DEPOT,
 
	CMD_SEND_AIRCRAFT_TO_HANGAR,
 
};
 

	
 

	
 
enum {
 
	BLOCKS_FOR_SPECIAL_VEHICLES   = 2, ///< Blocks needed for special vehicles
 
};
 

	
 
/**
 
 * Called if a new block is added to the vehicle-pool
 
 */
 
static void VehiclePoolNewBlock(uint start_item)
 
{
 
	Vehicle *v;
 

	
 
	/* 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 (v = GetVehicle(start_item); v != NULL; v = (v->index + 1U < GetVehiclePoolSize()) ? GetVehicle(v->index + 1) : NULL) v->index = start_item++;
 
}
 

	
 
/* Initialize the vehicle-pool */
 
DEFINE_OLD_POOL(Vehicle, Vehicle, VehiclePoolNewBlock, NULL)
 

	
 
void VehicleServiceInDepot(Vehicle *v)
 
{
 
	v->date_of_last_service = _date;
 
	v->breakdowns_since_last_service = 0;
 
	v->reliability = GetEngine(v->engine_type)->reliability;
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
 
}
 

	
 
bool VehicleNeedsService(const Vehicle *v)
 
{
 
	if (v->vehstatus & VS_CRASHED)
 
		return false; /* Crashed vehicles don't need service anymore */
 

	
 
	if (_patches.no_servicing_if_no_breakdowns && _opt.diff.vehicle_breakdowns == 0) {
 
		return EngineHasReplacementForPlayer(GetPlayer(v->owner), v->engine_type);  /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off */
 
	}
 

	
 
	return _patches.servint_ispercent ?
 
		(v->reliability < GetEngine(v->engine_type)->reliability * (100 - v->service_interval) / 100) :
 
		(v->date_of_last_service + v->service_interval < _date);
 
}
 

	
 
StringID VehicleInTheWayErrMsg(const Vehicle* v)
 
{
 
	switch (v->type) {
 
		case VEH_Train:    return STR_8803_TRAIN_IN_THE_WAY;
 
		case VEH_Road:     return STR_9000_ROAD_VEHICLE_IN_THE_WAY;
 
		case VEH_Aircraft: return STR_A015_AIRCRAFT_IN_THE_WAY;
 
		default:           return STR_980E_SHIP_IN_THE_WAY;
 
	}
 
}
 

	
 
static void *EnsureNoVehicleProc(Vehicle *v, void *data)
 
{
 
	if (v->tile != *(const TileIndex*)data || v->type == VEH_Disaster)
 
		return NULL;
 

	
 
	_error_message = VehicleInTheWayErrMsg(v);
 
	return v;
 
}
 

	
 
bool EnsureNoVehicle(TileIndex tile)
 
{
 
	return VehicleFromPos(tile, &tile, EnsureNoVehicleProc) == NULL;
 
}
 

	
 
static void *EnsureNoVehicleProcZ(Vehicle *v, void *data)
 
{
 
	const TileInfo *ti = data;
 

	
 
	if (v->tile != ti->tile || v->type == VEH_Disaster) return NULL;
 
	if (v->z_pos > ti->z) return NULL;
 

	
 
	_error_message = VehicleInTheWayErrMsg(v);
 
	return v;
 
}
 

	
 

	
 
bool EnsureNoVehicleOnGround(TileIndex tile)
 
{
 
	TileInfo ti;
 

	
 
	ti.tile = tile;
 
	ti.z = GetTileMaxZ(tile);
 
	return VehicleFromPos(tile, &ti, EnsureNoVehicleProcZ) == NULL;
 
}
 

	
 
Vehicle *FindVehicleOnTileZ(TileIndex tile, byte z)
 
{
 
	TileInfo ti;
 

	
 
	ti.tile = tile;
 
	ti.z = z;
 

	
 
	return VehicleFromPos(tile, &ti, EnsureNoVehicleProcZ);
 
}
 

	
 
Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z)
 
{
 
	int x1 = TileX(from);
 
	int y1 = TileY(from);
 
	int x2 = TileX(to);
 
	int y2 = TileY(to);
 
	Vehicle *veh;
 

	
 
	/* Make sure x1 < x2 or y1 < y2 */
 
	if (x1 > x2 || y1 > y2) {
 
		intswap(x1,x2);
 
		intswap(y1,y2);
 
	}
 
	FOR_ALL_VEHICLES(veh) {
 
		if ((veh->type == VEH_Train || veh->type == VEH_Road) && (z==0xFF || veh->z_pos == z)) {
 
			if ((veh->x_pos>>4) >= x1 && (veh->x_pos>>4) <= x2 &&
 
					(veh->y_pos>>4) >= y1 && (veh->y_pos>>4) <= y2) {
 
				return veh;
 
			}
 
		}
 
	}
 
	return NULL;
 
}
 

	
 

	
 
static void UpdateVehiclePosHash(Vehicle* v, int x, int y);
 

	
 
void VehiclePositionChanged(Vehicle *v)
 
{
 
	int img = v->cur_image;
 
	Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
 
	const Sprite* spr = GetSprite(img);
 

	
 
	pt.x += spr->x_offs;
 
	pt.y += spr->y_offs;
 

	
 
	UpdateVehiclePosHash(v, pt.x, pt.y);
 

	
 
	v->left_coord = pt.x;
 
	v->top_coord = pt.y;
 
	v->right_coord = pt.x + spr->width + 2;
 
	v->bottom_coord = pt.y + spr->height + 2;
 
}
 

	
 
// Called after load to update coordinates
 
void AfterLoadVehicles(void)
 
{
 
	Vehicle *v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		v->first = NULL;
 
		if (v->type == VEH_Train) v->u.rail.first_engine = INVALID_ENGINE;
 
	}
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Train && (IsFrontEngine(v) || IsFreeWagon(v)))
 
			TrainConsistChanged(v);
 
	}
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		switch (v->type) {
 
			case VEH_Train: v->cur_image = GetTrainImage(v, v->direction); break;
 
			case VEH_Road: v->cur_image = GetRoadVehImage(v, v->direction); break;
 
			case VEH_Ship: v->cur_image = GetShipImage(v, v->direction); break;
 
			case VEH_Aircraft:
 
				if (v->subtype == 0 || v->subtype == 2) {
 
					v->cur_image = GetAircraftImage(v, v->direction);
 
					if (v->next != NULL) v->next->cur_image = v->cur_image;
 
				}
 
				break;
 
			default: break;
 
		}
 

	
 
		v->left_coord = INVALID_COORD;
 
		VehiclePositionChanged(v);
 
	}
 
}
 

	
 
static Vehicle *InitializeVehicle(Vehicle *v)
 
{
 
	VehicleID index = v->index;
 
	memset(v, 0, sizeof(Vehicle));
 
	v->index = index;
 

	
 
	assert(v->orders == NULL);
 

	
 
	v->left_coord = INVALID_COORD;
 
	v->first = NULL;
 
	v->next = NULL;
 
	v->next_hash = NULL;
 
	v->string_id = 0;
 
	v->next_shared = NULL;
 
	v->prev_shared = NULL;
 
	v->depot_list  = NULL;
 
	v->random_bits = 0;
 
	return v;
 
}
 

	
 
/**
 
 * Get a value for a vehicle's random_bits.
 
 * @return A random value from 0 to 255.
 
 */
 
byte VehicleRandomBits(void)
 
{
 
	return GB(Random(), 0, 8);
 
}
 

	
 
Vehicle *ForceAllocateSpecialVehicle(void)
 
{
 
	/* This stays a strange story.. there should always be room for special
 
	 * vehicles (special effects all over the map), but with 65k of vehicles
 
	 * is this realistic to double-check for that? For now we just reserve
 
	 * BLOCKS_FOR_SPECIAL_VEHICLES times block_size vehicles that may only
 
	 * be used for special vehicles.. should work nicely :) */
 

	
 
	Vehicle *v;
 

	
 
	/* 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 (v = GetVehicle(0); v != NULL; v = (v->index + 1U < GetVehiclePoolSize()) ? GetVehicle(v->index + 1) : NULL) {
 
		/* No more room for the special vehicles, return NULL */
 
		if (v->index >= (1 << Vehicle_POOL_BLOCK_SIZE_BITS) * BLOCKS_FOR_SPECIAL_VEHICLES)
 
			return NULL;
 

	
 
		if (!IsValidVehicle(v)) return InitializeVehicle(v);
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/*
 
 * finds a free vehicle in the memory or allocates a new one
 
 * returns a pointer to the first free vehicle or NULL if all vehicles are in use
 
 * *skip_vehicles is an offset to where in the array we should begin looking
 
 * this is to avoid looping though the same vehicles more than once after we learned that they are not free
 
 * this feature is used by AllocateVehicles() since it need to allocate more than one and when
 
 * another block is added to _Vehicle_pool, since we only do that when we know it's already full
 
 */
 
static Vehicle *AllocateSingleVehicle(VehicleID *skip_vehicles)
 
{
 
	/* See note by ForceAllocateSpecialVehicle() why we skip the
 
	 * first blocks */
 
	Vehicle *v;
 
	const int offset = (1 << Vehicle_POOL_BLOCK_SIZE_BITS) * BLOCKS_FOR_SPECIAL_VEHICLES;
 

	
 
	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
 
	 * TODO - This is just a temporary stage, this will be removed. */
 
	if (*skip_vehicles < (_Vehicle_pool.total_items - offset)) { // make sure the offset in the array is not larger than the array itself
 
		for (v = GetVehicle(offset + *skip_vehicles); v != NULL; v = (v->index + 1U < GetVehiclePoolSize()) ? GetVehicle(v->index + 1) : NULL) {
 
			(*skip_vehicles)++;
 
			if (!IsValidVehicle(v)) return InitializeVehicle(v);
 
		}
 
	}
 

	
 
	/* Check if we can add a block to the pool */
 
	if (AddBlockToPool(&_Vehicle_pool))
 
		return AllocateSingleVehicle(skip_vehicles);
 

	
 
	return NULL;
 
}
 

	
 

	
 
Vehicle *AllocateVehicle(void)
 
{
 
	VehicleID counter = 0;
 
	return AllocateSingleVehicle(&counter);
 
}
 

	
 

	
 
/** Allocates a lot of vehicles and frees them again
 
 * @param vl pointer to an array of vehicles to get allocated. Can be NULL if the vehicles aren't needed (makes it test only)
 
 * @param num number of vehicles to allocate room for
 
 * @return true if there is room to allocate all the vehicles
 
 */
 
bool AllocateVehicles(Vehicle **vl, int num)
 
{
 
	int i;
 
	Vehicle *v;
 
	VehicleID counter = 0;
 

	
 
	for (i = 0; i != num; i++) {
 
		v = AllocateSingleVehicle(&counter);
 
		if (v == NULL) {
 
			return false;
 
		}
 
		if (vl != NULL) {
 
			vl[i] = v;
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 

	
 
static Vehicle *_vehicle_position_hash[0x1000];
 

	
 
void *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
 
{
 
	Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, 0);
 

	
 
	// The hash area to scan
 
	const int xl = GB(pt.x - 174, 7, 6);
 
	const int xu = GB(pt.x + 104, 7, 6);
 
	const int yl = GB(pt.y - 294, 6, 6) << 6;
 
	const int yu = GB(pt.y +  56, 6, 6) << 6;
 

	
 
	int x;
 
	int y;
 

	
 
	for (y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
 
		for (x = xl;; x = (x + 1) & 0x3F) {
 
			Vehicle *v = _vehicle_position_hash[(x + y) & 0xFFFF];
 

	
 
			while (v != NULL) {
 
				void* a = proc(v, data);
 

	
 
				if (a != NULL) return a;
 
				v = v->next_hash;
 
			}
 

	
 
			if (x == xu) break;
 
		}
 

	
 
		if (y == yu) break;
 
	}
 
	return NULL;
 
}
 

	
 

	
 
static void UpdateVehiclePosHash(Vehicle* v, int x, int y)
 
{
 
	Vehicle **old_hash, **new_hash;
 
	int old_x = v->left_coord;
 
	int old_y = v->top_coord;
 

	
 
	new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(x,y)];
 
	old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_position_hash[GEN_HASH(old_x, old_y)];
 

	
 
	if (old_hash == new_hash) return;
 

	
 
	/* remove from hash table? */
 
	if (old_hash != NULL) {
 
		Vehicle *last = NULL;
 
		Vehicle *u = *old_hash;
 
		while (u != v) {
 
			last = u;
 
			u = u->next_hash;
 
			assert(u != NULL);
 
		}
 

	
 
		if (last == NULL) {
 
			*old_hash = v->next_hash;
 
		} else {
 
			last->next_hash = v->next_hash;
 
		}
 
	}
 

	
 
	/* insert into hash table? */
 
	if (new_hash != NULL) {
 
		v->next_hash = *new_hash;
 
		*new_hash = v;
 
	}
 
}
 

	
 
void ResetVehiclePosHash(void)
 
{
 
	memset(_vehicle_position_hash, 0, sizeof(_vehicle_position_hash));
 
}
 

	
 
void InitializeVehicles(void)
 
{
 
	uint i;
 

	
 
	/* Clean the vehicle pool, and reserve enough blocks
 
	 *  for the special vehicles, plus one for all the other
 
	 *  vehicles (which is increased on-the-fly) */
 
	CleanPool(&_Vehicle_pool);
 
	AddBlockToPool(&_Vehicle_pool);
 
	for (i = 0; i < BLOCKS_FOR_SPECIAL_VEHICLES; i++) {
 
		AddBlockToPool(&_Vehicle_pool);
 
	}
 

	
 
	ResetVehiclePosHash();
 
}
 

	
 
Vehicle *GetLastVehicleInChain(Vehicle *v)
 
{
 
	while (v->next != NULL) v = v->next;
 
	return v;
 
}
 

	
 
/** Finds the previous vehicle in a chain, by a brute force search.
 
 * This old function is REALLY slow because it searches through all vehicles to
 
 * find the previous vehicle, but if v->first has not been set, then this function
 
 * will need to be used to find the previous one. This function should never be
 
 * called by anything but GetFirstVehicleInChain
 
 */
 
static Vehicle *GetPrevVehicleInChain_bruteforce(const Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	FOR_ALL_VEHICLES(u) if (u->type == VEH_Train && u->next == v) return u;
 

	
 
	return NULL;
 
}
 

	
 
/** Find the previous vehicle in a chain, by using the v->first cache.
 
 * While this function is fast, it cannot be used in the GetFirstVehicleInChain
 
 * function, otherwise you'll end up in an infinite loop call
 
 */
 
Vehicle *GetPrevVehicleInChain(const Vehicle *v)
 
{
 
	Vehicle *u;
 
	assert(v != NULL);
 

	
 
	u = GetFirstVehicleInChain(v);
 

	
 
	// Check to see if this is the first
 
	if (v == u) return NULL;
 

	
 
	for (; u->next != v; u = u->next) assert(u->next != NULL);
 

	
 
	return u;
 
}
 

	
 
/** Finds the first vehicle in a chain.
 
 * This function reads out the v->first cache. Should the cache be dirty,
 
 * it determines the first vehicle in a chain, and updates the cache.
 
 */
 
Vehicle *GetFirstVehicleInChain(const Vehicle *v)
 
{
 
	Vehicle* u;
 

	
 
	assert(v != NULL);
 

	
 
	if (v->first != NULL) {
 
		if (IsFrontEngine(v->first) || IsFreeWagon(v->first)) return v->first;
 

	
 
		DEBUG(misc, 0, "v->first cache faulty. We shouldn't be here, rebuilding cache!");
 
	}
 

	
 
	/* It is the fact (currently) that newly built vehicles do not have
 
	 * their ->first pointer set. When this is the case, go up to the
 
	 * first engine and set the pointers correctly. Also the first pointer
 
	 * is not saved in a savegame, so this has to be fixed up after loading */
 

	
 
	/* Find the 'locomotive' or the first wagon in a chain */
 
	while ((u = GetPrevVehicleInChain_bruteforce(v)) != NULL) v = u;
 

	
 
	/* Set the first pointer of all vehicles in that chain to the first wagon */
 
	if (IsFrontEngine(v) || IsFreeWagon(v))
 
		for (u = (Vehicle *)v; u != NULL; u = u->next) u->first = (Vehicle *)v;
 

	
 
	return (Vehicle*)v;
 
}
 

	
 
uint CountVehiclesInChain(const Vehicle* v)
 
{
 
	uint count = 0;
 
	do count++; while ((v = v->next) != NULL);
 
	return count;
 
}
 

	
 
/** Check if a vehicle is counted in num_engines in each player struct
 
 * @param *v Vehicle to test
 
 * @return true if the vehicle is counted in num_engines
 
 */
 
bool IsEngineCountable(const Vehicle *v)
 
{
 
	switch (v->type) {
 
		case VEH_Aircraft: return (v->subtype <= 2); // don't count plane shadows and helicopter rotors
 
		case VEH_Train:
 
			return !IsArticulatedPart(v) && // tenders and other articulated parts
 
			(!IsMultiheaded(v) || IsTrainEngine(v)); // rear parts of multiheaded engines
 
		case VEH_Road:
 
		case VEH_Ship:
 
			return true;
 
		default: return false; // Only count player buildable vehicles
 
	}
 
}
 

	
 
void DestroyVehicle(Vehicle *v)
 
{
 
	if (IsEngineCountable(v)) GetPlayer(v->owner)->num_engines[v->engine_type]--;
 

	
 
	DeleteVehicleNews(v->index, INVALID_STRING_ID);
 

	
 
	DeleteName(v->string_id);
 
	if (v->type == VEH_Road) ClearSlot(v);
 

	
 
	if (v->type != VEH_Train || (v->type == VEH_Train && (IsFrontEngine(v) || IsFreeWagon(v)))) {
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	}
 

	
 
	UpdateVehiclePosHash(v, INVALID_COORD, 0);
 
	v->next_hash = NULL;
 
	if (v->orders != NULL) DeleteVehicleOrders(v);
 

	
 
	/* Now remove any artic part. This will trigger an other
 
	 *  destroy vehicle, which on his turn can remove any
 
	 *  other artic parts. */
 
	if (EngineHasArticPart(v)) DeleteVehicle(v->next);
 
}
 

	
 
void DeleteVehicleChain(Vehicle *v)
 
{
 
	do {
 
		Vehicle *u = v;
 
		v = GetNextVehicle(v);
 
		DeleteVehicle(u);
 
	} while (v != NULL);
 
}
 

	
 

	
 
void Aircraft_Tick(Vehicle *v);
 
void RoadVeh_Tick(Vehicle *v);
 
void Ship_Tick(Vehicle *v);
 
void Train_Tick(Vehicle *v);
 
static void EffectVehicle_Tick(Vehicle *v);
 
void DisasterVehicle_Tick(Vehicle *v);
 
static int32 MaybeReplaceVehicle(Vehicle *v, bool check, bool display_costs);
 

	
 
// head of the linked list to tell what vehicles that visited a depot in a tick
 
static Vehicle* _first_veh_in_depot_list;
 

	
 
/** Adds a vehicle to the list of vehicles, that visited a depot this tick
 
 * @param *v vehicle to add
 
 */
 
void VehicleEnteredDepotThisTick(Vehicle *v)
 
{
 
	// we need to set v->leave_depot_instantly as we have no control of it's contents at this time
 
	if (HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT) && !HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS) && v->current_order.type == OT_GOTO_DEPOT) {
 
		// we keep the vehicle in the depot since the user ordered it to stay
 
		v->leave_depot_instantly = false;
 
	} else {
 
		// the vehicle do not plan on stopping in the depot, so we stop it to ensure that it will not reserve the path
 
		// out of the depot before we might autoreplace it to a different engine. The new engine would not own the reserved path
 
		// we store that we stopped the vehicle, so autoreplace can start it again
 
		v->vehstatus |= VS_STOPPED;
 
		v->leave_depot_instantly = true;
 
	}
 

	
 
	if (_first_veh_in_depot_list == NULL) {
 
		_first_veh_in_depot_list = v;
 
	} else {
 
		Vehicle *w = _first_veh_in_depot_list;
 
		while (w->depot_list != NULL) w = w->depot_list;
 
		w->depot_list = v;
 
	}
 
}
 

	
 
typedef void VehicleTickProc(Vehicle*);
 
static VehicleTickProc* _vehicle_tick_procs[] = {
 
	Train_Tick,
 
	RoadVeh_Tick,
 
	Ship_Tick,
 
	Aircraft_Tick,
 
	EffectVehicle_Tick,
 
	DisasterVehicle_Tick,
 
};
 

	
 
void CallVehicleTicks(void)
 
{
 
	Vehicle *v;
 

	
 
#ifdef ENABLE_NETWORK
 
	// hotfix for desync problem:
 
	//  for MP games invalidate the YAPF cache every tick to keep it exactly the same on the server and all clients
 
	if (_networking) {
 
		YapfNotifyTrackLayoutChange(0, 0);
 
	}
 
#endif //ENABLE_NETWORK
 

	
 
	_first_veh_in_depot_list = NULL; // now we are sure it's initialized at the start of each tick
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		_vehicle_tick_procs[v->type - 0x10](v);
 

	
 
		switch (v->type) {
 
			case VEH_Train:
 
			case VEH_Road:
 
			case VEH_Aircraft:
 
			case VEH_Ship:
 
				if (v->type == VEH_Train && IsTrainWagon(v)) continue;
 
				if (v->type == VEH_Aircraft && v->subtype > 0) continue;
 

	
 
				v->motion_counter += (v->direction & 1) ? (v->cur_speed * 3) / 4 : v->cur_speed;
 
				/* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
 
				if (GB(v->motion_counter, 0, 8) < v->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
 

	
 
				/* Play an alterate running sound every 16 ticks */
 
				if (GB(v->tick_counter, 0, 4) == 0) PlayVehicleSound(v, v->cur_speed > 0 ? VSE_RUNNING_16 : VSE_STOPPED_16);
 
		}
 
	}
 

	
 
	// now we handle all the vehicles that entered a depot this tick
 
	v = _first_veh_in_depot_list;
 
	while (v != NULL) {
 
		Vehicle *w = v->depot_list;
 
		v->depot_list = NULL; // it should always be NULL at the end of each tick
 
		MaybeReplaceVehicle(v, false, true);
 
		v = w;
 
	}
 
}
 

	
 
static bool CanFillVehicle_FullLoadAny(Vehicle *v)
 
{
 
	uint32 full = 0, not_full = 0;
 
	bool keep_loading = false;
 
	const GoodsEntry *ge = GetStation(v->last_station_visited)->goods;
 

	
 
	//special handling of aircraft
 

	
 
	//if the aircraft carries passengers and is NOT full, then
 
	//continue loading, no matter how much mail is in
 
	if (v->type == VEH_Aircraft &&
 
			v->cargo_type == CT_PASSENGERS &&
 
			v->cargo_cap != v->cargo_count) {
 
		return true;
 
	}
 

	
 
	// patch should return "true" to continue loading, i.e. when there is no cargo type that is fully loaded.
 
	do {
 
		//Should never happen, but just in case future additions change this
 
		assert(v->cargo_type<32);
 

	
 
		if (v->cargo_cap != 0) {
 
			uint32 mask = 1 << v->cargo_type;
 

	
 
			if (v->cargo_cap == v->cargo_count) {
 
				full |= mask;
 
			} else if (GB(ge[v->cargo_type].waiting_acceptance, 0, 12) > 0 ||
 
					(HASBIT(v->load_status, LS_CARGO_UNLOADING) && (ge[v->cargo_type].waiting_acceptance & 0x8000))) {
 
				/* If there is any cargo waiting, or this vehicle is still unloading
 
				 * and the station accepts the cargo, don't leave the station. */
 
				keep_loading = true;
 
			} else {
 
				not_full |= mask;
 
			}
 
		}
 
	} while ((v = v->next) != NULL);
 

	
 
	// continue loading if there is a non full cargo type and no cargo type that is full
 
	return keep_loading || (not_full && (full & ~not_full) == 0);
 
}
 

	
 
bool CanFillVehicle(Vehicle *v)
 
{
 
	TileIndex tile = v->tile;
 

	
 
	if (IsTileType(tile, MP_STATION) ||
 
			(v->type == VEH_Ship && (
 
				IsTileType(TILE_ADDXY(tile,  1,  0), MP_STATION) ||
 
				IsTileType(TILE_ADDXY(tile, -1,  0), MP_STATION) ||
 
				IsTileType(TILE_ADDXY(tile,  0,  1), MP_STATION) ||
 
				IsTileType(TILE_ADDXY(tile,  0, -1), MP_STATION) ||
 
				IsTileType(TILE_ADDXY(tile, -2,  0), MP_STATION)
 
			))) {
 

	
 
		// If patch is active, use alternative CanFillVehicle-function
 
		if (_patches.full_load_any && v->current_order.flags & OF_FULL_LOAD) return CanFillVehicle_FullLoadAny(v);
 

	
 
		do {
 
			if (v->cargo_count != v->cargo_cap) return true;
 
		} while ((v = v->next) != NULL);
 
	}
 
	return false;
 
}
 

	
 
/** Check if a given engine type can be refitted to a given cargo
 
 * @param engine_type Engine type to check
 
 * @param cid_to check refit to this cargo-type
 
 * @return true if it is possible, false otherwise
 
 */
 
bool CanRefitTo(EngineID engine_type, CargoID cid_to)
 
{
 
	CargoID cid = _global_cargo_id[_opt_ptr->landscape][cid_to];
 
	return HASBIT(EngInfo(engine_type)->refit_mask, cid);
 
}
 

	
 
/** Find the first cargo type that an engine can be refitted to.
 
 * @param engine Which engine to find cargo for.
 
 * @return A climate dependent cargo type. CT_INVALID is returned if not refittable.
 
 */
 
CargoID FindFirstRefittableCargo(EngineID engine_type)
 
{
 
	CargoID cid;
 
	uint32 refit_mask = EngInfo(engine_type)->refit_mask;
 

	
 
	if (refit_mask != 0) {
 
		for (cid = CT_PASSENGERS; cid < NUM_CARGO; cid++) {
 
			if (HASBIT(refit_mask, _global_cargo_id[_opt_ptr->landscape][cid])) return cid;
 
		}
 
	}
 

	
 
	return CT_INVALID;
 
}
 

	
 
/** Learn the price of refitting a certain engine
 
* @param engine Which engine to refit
 
* @return Price for refitting
 
*/
 
int32 GetRefitCost(EngineID engine_type)
 
{
 
	int32 base_cost = 0;
 

	
 
	switch (GetEngine(engine_type)->type) {
 
		case VEH_Ship: base_cost = _price.ship_base; break;
 
		case VEH_Road: base_cost = _price.roadveh_base; break;
 
		case VEH_Aircraft: base_cost = _price.aircraft_base; break;
 
		case VEH_Train:
 
			base_cost = 2 * ((RailVehInfo(engine_type)->flags & RVI_WAGON) ?
 
							 _price.build_railwagon : _price.build_railvehicle);
 
			break;
 
		default: NOT_REACHED(); break;
 
	}
 
	return (EngInfo(engine_type)->refit_cost * base_cost) >> 10;
 
}
 

	
 
static void DoDrawVehicle(const Vehicle *v)
 
{
 
	uint32 image = v->cur_image;
 

	
 
	if (v->vehstatus & VS_SHADOW) {
 
		MAKE_TRANSPARENT(image);
 
	} else if (v->vehstatus & VS_DEFPAL) {
 
		image |= (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
 
	}
 

	
 
	AddSortableSpriteToDraw(image, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
 
		v->sprite_width, v->sprite_height, v->z_height, v->z_pos);
 
}
 

	
 
void ViewportAddVehicles(DrawPixelInfo *dpi)
 
{
 
	// The bounding rectangle
 
	const int l = dpi->left;
 
	const int r = dpi->left + dpi->width;
 
	const int t = dpi->top;
 
	const int b = dpi->top + dpi->height;
 

	
 
	// The hash area to scan
 
	const int xl = GB(l - 70, 7, 6);
 
	const int xu = GB(r,      7, 6);
 
	const int yl = GB(t - 70, 6, 6) << 6;
 
	const int yu = GB(b,      6, 6) << 6;
 

	
 
	int x;
 
	int y;
 

	
 
	for (y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
 
		for (x = xl;; x = (x + 1) & 0x3F) {
 
			const Vehicle *v = _vehicle_position_hash[(x + y) & 0xFFFF];
 

	
 
			while (v != NULL) {
 
				if (!(v->vehstatus & VS_HIDDEN) &&
 
						l <= v->right_coord &&
 
						t <= v->bottom_coord &&
 
						r >= v->left_coord &&
 
						b >= v->top_coord) {
 
					DoDrawVehicle(v);
 
				}
 
				v = v->next_hash;
 
			}
 

	
 
			if (x == xu) break;
 
		}
 

	
 
		if (y == yu) break;
 
	}
 
}
 

	
 
static void ChimneySmokeInit(Vehicle *v)
 
{
 
	uint32 r = Random();
 
	v->cur_image = SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3);
 
	v->progress = GB(r, 16, 3);
 
}
 

	
 
static void ChimneySmokeTick(Vehicle *v)
 
{
 
	if (v->progress > 0) {
 
		v->progress--;
 
	} else {
 
		TileIndex tile;
 

	
 
		BeginVehicleMove(v);
 

	
 
		tile = TileVirtXY(v->x_pos, v->y_pos);
 
		if (!IsTileType(tile, MP_INDUSTRY)) {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
			return;
 
		}
 

	
 
		if (v->cur_image != SPR_CHIMNEY_SMOKE_7) {
 
			v->cur_image++;
 
		} else {
 
			v->cur_image = SPR_CHIMNEY_SMOKE_0;
 
		}
 
		v->progress = 7;
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void SteamSmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_STEAM_SMOKE_0;
 
	v->progress = 12;
 
}
 

	
 
static void SteamSmokeTick(Vehicle *v)
 
{
 
	bool moved = false;
 

	
 
	BeginVehicleMove(v);
 

	
 
	v->progress++;
 

	
 
	if ((v->progress & 7) == 0) {
 
		v->z_pos++;
 
		moved = true;
 
	}
 

	
 
	if ((v->progress & 0xF) == 4) {
 
		if (v->cur_image != SPR_STEAM_SMOKE_4) {
 
			v->cur_image++;
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
			return;
 
		}
 
		moved = true;
 
	}
 

	
 
	if (moved) {
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void DieselSmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_DIESEL_SMOKE_0;
 
	v->progress = 0;
 
}
 

	
 
static void DieselSmokeTick(Vehicle *v)
 
{
 
	v->progress++;
 

	
 
	if ((v->progress & 3) == 0) {
 
		BeginVehicleMove(v);
 
		v->z_pos++;
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	} else if ((v->progress & 7) == 1) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_DIESEL_SMOKE_5) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
		}
 
	}
 
}
 

	
 
static void ElectricSparkInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_ELECTRIC_SPARK_0;
 
	v->progress = 1;
 
}
 

	
 
static void ElectricSparkTick(Vehicle *v)
 
{
 
	if (v->progress < 2) {
 
		v->progress++;
 
	} else {
 
		v->progress = 0;
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_ELECTRIC_SPARK_5) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
		}
 
	}
 
}
 

	
 
static void SmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_SMOKE_0;
 
	v->progress = 12;
 
}
 

	
 
static void SmokeTick(Vehicle *v)
 
{
 
	bool moved = false;
 

	
 
	BeginVehicleMove(v);
 

	
 
	v->progress++;
 

	
 
	if ((v->progress & 3) == 0) {
 
		v->z_pos++;
 
		moved = true;
 
	}
 

	
 
	if ((v->progress & 0xF) == 4) {
 
		if (v->cur_image != SPR_SMOKE_4) {
 
			v->cur_image++;
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
			return;
 
		}
 
		moved = true;
 
	}
 

	
 
	if (moved) {
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void ExplosionLargeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_EXPLOSION_LARGE_0;
 
	v->progress = 0;
 
}
 

	
 
static void ExplosionLargeTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 3) == 0) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_EXPLOSION_LARGE_F) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
		}
 
	}
 
}
 

	
 
static void BreakdownSmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_BREAKDOWN_SMOKE_0;
 
	v->progress = 0;
 
}
 

	
 
static void BreakdownSmokeTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 7) == 0) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) {
 
			v->cur_image++;
 
		} else {
 
			v->cur_image = SPR_BREAKDOWN_SMOKE_0;
 
		}
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 

	
 
	v->u.special.unk0--;
 
	if (v->u.special.unk0 == 0) {
 
		BeginVehicleMove(v);
 
		EndVehicleMove(v);
 
		DeleteVehicle(v);
 
	}
 
}
 

	
 
static void ExplosionSmallInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_EXPLOSION_SMALL_0;
 
	v->progress = 0;
 
}
 

	
 
static void ExplosionSmallTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 3) == 0) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_EXPLOSION_SMALL_B) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
		}
 
	}
 
}
 

	
 
static void BulldozerInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_BULLDOZER_NE;
 
	v->progress = 0;
 
	v->u.special.unk0 = 0;
 
	v->u.special.unk2 = 0;
 
}
 

	
 
typedef struct BulldozerMovement {
 
	byte direction:2;
 
	byte image:2;
 
	byte duration:3;
 
} BulldozerMovement;
 

	
 
static const BulldozerMovement _bulldozer_movement[] = {
 
	{ 0, 0, 4 },
 
	{ 3, 3, 4 },
 
	{ 2, 2, 7 },
 
	{ 0, 2, 7 },
 
	{ 1, 1, 3 },
 
	{ 2, 2, 7 },
 
	{ 0, 2, 7 },
 
	{ 1, 1, 3 },
 
	{ 2, 2, 7 },
 
	{ 0, 2, 7 },
 
	{ 3, 3, 6 },
 
	{ 2, 2, 6 },
 
	{ 1, 1, 7 },
 
	{ 3, 1, 7 },
 
	{ 0, 0, 3 },
 
	{ 1, 1, 7 },
 
	{ 3, 1, 7 },
 
	{ 0, 0, 3 },
 
	{ 1, 1, 7 },
 
	{ 3, 1, 7 }
 
};
 

	
 
static const struct {
 
	int8 x;
 
	int8 y;
 
} _inc_by_dir[] = {
 
	{ -1,  0 },
 
	{  0,  1 },
 
	{  1,  0 },
 
	{  0, -1 }
 
};
 

	
 
static void BulldozerTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 7) == 0) {
 
		const BulldozerMovement* b = &_bulldozer_movement[v->u.special.unk0];
 

	
 
		BeginVehicleMove(v);
 

	
 
		v->cur_image = SPR_BULLDOZER_NE + b->image;
 

	
 
		v->x_pos += _inc_by_dir[b->direction].x;
 
		v->y_pos += _inc_by_dir[b->direction].y;
 

	
 
		v->u.special.unk2++;
 
		if (v->u.special.unk2 >= b->duration) {
 
			v->u.special.unk2 = 0;
 
			v->u.special.unk0++;
 
			if (v->u.special.unk0 == lengthof(_bulldozer_movement)) {
 
				EndVehicleMove(v);
 
				DeleteVehicle(v);
 
				return;
 
			}
 
		}
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void BubbleInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_BUBBLE_GENERATE_0;
 
	v->spritenum = 0;
 
	v->progress = 0;
 
}
 

	
 
typedef struct BubbleMovement {
 
	int8 x:4;
 
	int8 y:4;
 
	int8 z:4;
 
	byte image:4;
 
} BubbleMovement;
 

	
 
#define MK(x, y, z, i) { x, y, z, i }
 
#define ME(i) { i, 4, 0, 0 }
 

	
 
static const BubbleMovement _bubble_float_sw[] = {
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 2),
 
	ME(1)
 
};
 

	
 

	
 
static const BubbleMovement _bubble_float_ne[] = {
 
	MK( 0, 0, 1, 0),
 
	MK(-1, 0, 1, 1),
 
	MK( 0, 0, 1, 0),
 
	MK(-1, 0, 1, 2),
 
	ME(1)
 
};
 

	
 
static const BubbleMovement _bubble_float_se[] = {
 
	MK(0, 0, 1, 0),
 
	MK(0, 1, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 1, 1, 2),
 
	ME(1)
 
};
 

	
 
static const BubbleMovement _bubble_float_nw[] = {
 
	MK(0,  0, 1, 0),
 
	MK(0, -1, 1, 1),
 
	MK(0,  0, 1, 0),
 
	MK(0, -1, 1, 2),
 
	ME(1)
 
};
 

	
 
static const BubbleMovement _bubble_burst[] = {
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 7),
 
	MK(0, 0, 1, 8),
 
	MK(0, 0, 1, 9),
 
	ME(0)
 
};
 

	
 
static const BubbleMovement _bubble_absorb[] = {
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(2, 1, 3, 0),
 
	MK(1, 1, 3, 1),
 
	MK(2, 1, 3, 0),
 
	MK(1, 1, 3, 2),
 
	MK(2, 1, 3, 0),
 
	MK(1, 1, 3, 1),
 
	MK(2, 1, 3, 0),
 
	MK(1, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 2),
 
	ME(2),
 
	MK(0, 0, 0, 0xA),
 
	MK(0, 0, 0, 0xB),
 
	MK(0, 0, 0, 0xC),
 
	MK(0, 0, 0, 0xD),
 
	MK(0, 0, 0, 0xE),
 
	ME(0)
 
};
 
#undef ME
 
#undef MK
 

	
 
static const BubbleMovement * const _bubble_movement[] = {
 
	_bubble_float_sw,
 
	_bubble_float_ne,
 
	_bubble_float_se,
 
	_bubble_float_nw,
 
	_bubble_burst,
 
	_bubble_absorb,
 
};
 

	
 
static void BubbleTick(Vehicle *v)
 
{
 
	/*
 
	 * Warning: those effects can NOT use Random(), and have to use
 
	 *  InteractiveRandom(), because somehow someone forgot to save
 
	 *  spritenum to the savegame, and so it will cause desyncs in
 
	 *  multiplayer!! (that is: in ToyLand)
 
	 */
 
	uint et;
 
	const BubbleMovement *b;
 

	
 
	v->progress++;
 
	if ((v->progress & 3) != 0)
 
		return;
 

	
 
	BeginVehicleMove(v);
 

	
 
	if (v->spritenum == 0) {
 
		v->cur_image++;
 
		if (v->cur_image < SPR_BUBBLE_GENERATE_3) {
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
			return;
 
		}
 
		if (v->u.special.unk2 != 0) {
 
			v->spritenum = GB(InteractiveRandom(), 0, 2) + 1;
 
		} else {
 
			v->spritenum = 6;
 
		}
 
		et = 0;
 
	} else {
 
		et = v->engine_type + 1;
 
	}
 

	
 
	b = &_bubble_movement[v->spritenum - 1][et];
 

	
 
	if (b->y == 4 && b->x == 0) {
 
		EndVehicleMove(v);
 
		DeleteVehicle(v);
 
		return;
 
	}
 

	
 
	if (b->y == 4 && b->x == 1) {
 
		if (v->z_pos > 180 || CHANCE16I(1, 96, InteractiveRandom())) {
 
			v->spritenum = 5;
 
			SndPlayVehicleFx(SND_2F_POP, v);
 
		}
 
		et = 0;
 
	}
 

	
 
	if (b->y == 4 && b->x == 2) {
 
		TileIndex tile;
 

	
 
		et++;
 
		SndPlayVehicleFx(SND_31_EXTRACT, v);
 

	
 
		tile = TileVirtXY(v->x_pos, v->y_pos);
 
		if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == 0xA2) AddAnimatedTile(tile);
 
	}
 

	
 
	v->engine_type = et;
 
	b = &_bubble_movement[v->spritenum - 1][et];
 

	
 
	v->x_pos += b->x;
 
	v->y_pos += b->y;
 
	v->z_pos += b->z;
 
	v->cur_image = SPR_BUBBLE_0 + b->image;
 

	
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
}
 

	
 

	
 
typedef void EffectInitProc(Vehicle *v);
 
typedef void EffectTickProc(Vehicle *v);
 

	
 
static EffectInitProc * const _effect_init_procs[] = {
 
	ChimneySmokeInit,
 
	SteamSmokeInit,
 
	DieselSmokeInit,
 
	ElectricSparkInit,
 
	SmokeInit,
 
	ExplosionLargeInit,
 
	BreakdownSmokeInit,
 
	ExplosionSmallInit,
 
	BulldozerInit,
 
	BubbleInit,
 
};
 

	
 
static EffectTickProc * const _effect_tick_procs[] = {
 
	ChimneySmokeTick,
 
	SteamSmokeTick,
 
	DieselSmokeTick,
 
	ElectricSparkTick,
 
	SmokeTick,
 
	ExplosionLargeTick,
 
	BreakdownSmokeTick,
 
	ExplosionSmallTick,
 
	BulldozerTick,
 
	BubbleTick,
 
};
 

	
 

	
 
Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicle type)
 
{
 
	Vehicle *v;
 

	
 
	v = ForceAllocateSpecialVehicle();
 
	if (v != NULL) {
 
		v->type = VEH_Special;
 
		v->subtype = type;
 
		v->x_pos = x;
 
		v->y_pos = y;
 
		v->z_pos = z;
 
		v->z_height = v->sprite_width = v->sprite_height = 1;
 
		v->x_offs = v->y_offs = 0;
 
		v->tile = 0;
 
		v->vehstatus = VS_UNCLICKABLE;
 

	
 
		_effect_init_procs[type](v);
 

	
 
		VehiclePositionChanged(v);
 
		BeginVehicleMove(v);
 
		EndVehicleMove(v);
 
	}
 
	return v;
 
}
 

	
 
Vehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicle type)
 
{
 
	int safe_x = clamp(x, 0, MapMaxX() * TILE_SIZE);
 
	int safe_y = clamp(y, 0, MapMaxY() * TILE_SIZE);
 
	return CreateEffectVehicle(x, y, GetSlopeZ(safe_x, safe_y) + z, type);
 
}
 

	
 
Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicle type)
 
{
 
	return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
 
}
 

	
 
static void EffectVehicle_Tick(Vehicle *v)
 
{
 
	_effect_tick_procs[v->subtype](v);
 
}
 

	
 
Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
 
{
 
	Vehicle *found = NULL, *v;
 
	uint dist, best_dist = (uint)-1;
 

	
 
	if ( (uint)(x -= vp->left) >= (uint)vp->width ||
 
			 (uint)(y -= vp->top) >= (uint)vp->height)
 
				return NULL;
 

	
 
	x = (x << vp->zoom) + vp->virtual_left;
 
	y = (y << vp->zoom) + vp->virtual_top;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if ((v->vehstatus & (VS_HIDDEN|VS_UNCLICKABLE)) == 0 &&
 
				x >= v->left_coord && x <= v->right_coord &&
 
				y >= v->top_coord && y <= v->bottom_coord) {
 

	
 
			dist = max(
 
				myabs( ((v->left_coord + v->right_coord)>>1) - x ),
 
				myabs( ((v->top_coord + v->bottom_coord)>>1) - y )
 
			);
 

	
 
			if (dist < best_dist) {
 
				found = v;
 
				best_dist = dist;
 
			}
 
		}
 
	}
 

	
 
	return found;
 
}
 

	
 

	
 
void DecreaseVehicleValue(Vehicle *v)
 
{
 
	v->value -= v->value >> 8;
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
}
 

	
 
static const byte _breakdown_chance[64] = {
 
	  3,   3,   3,   3,   3,   3,   3,   3,
 
	  4,   4,   5,   5,   6,   6,   7,   7,
 
	  8,   8,   9,   9,  10,  10,  11,  11,
 
	 12,  13,  13,  13,  13,  14,  15,  16,
 
	 17,  19,  21,  25,  28,  31,  34,  37,
 
	 40,  44,  48,  52,  56,  60,  64,  68,
 
	 72,  80,  90, 100, 110, 120, 130, 140,
 
	150, 170, 190, 210, 230, 250, 250, 250,
 
};
 

	
 
void CheckVehicleBreakdown(Vehicle *v)
 
{
 
	int rel, rel_old;
 
	uint32 r;
 
	int chance;
 

	
 
	/* decrease reliability */
 
	v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
 
	if ((rel_old >> 8) != (rel >> 8))
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
	if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
 
			v->cur_speed < 5 || _game_mode == GM_MENU) {
 
		return;
 
	}
 

	
 
	r = Random();
 

	
 
	/* increase chance of failure */
 
	chance = v->breakdown_chance + 1;
 
	if (CHANCE16I(1,25,r)) chance += 25;
 
	v->breakdown_chance = min(255, chance);
 

	
 
	/* calculate reliability value to use in comparison */
 
	rel = v->reliability;
 
	if (v->type == VEH_Ship) rel += 0x6666;
 

	
 
	/* disabled breakdowns? */
 
	if (_opt.diff.vehicle_breakdowns < 1) return;
 

	
 
	/* reduced breakdowns? */
 
	if (_opt.diff.vehicle_breakdowns == 1) rel += 0x6666;
 

	
 
	/* check if to break down */
 
	if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
 
		v->breakdown_ctr    = GB(r, 16, 6) + 0x3F;
 
		v->breakdown_delay  = GB(r, 24, 7) + 0x80;
 
		v->breakdown_chance = 0;
 
	}
 
}
 

	
 
static const StringID _vehicle_type_names[4] = {
 
	STR_019F_TRAIN,
 
	STR_019C_ROAD_VEHICLE,
 
	STR_019E_SHIP,
 
	STR_019D_AIRCRAFT,
 
};
 

	
 
static void ShowVehicleGettingOld(Vehicle *v, StringID msg)
 
{
 
	if (v->owner != _local_player) return;
 

	
 
	// Do not show getting-old message if autorenew is active
 
	if (GetPlayer(v->owner)->engine_renew) return;
 

	
 
	SetDParam(0, _vehicle_type_names[v->type - 0x10]);
 
	SetDParam(1, v->unitnumber);
 
	AddNewsItem(msg, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
 
}
 

	
 
void AgeVehicle(Vehicle *v)
 
{
 
	int age;
 

	
 
	if (v->age < 65535)
 
		v->age++;
 

	
 
	age = v->age - v->max_age;
 
	if (age == 366*0 || age == 366*1 || age == 366*2 || age == 366*3 || age == 366*4)
 
		v->reliability_spd_dec <<= 1;
 

	
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
	if (age == -366) {
 
		ShowVehicleGettingOld(v, STR_01A0_IS_GETTING_OLD);
 
	} else if (age == 0) {
 
		ShowVehicleGettingOld(v, STR_01A1_IS_GETTING_VERY_OLD);
 
	} else if (age == 366*1 || age == 366*2 || age == 366*3 || age == 366*4 || age == 366*5) {
 
		ShowVehicleGettingOld(v, STR_01A2_IS_GETTING_VERY_OLD_AND);
 
	}
 
}
 

	
 
/** Starts or stops a lot of vehicles
 
 * @param tile Tile of the depot where the vehicles are started/stopped (only used for depots)
 
 * @param p1 Station/Order/Depot ID (only used for vehicle list windows)
 
 * @param p2 bitmask
 
 *   - bit 0-4 Vehicle type
 
 *   - bit 5 false = start vehicles, true = stop vehicles
 
 *   - bit 6 if set, then it's a vehicle list window, not a depot and Tile is ignored in this case
 
 *   - bit 8-11 Vehicle List Window type (ignored unless bit 1 is set)
 
 */
 
int32 CmdMassStartStopVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle **vl = NULL;
 
	uint16 engine_list_length = 0;
 
	uint16 engine_count = 0;
 
	int32 return_value = CMD_ERROR;
 
	uint i;
 
	uint stop_command;
 
	byte vehicle_type = GB(p2, 0, 5);
 
	bool start_stop = HASBIT(p2, 5);
 
	bool vehicle_list_window = HASBIT(p2, 6);
 

	
 
	switch (vehicle_type) {
 
		case VEH_Train:    stop_command = CMD_START_STOP_TRAIN;    break;
 
		case VEH_Road:     stop_command = CMD_START_STOP_ROADVEH;  break;
 
		case VEH_Ship:     stop_command = CMD_START_STOP_SHIP;     break;
 
		case VEH_Aircraft: stop_command = CMD_START_STOP_AIRCRAFT; break;
 
		default: return CMD_ERROR;
 
	}
 

	
 
	if (vehicle_list_window) {
 
		uint16 id = GB(p1, 0, 16);
 
		uint16 window_type = p2 & VLW_MASK;
 

	
 
		engine_count = GenerateVehicleSortList((const Vehicle***)&vl, &engine_list_length, vehicle_type, _current_player, id, id, id, window_type);
 
	} else {
 
		/* Get the list of vehicles in the depot */
 
		BuildDepotVehicleList(vehicle_type, tile, &vl, &engine_list_length, &engine_count, NULL, NULL, NULL);
 
	}
 

	
 
	for (i = 0; i < engine_count; i++) {
 
		const Vehicle *v = vl[i];
 
		int32 ret;
 

	
 
		if (!!(v->vehstatus & VS_STOPPED) != start_stop) continue;
 

	
 
		if (!vehicle_list_window) {
 
			if (vehicle_type == VEH_Train) {
 
				if (CheckTrainInDepot(v, false) == -1) continue;
 
			} else {
 
				if (!(v->vehstatus & VS_HIDDEN)) continue;
 
			}
 
		}
 

	
 
		ret = DoCommand(tile, v->index, 0, flags, stop_command);
 

	
 
		if (!CmdFailed(ret)) {
 
			return_value = 0;
 
			/* We know that the command is valid for at least one vehicle.
 
			 * If we haven't set DC_EXEC, then there is no point in continueing because it will be valid */
 
			if (!(flags & DC_EXEC)) break;
 
		}
 
	}
 

	
 
	free(vl);
 
	return return_value;
 
}
 

	
 
/** Sells all vehicles in a depot
 
* @param tile Tile of the depot where the depot is
 
* @param p1 Vehicle type
 
* @param p2 unused
 
*/
 
int32 CmdDepotSellAllVehicles(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle **engines = NULL;
 
	Vehicle **wagons = NULL;
 
	uint16 engine_list_length = 0;
 
	uint16 engine_count = 0;
 
	uint16 wagon_list_length = 0;
 
	uint16 wagon_count = 0;
 

	
 
	int32 cost = 0;
 
	uint i, sell_command, total_number_vehicles;
 
	byte vehicle_type = GB(p1, 0, 8);
 

	
 
	switch (vehicle_type) {
 
		case VEH_Train:    sell_command = CMD_SELL_RAIL_WAGON; break;
 
		case VEH_Road:     sell_command = CMD_SELL_ROAD_VEH;   break;
 
		case VEH_Ship:     sell_command = CMD_SELL_SHIP;       break;
 
		case VEH_Aircraft: sell_command = CMD_SELL_AIRCRAFT;   break;
 
		default: return CMD_ERROR;
 
	}
 

	
 
	/* Get the list of vehicles in the depot */
 
	BuildDepotVehicleList(vehicle_type, tile, &engines, &engine_list_length, &engine_count,
 
						                      &wagons,  &wagon_list_length,  &wagon_count);
 

	
 
	total_number_vehicles = engine_count + wagon_count;
 
	for (i = 0; i < total_number_vehicles; i++) {
 
		const Vehicle *v;
 
		int32 ret;
 

	
 
		if (i < engine_count) {
 
			v = engines[i];
 
		} else {
 
			v = wagons[i - engine_count];
 
		}
 

	
 
		ret = DoCommand(tile, v->index, 1, flags, sell_command);
 

	
 
		if (!CmdFailed(ret)) cost += ret;
 
	}
 

	
 
	free(engines);
 
	free(wagons);
 
	if (cost == 0) return CMD_ERROR; // no vehicles to sell
 
	return cost;
 
}
 

	
 
/** Autoreplace all vehicles in the depot
 
* @param tile Tile of the depot where the vehicles are
 
* @param p1 Type of vehicle
 
* @param p2 Unused
 
*/
 
int32 CmdDepotMassAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle **vl = NULL;
 
	uint16 engine_list_length = 0;
 
	uint16 engine_count = 0;
 
	uint i, x = 0, y = 0, z = 0;
 
	int32 cost = 0;
 
	byte vehicle_type = GB(p1, 0, 8);
 

	
 

	
 
	if (!IsTileOwner(tile, _current_player)) return CMD_ERROR;
 

	
 
	/* Get the list of vehicles in the depot */
 
	BuildDepotVehicleList(vehicle_type, tile, &vl, &engine_list_length, &engine_count, NULL, NULL, NULL);
 

	
 

	
 
	for (i = 0; i < engine_count; i++) {
 
		Vehicle *v = vl[i];
 
		bool stopped = !(v->vehstatus & VS_STOPPED);
 
		int32 ret;
 

	
 
		/* Ensure that the vehicle completely in the depot */
 
		if (!IsVehicleInDepot(v)) continue;
 

	
 
		x = v->x_pos;
 
		y = v->y_pos;
 
		z = v->z_pos;
 

	
 
		if (stopped) {
 
			v->vehstatus |= VS_STOPPED; // Stop the vehicle
 
			v->leave_depot_instantly = true;
 
		}
 
		ret = MaybeReplaceVehicle(v, !(flags & DC_EXEC), false);
 

	
 
		if (!CmdFailed(ret)) {
 
			cost += ret;
 
			if (!(flags & DC_EXEC)) break;
 
			/* There is a problem with autoreplace and newgrf
 
			 * It's impossible to tell the length of a train after it's being replaced before it's actually done
 
			 * Because of this, we can't estimate costs due to wagon removal and we will have to always return 0 and pay manually
 
			 * Since we pay after each vehicle is replaced and MaybeReplaceVehicle() check if the player got enough money
 
			 * we should never reach a condition where the player will end up with negative money from doing this */
 
			SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 
			SubtractMoneyFromPlayer(ret);
 
		}
 
	}
 

	
 
	if (cost == 0) {
 
		cost = CMD_ERROR;
 
	} else {
 
		if (flags & DC_EXEC) {
 
			/* Display the cost animation now that DoCommandP() can't do it for us (see previous comments) */
 
			if (IsLocalPlayer()) ShowCostOrIncomeAnimation(x, y, z, cost);
 
		}
 
		cost = 0;
 
	}
 

	
 
	free(vl);
 
	return cost;
 
}
 

	
 
/** Clone a vehicle. If it is a train, it will clone all the cars too
 
 * @param tile tile of the depot where the cloned vehicle is build
 
 * @param p1 the original vehicle's index
 
 * @param p2 1 = shared orders, else copied orders
 
 */
 
int32 CmdCloneVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v_front, *v;
 
	Vehicle *w_front, *w, *w_rear;
 
	int cost, total_cost = 0;
 
	uint32 build_argument = 2;
 

	
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 
	v = GetVehicle(p1);
 
	v_front = v;
 
	w = NULL;
 
	w_front = NULL;
 
	w_rear = NULL;
 

	
 

	
 
	/*
 
	 * v_front is the front engine in the original vehicle
 
	 * v is the car/vehicle of the original vehicle, that is currently being copied
 
	 * w_front is the front engine of the cloned vehicle
 
	 * w is the car/vehicle currently being cloned
 
	 * w_rear is the rear end of the cloned train. It's used to add more cars and is only used by trains
 
	 */
 

	
 
	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (v->type == VEH_Train && (!IsFrontEngine(v) || v->u.rail.crash_anim_pos >= 4400)) return CMD_ERROR;
 

	
 
	// check that we can allocate enough vehicles
 
	if (!(flags & DC_EXEC)) {
 
		int veh_counter = 0;
 
		do {
 
			veh_counter++;
 
		} while ((v = v->next) != NULL);
 

	
 
		if (!AllocateVehicles(NULL, veh_counter)) {
 
			return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 
		}
 
	}
 

	
 
	v = v_front;
 

	
 
	do {
 

	
 
		if (IsMultiheaded(v) && !IsTrainEngine(v)) {
 
			/* we build the rear ends of multiheaded trains with the front ones */
 
			continue;
 
		}
 

	
 
		cost = DoCommand(tile, v->engine_type, build_argument, flags, CMD_BUILD_VEH(v->type));
 
		build_argument = 3; // ensure that we only assign a number to the first engine
 

	
 
		if (CmdFailed(cost)) return cost;
 

	
 
		total_cost += cost;
 

	
 
		if (flags & DC_EXEC) {
 
			w = GetVehicle(_new_vehicle_id);
 

	
 
			if (v->cargo_type != w->cargo_type || v->cargo_subtype != w->cargo_subtype) {
 
				// we can't pay for refitting because we can't estimate refitting costs for a vehicle before it's build
 
				// if we pay for it anyway, the cost and the estimated cost will not be the same and we will have an assert
 
				DoCommand(0, w->index, v->cargo_type | (v->cargo_subtype << 8), flags, CMD_REFIT_VEH(v->type));
 
			}
 
			if (v->type == VEH_Train && HASBIT(v->u.rail.flags, VRF_REVERSE_DIRECTION)) {
 
				SETBIT(w->u.rail.flags, VRF_REVERSE_DIRECTION);
 
			}
 

	
 
			if (v->type == VEH_Train && !IsFrontEngine(v)) {
 
				// this s a train car
 
				// add this unit to the end of the train
 
				DoCommand(0, (w_rear->index << 16) | w->index, 1, flags, CMD_MOVE_RAIL_VEHICLE);
 
			} else {
 
				// this is a front engine or not a train. It need orders
 
				w_front = w;
 
				w->service_interval = v->service_interval;
 
				DoCommand(0, (v->index << 16) | w->index, p2 & 1 ? CO_SHARE : CO_COPY, flags, CMD_CLONE_ORDER);
 
			}
 
			w_rear = w; // trains needs to know the last car in the train, so they can add more in next loop
 
		}
 
	} while (v->type == VEH_Train && (v = GetNextVehicle(v)) != NULL);
 

	
 
	if (flags & DC_EXEC && v_front->type == VEH_Train) {
 
		// for trains this needs to be the front engine due to the callback function
 
		_new_vehicle_id = w_front->index;
 
	}
 

	
 
	/* Set the expense type last as refitting will make the cost go towards
 
	 * running costs... */
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 
	return total_cost;
 
}
 

	
 
/*
 
 * move the cargo from one engine to another if possible
 
 */
 
static void MoveVehicleCargo(Vehicle *dest, Vehicle *source)
 
{
 
	Vehicle *v = dest;
 
	int units_moved;
 

	
 
	do {
 
		do {
 
			if (source->cargo_type != dest->cargo_type)
 
				continue; // cargo not compatible
 

	
 
			if (dest->cargo_count == dest->cargo_cap)
 
				continue; // the destination vehicle is already full
 

	
 
			units_moved = min(source->cargo_count, dest->cargo_cap - dest->cargo_count);
 
			source->cargo_count -= units_moved;
 
			dest->cargo_count   += units_moved;
 
			dest->cargo_source   = source->cargo_source;
 

	
 
			// copy the age of the cargo
 
			dest->cargo_days   = source->cargo_days;
 
			dest->day_counter  = source->day_counter;
 
			dest->tick_counter = source->tick_counter;
 

	
 
		} while (source->cargo_count > 0 && (dest = dest->next) != NULL);
 
		dest = v;
 
	} while ((source = source->next) != NULL);
 
}
 

	
 
static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, const EngineID engine_type)
 
{
 
	const Order *o;
 
	const Vehicle *u;
 

	
 
	if (v->type == VEH_Train) {
 
		u = GetFirstVehicleInChain(v);
 
	} else {
 
		u = v;
 
	}
 

	
 
	FOR_VEHICLE_ORDERS(u, o) {
 
		if (!(o->refit_cargo < NUM_CARGO)) continue;
 
		if (!CanRefitTo(v->engine_type, o->refit_cargo)) continue;
 
		if (!CanRefitTo(engine_type, o->refit_cargo)) return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Function to find what type of cargo to refit to when autoreplacing
 
 * @param *v Original vehicle, that is being replaced
 
 * @param engine_type The EngineID of the vehicle that is being replaced to
 
 * @return The cargo type to replace to
 
 *    CT_NO_REFIT is returned if no refit is needed
 
 *    CT_INVALID is returned when both old and new vehicle got cargo capacity and refitting the new one to the old one's cargo type isn't possible
 
 */
 
static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type)
 
{
 
	bool new_cargo_capacity = true;
 
	CargoID new_cargo_type = CT_INVALID;
 

	
 
	switch (v->type) {
 
		case VEH_Train:
 
			new_cargo_capacity = (RailVehInfo(engine_type)->capacity > 0);
 
			new_cargo_type     = RailVehInfo(engine_type)->cargo_type;
 
			break;
 

	
 
		case VEH_Road:
 
			new_cargo_capacity = (RoadVehInfo(engine_type)->capacity > 0);
 
			new_cargo_type     = RoadVehInfo(engine_type)->cargo_type;
 
			break;
 
		case VEH_Ship:
 
			new_cargo_capacity = (ShipVehInfo(engine_type)->capacity > 0);
 
			new_cargo_type     = ShipVehInfo(engine_type)->cargo_type;
 
			break;
 

	
 
		case VEH_Aircraft:
 
			/* all aircraft starts as passenger planes with cargo capacity
 
			 * new_cargo_capacity is always true for aircraft, which is the init value. No need to set it here */
 
			new_cargo_type     = CT_PASSENGERS;
 
			break;
 

	
 
		default: NOT_REACHED(); break;
 
	}
 

	
 
	if (!new_cargo_capacity) return CT_NO_REFIT; // Don't try to refit an engine with no cargo capacity
 

	
 
	if (v->cargo_type == new_cargo_type || CanRefitTo(engine_type, v->cargo_type)) {
 
		if (VerifyAutoreplaceRefitForOrders(v, engine_type)) {
 
			return v->cargo_type == new_cargo_type ? CT_NO_REFIT : v->cargo_type;
 
		} else {
 
			return CT_INVALID;
 
		}
 
	}
 
	if (v->type != VEH_Train) return CT_INVALID; // We can't refit the vehicle to carry the cargo we want
 

	
 
	/* Below this line it's safe to assume that the vehicle in question is a train */
 

	
 
	if (v->cargo_cap != 0) return CT_INVALID; // trying to replace a vehicle with cargo capacity into another one with incompatible cargo type
 

	
 
	/* the old engine didn't have cargo capacity, but the new one does
 
	 * now we will figure out what cargo the train is carrying and refit to fit this */
 
	v = GetFirstVehicleInChain(v);
 
	do {
 
		if (v->cargo_cap == 0) continue;
 
		/* Now we found a cargo type being carried on the train and we will see if it is possible to carry to this one */
 
		if (v->cargo_type == new_cargo_type) return CT_NO_REFIT;
 
		if (CanRefitTo(engine_type, v->cargo_type)) return v->cargo_type;
 
	} while ((v=v->next) != NULL);
 
	return CT_NO_REFIT; // We failed to find a cargo type on the old vehicle and we will not refit the new one
 
}
 

	
 
/* Replaces a vehicle (used to be called autorenew)
 
 * This function is only called from MaybeReplaceVehicle()
 
 * Must be called with _current_player set to the owner of the vehicle
 
 * @param w Vehicle to replace
 
 * @param flags is the flags to use when calling DoCommand(). Mainly DC_EXEC counts
 
 * @return value is cost of the replacement or CMD_ERROR
 
 */
 
static int32 ReplaceVehicle(Vehicle **w, byte flags, int32 total_cost)
 
{
 
	int32 cost;
 
	int32 sell_value;
 
	Vehicle *old_v = *w;
 
	const Player *p = GetPlayer(old_v->owner);
 
	EngineID new_engine_type;
 
	const UnitID cached_unitnumber = old_v->unitnumber;
 
	bool new_front = false;
 
	Vehicle *new_v = NULL;
 
	char vehicle_name[32];
 
	CargoID replacement_cargo_type;
 

	
 
	new_engine_type = EngineReplacementForPlayer(p, old_v->engine_type);
 
	if (new_engine_type == INVALID_ENGINE) new_engine_type = old_v->engine_type;
 

	
 
	replacement_cargo_type = GetNewCargoTypeForReplace(old_v, new_engine_type);
 

	
 
	/* check if we can't refit to the needed type, so no replace takes place to prevent the vehicle from altering cargo type */
 
	if (replacement_cargo_type == CT_INVALID) return 0;
 

	
 
	sell_value = DoCommand(0, old_v->index, 0, DC_QUERY_COST, CMD_SELL_VEH(old_v->type));
 

	
 
	/* We give the player a loan of the same amount as the sell value.
 
	 * This is needed in case he needs the income from the sale to build the new vehicle.
 
	 * We take it back if building fails or when we really sell the old engine */
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 
	SubtractMoneyFromPlayer(sell_value);
 

	
 
	cost = DoCommand(old_v->tile, new_engine_type, 3, flags, CMD_BUILD_VEH(old_v->type));
 
	if (CmdFailed(cost)) {
 
		SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 
		SubtractMoneyFromPlayer(-sell_value); // Take back the money we just gave the player
 
		return cost;
 
	}
 

	
 
	if (replacement_cargo_type != CT_NO_REFIT) cost += GetRefitCost(new_engine_type); // add refit cost
 

	
 
	if (flags & DC_EXEC) {
 
		new_v = GetVehicle(_new_vehicle_id);
 
		*w = new_v; //we changed the vehicle, so MaybeReplaceVehicle needs to work on the new one. Now we tell it what the new one is
 

	
 
		/* refit if needed */
 
		if (replacement_cargo_type != CT_NO_REFIT) {
 
			if (CmdFailed(DoCommand(0, new_v->index, replacement_cargo_type, DC_EXEC, CMD_REFIT_VEH(new_v->type)))) {
 
				/* Being here shows a failure, which most likely is in GetNewCargoTypeForReplace() or incorrect estimation costs */
 
				error("Autoreplace failed to refit. Replace engine %d to %d and refit to cargo %d", old_v->engine_type, new_v->engine_type, replacement_cargo_type);
 
			}
 
		}
 

	
 
		if (new_v->type == VEH_Train && HASBIT(old_v->u.rail.flags, VRF_REVERSE_DIRECTION) && !IsMultiheaded(new_v) && !(new_v->next != NULL && IsArticulatedPart(new_v->next))) {
 
			// we are autorenewing to a single engine, so we will turn it as the old one was turned as well
 
			SETBIT(new_v->u.rail.flags, VRF_REVERSE_DIRECTION);
 
		}
 

	
 
		if (old_v->type == VEH_Train && !IsFrontEngine(old_v)) {
 
			/* this is a railcar. We need to move the car into the train
 
			 * We add the new engine after the old one instead of replacing it. It will give the same result anyway when we
 
			 * sell the old engine in a moment
 
			 */
 
			DoCommand(0, (GetPrevVehicleInChain(old_v)->index << 16) | new_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 
			/* Now we move the old one out of the train */
 
			DoCommand(0, (INVALID_VEHICLE << 16) | old_v->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 
		} else {
 
			// copy/clone the orders
 
			DoCommand(0, (old_v->index << 16) | new_v->index, IsOrderListShared(old_v) ? CO_SHARE : CO_COPY, DC_EXEC, CMD_CLONE_ORDER);
 
			new_v->cur_order_index = old_v->cur_order_index;
 
			ChangeVehicleViewWindow(old_v, new_v);
 
			new_v->profit_this_year = old_v->profit_this_year;
 
			new_v->profit_last_year = old_v->profit_last_year;
 
			new_v->service_interval = old_v->service_interval;
 
			new_front = true;
 
			new_v->unitnumber = old_v->unitnumber; // use the same unit number
 

	
 
			new_v->current_order = old_v->current_order;
 
			if (old_v->type == VEH_Train && GetNextVehicle(old_v) != NULL){
 
				Vehicle *temp_v = GetNextVehicle(old_v);
 

	
 
				// move the entire train to the new engine, excluding the old engine
 
				if (IsMultiheaded(old_v) && temp_v == old_v->u.rail.other_multiheaded_part) {
 
					// we got front and rear of a multiheaded engine right after each other. We should work with the next in line instead
 
					temp_v = GetNextVehicle(temp_v);
 
				}
 

	
 
				if (temp_v != NULL) {
 
					DoCommand(0, (new_v->index << 16) | temp_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 
				}
 
			}
 
		}
 
		/* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */
 
		MoveVehicleCargo(new_v->type == VEH_Train ? GetFirstVehicleInChain(new_v) : new_v, old_v);
 

	
 
		// Get the name of the old vehicle if it has a custom name.
 
		if (!IsCustomName(old_v->string_id)) {
 
			vehicle_name[0] = '\0';
 
		} else {
 
			GetName(vehicle_name, old_v->string_id & 0x7FF, lastof(vehicle_name));
 
		}
 
	} else { // flags & DC_EXEC not set
 
		/* Ensure that the player will not end up having negative money while autoreplacing
 
		 * This is needed because the only other check is done after the income from selling the old vehicle is substracted from the cost */
 
		if (p->money64 < (cost + total_cost)) {
 
			SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 
			SubtractMoneyFromPlayer(-sell_value); // Pay back the loan
 
			return CMD_ERROR;
 
		}
 
	}
 

	
 
	/* Take back the money we just gave the player just before building the vehicle
 
	 * The player will get the same amount now that the sale actually takes place */
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 
	SubtractMoneyFromPlayer(-sell_value);
 

	
 
	/* sell the engine/ find out how much you get for the old engine (income is returned as negative cost) */
 
	cost += DoCommand(0, old_v->index, 0, flags, CMD_SELL_VEH(old_v->type));
 

	
 
	if (new_front) {
 
		/* now we assign the old unitnumber to the new vehicle */
 
		new_v->unitnumber = cached_unitnumber;
 
	}
 

	
 
	/* Transfer the name of the old vehicle */
 
	if ((flags & DC_EXEC) && vehicle_name[0] != '\0') {
 
		_cmd_text = vehicle_name;
 
		DoCommand(0, new_v->index, 0, DC_EXEC, CMD_NAME_VEHICLE);
 
	}
 

	
 
	return cost;
 
}
 

	
 
/** replaces a vehicle if it's set for autoreplace or is too old
 
 * (used to be called autorenew)
 
 * @param v The vehicle to replace
 
 * if the vehicle is a train, v needs to be the front engine
 
 * @param check Checks if the replace is valid. No action is done at all
 
 * @param display_costs If set, a cost animation is shown (only if check is false)
 
 * @return CMD_ERROR if something went wrong. Otherwise the price of the replace
 
 */
 
static int32 MaybeReplaceVehicle(Vehicle *v, bool check, bool display_costs)
 
{
 
	Vehicle *w;
 
	const Player *p = GetPlayer(v->owner);
 
	byte flags = 0;
 
	int32 cost, temp_cost = 0;
 
	bool stopped = false;
 

	
 
	/* Remember the length in case we need to trim train later on
 
	 * If it's not a train, the value is unused
 
	 * round up to the length of the tiles used for the train instead of the train length instead
 
	 * Useful when newGRF uses custom length */
 
	uint16 old_total_length = (v->type == VEH_Train ?
 
		(v->u.rail.cached_total_length + TILE_SIZE - 1) / TILE_SIZE * TILE_SIZE :
 
		-1
 
	);
 

	
 

	
 
	_current_player = v->owner;
 

	
 
	assert(v->type == VEH_Train || v->type == VEH_Road || v->type == VEH_Ship || v->type == VEH_Aircraft);
 

	
 
	assert(v->vehstatus & VS_STOPPED); // the vehicle should have been stopped in VehicleEnteredDepotThisTick() if needed
 

	
 
	if (v->leave_depot_instantly) {
 
		// we stopped the vehicle to do this, so we have to remember to start it again when we are done
 
		// we need to store this info as the engine might be replaced and lose this info
 
		stopped = true;
 
	}
 

	
 
	for (;;) {
 
		cost = 0;
 
		w = v;
 
		do {
 
			if (w->type == VEH_Train && IsMultiheaded(w) && !IsTrainEngine(w)) {
 
				/* we build the rear ends of multiheaded trains with the front ones */
 
				continue;
 
			}
 

	
 
			// check if the vehicle should be replaced
 
			if (!p->engine_renew ||
 
					w->age - w->max_age < (p->engine_renew_months * 30) || // replace if engine is too old
 
					w->max_age == 0) { // rail cars got a max age of 0
 
				if (!EngineHasReplacementForPlayer(p, w->engine_type)) // updates to a new model
 
					continue;
 
			}
 

	
 
			/* Now replace the vehicle */
 
			temp_cost = ReplaceVehicle(&w, flags, cost);
 

	
 
			if (flags & DC_EXEC &&
 
					(w->type != VEH_Train || w->u.rail.first_engine == INVALID_ENGINE)) {
 
				/* now we bought a new engine and sold the old one. We need to fix the
 
				 * pointers in order to avoid pointing to the old one for trains: these
 
				 * pointers should point to the front engine and not the cars
 
				 */
 
				v = w;
 
			}
 

	
 
			if (!CmdFailed(temp_cost)) {
 
				cost += temp_cost;
 
			}
 
		} while (w->type == VEH_Train && (w = GetNextVehicle(w)) != NULL);
 

	
 
		if (!(flags & DC_EXEC) && (p->money64 < (int32)(cost + p->engine_renew_money) || cost == 0)) {
 
			if (!check && p->money64 < (int32)(cost + p->engine_renew_money) && ( _local_player == v->owner ) && cost != 0) {
 
				StringID message;
 
				SetDParam(0, v->unitnumber);
 
				switch (v->type) {
 
					case VEH_Train:    message = STR_TRAIN_AUTORENEW_FAILED;       break;
 
					case VEH_Road:     message = STR_ROADVEHICLE_AUTORENEW_FAILED; break;
 
					case VEH_Ship:     message = STR_SHIP_AUTORENEW_FAILED;        break;
 
					case VEH_Aircraft: message = STR_AIRCRAFT_AUTORENEW_FAILED;    break;
 
						// This should never happen
 
					default: NOT_REACHED(); message = 0; break;
 
				}
 

	
 
				AddNewsItem(message, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
 
			}
 
			if (stopped) v->vehstatus &= ~VS_STOPPED;
 
			if (display_costs) _current_player = OWNER_NONE;
 
			return CMD_ERROR;
 
		}
 

	
 
		if (flags & DC_EXEC) {
 
			break; // we are done replacing since the loop ran once with DC_EXEC
 
		} else if (check) {
 
			/* It's a test only and we know that we can do this
 
			 * NOTE: payment for wagon removal is NOT included in this price */
 
			return cost;
 
		}
 
		// now we redo the loop, but this time we actually do stuff since we know that we can do it
 
		flags |= DC_EXEC;
 
	}
 

	
 
	/* If setting is on to try not to exceed the old length of the train with the replacement */
 
	if (v->type == VEH_Train && p->renew_keep_length) {
 
		Vehicle *temp;
 
		w = v;
 

	
 
		while (v->u.rail.cached_total_length > old_total_length) {
 
			// the train is too long. We will remove cars one by one from the start of the train until it's short enough
 
			while (w != NULL && !(RailVehInfo(w->engine_type)->flags&RVI_WAGON) ) {
 
				w = GetNextVehicle(w);
 
			}
 
			if (w == NULL) {
 
				// we failed to make the train short enough
 
				SetDParam(0, v->unitnumber);
 
				AddNewsItem(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
 
				break;
 
			}
 
			temp = w;
 
			w = GetNextVehicle(w);
 
			DoCommand(0, (INVALID_VEHICLE << 16) | temp->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 
			MoveVehicleCargo(v, temp);
 
			cost += DoCommand(0, temp->index, 0, DC_EXEC, CMD_SELL_RAIL_WAGON);
 
		}
 
	}
 

	
 
	if (stopped) v->vehstatus &= ~VS_STOPPED;
 
	if (display_costs) {
 
		if (IsLocalPlayer()) ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost);
 
		_current_player = OWNER_NONE;
 
	}
 
	return cost;
 
}
 

	
 
/* Extend the list size for BuildDepotVehicleList() */
 
static inline void ExtendVehicleListSize(const Vehicle ***engine_list, uint16 *engine_list_length, uint16 step_size)
 
{
 
	*engine_list_length = min(*engine_list_length + step_size, GetMaxVehicleIndex() + 1);
 
	*engine_list = realloc((void*)*engine_list, (*engine_list_length) * sizeof((*engine_list)[0]));
 
}
 

	
 
/** Generates a list of vehicles inside a depot
 
 * Will enlarge allocated space for the list if they are too small, so it's ok to call with (pointer to NULL array, pointer to uninitised uint16, pointer to 0)
 
 * If one of the lists is not needed (say wagons when finding ships), all the pointers regarding that list should be set to NULL
 
 * @param Type type of vehicle
 
 * @param tile The tile the depot is located in
 
 * @param ***engine_list Pointer to a pointer to an array of vehicles in the depot (old list is freed and a new one is malloced)
 
 * @param *engine_list_length Allocated size of engine_list. Needs to be set to 0 when engine_list points to a NULL array
 
 * @param *engine_count The number of engines stored in the list
 
 * @param ***wagon_list Pointer to a pointer to an array of free wagons in the depot (old list is freed and a new one is malloced)
 
 * @param *wagon_list_length Allocated size of wagon_list. Needs to be set to 0 when wagon_list points to a NULL array
 
 * @param *wagon_count The number of engines stored in the list
 
 */
 
void BuildDepotVehicleList(byte type, TileIndex tile, Vehicle ***engine_list, uint16 *engine_list_length, uint16 *engine_count, Vehicle ***wagon_list, uint16 *wagon_list_length, uint16 *wagon_count)
 
{
 
	Vehicle *v;
 

	
 
	/* This function should never be called without an array to store results */
 
	assert(!(engine_list == NULL && type != VEH_Train));
 
	assert(!(type == VEH_Train && engine_list == NULL && wagon_list == NULL));
 

	
 
	/* Both array and the length should either be NULL to disable the list or both should not be NULL */
 
	assert((engine_list == NULL && engine_list_length == NULL) || (engine_list != NULL && engine_list_length != NULL));
 
	assert((wagon_list == NULL && wagon_list_length == NULL) || (wagon_list != NULL && wagon_list_length != NULL));
 

	
 
	assert(!(engine_list != NULL && engine_count == NULL));
 
	assert(!(wagon_list != NULL && wagon_count == NULL));
 

	
 
	if (engine_count != NULL) *engine_count = 0;
 
	if (wagon_count != NULL) *wagon_count = 0;
 

	
 
	switch (type) {
 
		case VEH_Train:
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->tile == tile && v->type == VEH_Train && v->u.rail.track == 0x80) {
 
					if (IsFrontEngine(v)) {
 
						if (engine_list == NULL) continue;
 
						if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25);
 
						(*engine_list)[(*engine_count)++] = v;
 
					} else if (IsFreeWagon(v)) {
 
						if (wagon_list == NULL) continue;
 
						if (*wagon_count == *wagon_list_length) ExtendVehicleListSize((const Vehicle***)wagon_list, wagon_list_length, 25);
 
						(*wagon_list)[(*wagon_count)++] = v;
 
					}
 
				}
 
			}
 
			break;
 

	
 
		case VEH_Road:
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->tile == tile && v->type == VEH_Road && IsRoadVehInDepot(v)) {
 
					if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25);
 
					(*engine_list)[(*engine_count)++] = v;
 
				}
 
			}
 
			break;
 

	
 
		case VEH_Ship:
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->tile == tile && v->type == VEH_Ship && IsShipInDepot(v)) {
 
					if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25);
 
					(*engine_list)[(*engine_count)++] = v;
 
				}
 
			}
 
			break;
 

	
 
		case VEH_Aircraft:
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->tile == tile &&
 
						v->type == VEH_Aircraft &&
 
						v->subtype <= 2 &&
 
						v->vehstatus & VS_HIDDEN) {
 
					if (*engine_count == *engine_list_length) ExtendVehicleListSize((const Vehicle***)engine_list, engine_list_length, 25);
 
					(*engine_list)[(*engine_count)++] = v;
 
				}
 
			}
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
/**
 
* @param sort_list list to store the list in. Either NULL or the length length_of_array tells
 
* @param length_of_array informs the length allocated for sort_list. This is not the same as the number of vehicles in the list. Needs to be 0 when sort_list is NULL
 
* @param type type of vehicle
 
* @param owner PlayerID of owner to generate a list for
 
* @param station index of station to generate a list for. INVALID_STATION when not used
 
* @param order index of oder to generate a list for. INVALID_ORDER when not used
 
* @param window_type tells what kind of window the list is for. Use the VLW flags in vehicle_gui.h
 
* @return the number of vehicles added to the list
 
*/
 
uint GenerateVehicleSortList(const Vehicle ***sort_list, uint16 *length_of_array, byte type, PlayerID owner, StationID station, OrderID order, uint16 depot_airport_index, uint16 window_type)
 
{
 
	const uint subtype = (type != VEH_Aircraft) ? Train_Front : 2;
 
	uint n = 0;
 
	const Vehicle *v;
 

	
 
	switch (window_type) {
 
		case VLW_STATION_LIST: {
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->type == type && (
 
					(type == VEH_Train && IsFrontEngine(v)) ||
 
					(type != VEH_Train && v->subtype <= subtype))) {
 
					const Order *order;
 

	
 
					FOR_VEHICLE_ORDERS(v, order) {
 
						if (order->type == OT_GOTO_STATION && order->dest == station) {
 
							if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 50);
 
							(*sort_list)[n++] = v;
 
							break;
 
						}
 
					}
 
				}
 
			}
 
			break;
 
		}
 

	
 
		case VLW_SHARED_ORDERS: {
 
			FOR_ALL_VEHICLES(v) {
 
				/* Find a vehicle with the order in question */
 
				if (v->orders != NULL && v->orders->index == order) break;
 
			}
 

	
 
			if (v != NULL && v->orders != NULL && v->orders->index == order) {
 
				/* Only try to make the list if we found a vehicle using the order in question */
 
				for (v = GetFirstVehicleFromSharedList(v); v != NULL; v = v->next_shared) {
 
					if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 25);
 
					(*sort_list)[n++] = v;
 
				}
 
			}
 
			break;
 
		}
 

	
 
		case VLW_STANDARD: {
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->type == type && v->owner == owner && (
 
					(type == VEH_Train && IsFrontEngine(v)) ||
 
					(type != VEH_Train && v->subtype <= subtype))) {
 
					/* TODO find a better estimate on the total number of vehicles for current player */
 
					if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, GetNumVehicles()/4);
 
					(*sort_list)[n++] = v;
 
				}
 
			}
 
			break;
 
		}
 

	
 
		case VLW_DEPOT_LIST: {
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->type == type && (
 
					(type == VEH_Train && IsFrontEngine(v)) ||
 
					(type != VEH_Train && v->subtype <= subtype))) {
 
					const Order *order;
 

	
 
					FOR_VEHICLE_ORDERS(v, order) {
 
						if (order->type == OT_GOTO_DEPOT && order->dest == depot_airport_index) {
 
							if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, 25);
 
							(*sort_list)[n++] = v;
 
							break;
 
						}
 
					}
 
				}
 
			}
 
			break;
 
		}
 

	
 
		default: NOT_REACHED(); break;
 
	}
 

	
 
	if ((n + 100) < *length_of_array) {
 
		/* We allocated way too much for sort_list.
 
		 * Now we will reduce how much we allocated.
 
		 * We will still make it have room for 50 extra vehicles to prevent having
 
		 * to move the whole array if just one vehicle is added later */
 
		*length_of_array = n + 50;
 
		*sort_list = realloc((void*)*sort_list, (*length_of_array) * sizeof((*sort_list)[0]));
 
	}
 

	
 
	return n;
 
}
 

	
 
/** send all vehicles of type to depots
 
 * @param type type of vehicle
 
 * @param flags the flags used for DoCommand()
 
 * @param service should the vehicles only get service in the depots
 
 * @param owner PlayerID of owner of the vehicles to send
 
 * @param VLW_flag tells what kind of list requested the goto depot
 
 * @return 0 for success and CMD_ERROR if no vehicle is able to go to depot
 
 */
 
int32 SendAllVehiclesToDepot(byte type, uint32 flags, bool service, PlayerID owner, uint16 vlw_flag, uint32 id)
 
{
 
	const Vehicle **sort_list = NULL;
 
	uint n, i;
 
	uint16 array_length = 0;
 

	
 
	n = GenerateVehicleSortList(&sort_list, &array_length, type, owner, id, id, id, vlw_flag);
 

	
 
	/* Send all the vehicles to a depot */
 
	for (i = 0; i < n; i++) {
 
		const Vehicle *v = sort_list[i];
 
		int32 ret = DoCommand(v->tile, v->index, service | DEPOT_DONT_CANCEL, flags, CMD_SEND_TO_DEPOT(type));
 

	
 
		/* Return 0 if DC_EXEC is not set this is a valid goto depot command)
 
			* In this case we know that at least one vehicle can be sent to a depot
 
			* and we will issue the command. We can now safely quit the loop, knowing
 
			* it will succeed at least once. With DC_EXEC we really need to send them to the depot */
 
		if (!CmdFailed(ret) && !(flags & DC_EXEC)) {
 
			free((void*)sort_list);
 
			return 0;
 
		}
 
	}
 

	
 
	free((void*)sort_list);
 
	return (flags & DC_EXEC) ? 0 : CMD_ERROR;
 
}
 

	
 
bool IsVehicleInDepot(const Vehicle *v)
 
{
 
	switch (v->type) {
 
		case VEH_Train:    return CheckTrainInDepot(v, false) != -1;
 
		case VEH_Road:     return IsRoadVehInDepot(v);
 
		case VEH_Ship:     return IsShipInDepot(v);
 
		case VEH_Aircraft: return IsAircraftInHangar(v);
 
		default: NOT_REACHED();
 
	}
 
	return false;
 
}
 

	
 
void VehicleEnterDepot(Vehicle *v)
 
{
 
	switch (v->type) {
 
		case VEH_Train:
 
			InvalidateWindowClasses(WC_TRAINS_LIST);
 
			if (!IsFrontEngine(v)) v = GetFirstVehicleInChain(v);
 
			UpdateSignalsOnSegment(v->tile, GetRailDepotDirection(v->tile));
 
			v->load_unload_time_rem = 0;
 
			break;
 

	
 
		case VEH_Road:
 
			InvalidateWindowClasses(WC_ROADVEH_LIST);
 
			v->u.road.state = 254;
 
			break;
 

	
 
		case VEH_Ship:
 
			InvalidateWindowClasses(WC_SHIPS_LIST);
 
			v->u.ship.state = 0x80;
 
			RecalcShipStuff(v);
 
			break;
 

	
 
		case VEH_Aircraft:
 
			InvalidateWindowClasses(WC_AIRCRAFT_LIST);
 
			HandleAircraftEnterHangar(v);
 
			break;
 
		default: NOT_REACHED();
 
	}
 

	
 
	if (v->type != VEH_Train) {
 
		/* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
 
		 * We only increase the number of vehicles when the first one enters, so we will not need to search for more vehicles in the depot */
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	}
 
	InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 

	
 
	v->vehstatus |= VS_HIDDEN;
 
	v->cur_speed = 0;
 

	
 
	VehicleServiceInDepot(v);
 

	
 
	TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT) {
 
		Order t;
 

	
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 

	
 
		t = v->current_order;
 
		v->current_order.type = OT_DUMMY;
 
		v->current_order.flags = 0;
 

	
 
		if (t.refit_cargo < NUM_CARGO) {
 
			int32 cost;
 

	
 
			_current_player = v->owner;
 
			cost = DoCommand(v->tile, v->index, t.refit_cargo | t.refit_subtype << 8, DC_EXEC, CMD_REFIT_VEH(v->type));
 

	
 
			if (CmdFailed(cost)) {
 
				v->leave_depot_instantly = false; // We ensure that the vehicle stays in the depot
 
				if (v->owner == _local_player) {
 
					/* Notify the user that we stopped the vehicle */
 
					SetDParam(0, _vehicle_type_names[v->type - 0x10]);
 
					SetDParam(1, v->unitnumber);
 
					AddNewsItem(STR_ORDER_REFIT_FAILED, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
 
				}
 
			} else if (v->owner == _local_player && cost != 0) {
 
				ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost);
 
			}
 
		}
 

	
 
		if (HASBIT(t.flags, OFB_PART_OF_ORDERS)) {
 
			/* Part of orders */
 
			if (v->type == VEH_Train) v->u.rail.days_since_order_progr = 0;
 
			v->cur_order_index++;
 
		} else if (HASBIT(t.flags, OFB_HALT_IN_DEPOT)) {
 
			/* Force depot visit */
 
			v->vehstatus |= VS_STOPPED;
 
			if (v->owner == _local_player) {
 
				StringID string;
 

	
 
				switch (v->type) {
 
					case VEH_Train:    string = STR_8814_TRAIN_IS_WAITING_IN_DEPOT; break;
 
					case VEH_Road:     string = STR_9016_ROAD_VEHICLE_IS_WAITING;   break;
 
					case VEH_Ship:     string = STR_981C_SHIP_IS_WAITING_IN_DEPOT;  break;
 
					case VEH_Aircraft: string = STR_A014_AIRCRAFT_IS_WAITING_IN;    break;
 
					default: NOT_REACHED(); string = STR_EMPTY; // Set the string to something to avoid a compiler warning
 
				}
 

	
 
				SetDParam(0, v->unitnumber);
 
				AddNewsItem(string, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0),	v->index, 0);
 
			}
 
		}
 
	}
 
}
 

	
 
/** Give a custom name to your vehicle
 
 * @param tile unused
 
 * @param p1 vehicle ID to name
 
 * @param p2 unused
 
 */
 
int32 CmdNameVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	StringID str;
 

	
 
	if (!IsValidVehicleID(p1) || _cmd_text[0] == '\0') return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	str = AllocateNameUnique(_cmd_text, 2);
 
	if (str == 0) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		StringID old_str = v->string_id;
 
		v->string_id = str;
 
		DeleteName(old_str);
 
		ResortVehicleLists();
 
		MarkWholeScreenDirty();
 
	} else {
 
		DeleteName(str);
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
/** Change the service interval of a vehicle
 
 * @param tile unused
 
 * @param p1 vehicle ID that is being service-interval-changed
 
 * @param p2 new service interval
 
 */
 
int32 CmdChangeServiceInt(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle* v;
 
	uint16 serv_int = GetServiceIntervalClamped(p2); /* Double check the service interval from the user-input */
 

	
 
	if (serv_int != p2 || !IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

	
 
	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		v->service_interval = serv_int;
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
static Rect _old_vehicle_coords;
 

	
 
void BeginVehicleMove(Vehicle *v) {
 
	_old_vehicle_coords.left = v->left_coord;
 
	_old_vehicle_coords.top = v->top_coord;
 
	_old_vehicle_coords.right = v->right_coord;
 
	_old_vehicle_coords.bottom = v->bottom_coord;
 
}
 

	
 
void EndVehicleMove(Vehicle *v)
 
{
 
	MarkAllViewportsDirty(
 
		min(_old_vehicle_coords.left,v->left_coord),
 
		min(_old_vehicle_coords.top,v->top_coord),
 
		max(_old_vehicle_coords.right,v->right_coord)+1,
 
		max(_old_vehicle_coords.bottom,v->bottom_coord)+1
 
	);
 
}
 

	
 
/* returns true if staying in the same tile */
 
bool GetNewVehiclePos(const Vehicle *v, GetNewVehiclePosResult *gp)
 
{
 
	static const int8 _delta_coord[16] = {
 
		-1,-1,-1, 0, 1, 1, 1, 0, /* x */
 
		-1, 0, 1, 1, 1, 0,-1,-1, /* y */
 
	};
 

	
 
	int x = v->x_pos + _delta_coord[v->direction];
 
	int y = v->y_pos + _delta_coord[v->direction + 8];
 

	
 
	gp->x = x;
 
	gp->y = y;
 
	gp->old_tile = v->tile;
 
	gp->new_tile = TileVirtXY(x, y);
 
	return gp->old_tile == gp->new_tile;
 
}
 

	
 
static const Direction _new_direction_table[] = {
 
	DIR_N , DIR_NW, DIR_W ,
 
	DIR_NE, DIR_SE, DIR_SW,
 
	DIR_E , DIR_SE, DIR_S
 
};
 

	
 
Direction GetDirectionTowards(const Vehicle* v, int x, int y)
 
{
 
	Direction dir;
 
	DirDiff dirdiff;
 
	int i = 0;
 

	
 
	if (y >= v->y_pos) {
 
		if (y != v->y_pos) i+=3;
 
		i+=3;
 
	}
 

	
 
	if (x >= v->x_pos) {
 
		if (x != v->x_pos) i++;
 
		i++;
 
	}
 

	
 
	dir = v->direction;
 

	
 
	dirdiff = DirDifference(_new_direction_table[i], dir);
 
	if (dirdiff == DIRDIFF_SAME) return dir;
 
	return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
 
}
 

	
 
Trackdir GetVehicleTrackdir(const Vehicle* v)
 
{
 
	if (v->vehstatus & VS_CRASHED) return 0xFF;
 

	
 
	switch (v->type) {
 
		case VEH_Train:
 
			if (v->u.rail.track == 0x80) /* We'll assume the train is facing outwards */
 
				return DiagdirToDiagTrackdir(GetRailDepotDirection(v->tile)); /* Train in depot */
 

	
 
			if (v->u.rail.track == 0x40) /* train in tunnel, so just use his direction and assume a diagonal track */
 
				return DiagdirToDiagTrackdir(DirToDiagDir(v->direction));
 

	
 
			return TrackDirectionToTrackdir(FIND_FIRST_BIT(v->u.rail.track),v->direction);
 

	
 
		case VEH_Ship:
 
			if (IsShipInDepot(v))
 
				/* We'll assume the ship is facing outwards */
 
				return DiagdirToDiagTrackdir(GetShipDepotDirection(v->tile));
 

	
 
			return TrackDirectionToTrackdir(FIND_FIRST_BIT(v->u.ship.state),v->direction);
 

	
 
		case VEH_Road:
 
			if (IsRoadVehInDepot(v)) /* We'll assume the road vehicle is facing outwards */
 
				return DiagdirToDiagTrackdir(GetRoadDepotDirection(v->tile));
 

	
 
			if (IsRoadStopTile(v->tile)) /* We'll assume the road vehicle is facing outwards */
 
				return DiagdirToDiagTrackdir(GetRoadStopDir(v->tile)); /* Road vehicle in a station */
 

	
 
			/* If vehicle's state is a valid track direction (vehicle is not turning around) return it */
 
			if ((v->u.road.state & 7) < 6) return v->u.road.state;
 

	
 
			/* Vehicle is turning around, get the direction from vehicle's direction */
 
			return DiagdirToDiagTrackdir(DirToDiagDir(v->direction));
 

	
 
		/* case VEH_Aircraft: case VEH_Special: case VEH_Disaster: */
 
		default: return 0xFF;
 
	}
 
}
 
/* Return value has bit 0x2 set, when the vehicle enters a station. Then,
 
 * result << 8 contains the id of the station entered. If the return value has
 
 * bit 0x8 set, the vehicle could not and did not enter the tile. Are there
 
 * other bits that can be set? */
 
uint32 VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
 
{
 
	return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
 
}
 

	
 
UnitID GetFreeUnitNumber(byte type)
 
{
 
	UnitID unit, max = 0;
 
	const Vehicle *u;
 
	static bool *cache = NULL;
 
	static UnitID gmax = 0;
 

	
 
	switch (type) {
 
		case VEH_Train:    max = _patches.max_trains; break;
 
		case VEH_Road:     max = _patches.max_roadveh; break;
 
		case VEH_Ship:     max = _patches.max_ships; break;
 
		case VEH_Aircraft: max = _patches.max_aircraft; break;
 
		default: NOT_REACHED();
 
	}
 

	
 
	if (max == 0) {
 
		/* we can't build any of this kind of vehicle, so we just return 1 instead of looking for a free number
 
		 * a max of 0 will cause the following code to write to a NULL pointer
 
		 * We know that 1 is bigger than the max allowed vehicle number, so it's the same as returning something, that is too big
 
		 */
 
		return 1;
 
	}
 

	
 
	if (max > gmax) {
 
		gmax = max;
 
		free(cache);
 
		cache = malloc((max + 1) * sizeof(*cache));
 
	}
 

	
 
	// Clear the cache
 
	memset(cache, 0, (max + 1) * sizeof(*cache));
 

	
 
	// Fill the cache
 
	FOR_ALL_VEHICLES(u) {
 
		if (u->type == type && u->owner == _current_player && u->unitnumber != 0 && u->unitnumber <= max)
 
			cache[u->unitnumber] = true;
 
	}
 

	
 
	// Find the first unused unit number
 
	for (unit = 1; unit <= max; unit++) {
 
		if (!cache[unit]) break;
 
	}
 

	
 
	return unit;
 
}
 

	
 
static PalSpriteID GetEngineColourMap(EngineID engine_type, PlayerID player, EngineID parent_engine_type, CargoID cargo_type)
 
{
 
	SpriteID map;
 
	const Player *p = GetPlayer(player);
 
	LiveryScheme scheme = LS_DEFAULT;
 

	
 
	/* The default livery is always available for use, but its in_use flag determines
 
	 * whether any _other_ liveries are in use. */
 
	if (p->livery[LS_DEFAULT].in_use && (_patches.liveries == 2 || (_patches.liveries == 1 && player == _local_player))) {
 
		/* Determine the livery scheme to use */
 
		switch (GetEngine(engine_type)->type) {
 
			case VEH_Train: {
 
				switch (_engine_info[engine_type].railtype) {
 
					case RAILTYPE_RAIL:
 
					case RAILTYPE_ELECTRIC:
 
					{
 
						const RailVehicleInfo *rvi = RailVehInfo(engine_type);
 

	
 
						if (cargo_type == CT_INVALID) cargo_type = rvi->cargo_type;
 
						if (rvi->flags & RVI_WAGON) {
 
							if (cargo_type == CT_PASSENGERS || cargo_type == CT_MAIL || cargo_type == CT_VALUABLES) {
 
								if (parent_engine_type == INVALID_ENGINE) {
 
									scheme = LS_PASSENGER_WAGON_STEAM;
 
								} else {
 
									switch (RailVehInfo(parent_engine_type)->engclass) {
 
										case 0: scheme = LS_PASSENGER_WAGON_STEAM; break;
 
										case 1: scheme = LS_PASSENGER_WAGON_DIESEL; break;
 
										case 2: scheme = LS_PASSENGER_WAGON_ELECTRIC; break;
 
									}
 
								}
 
							} else {
 
								scheme = LS_FREIGHT_WAGON;
 
							}
 
						} else {
 
							bool is_mu = HASBIT(_engine_info[engine_type].misc_flags, EF_RAIL_IS_MU);
 

	
 
							switch (rvi->engclass) {
 
								case 0: scheme = LS_STEAM; break;
 
								case 1: scheme = is_mu ? LS_DMU : LS_DIESEL; break;
 
								case 2: scheme = is_mu ? LS_EMU : LS_ELECTRIC; break;
 
							}
 
						}
 
						break;
 
					}
 

	
 
					case RAILTYPE_MONO: scheme = LS_MONORAIL; break;
 
					case RAILTYPE_MAGLEV: scheme = LS_MAGLEV; break;
 
				}
 
				break;
 
			}
 

	
 
			case VEH_Road: {
 
				const RoadVehicleInfo *rvi = RoadVehInfo(engine_type);
 
				if (cargo_type == CT_INVALID) cargo_type = rvi->cargo_type;
 
				scheme = (cargo_type == CT_PASSENGERS) ? LS_BUS : LS_TRUCK;
 
				break;
 
			}
 

	
 
			case VEH_Ship: {
 
				const ShipVehicleInfo *svi = ShipVehInfo(engine_type);
 
				if (cargo_type == CT_INVALID) cargo_type = svi->cargo_type;
 
				scheme = (cargo_type == CT_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
 
				break;
 
			}
 

	
 
			case VEH_Aircraft: {
 
				const AircraftVehicleInfo *avi = AircraftVehInfo(engine_type);
 
				if (cargo_type == CT_INVALID) cargo_type = CT_PASSENGERS;
 
				switch (avi->subtype) {
 
					case 0: scheme = LS_HELICOPTER; break;
 
					case 1: scheme = LS_SMALL_PLANE; break;
 
					case 3: scheme = LS_LARGE_PLANE; break;
 
				}
 
				break;
 
			}
 
		}
 

	
 
		/* Switch back to the default scheme if the resolved scheme is not in use */
 
		if (!p->livery[scheme].in_use) scheme = LS_DEFAULT;
 
	}
 

	
 
	map = HASBIT(EngInfo(engine_type)->misc_flags, EF_USES_2CC) ?
 
		(SPR_2CCMAP_BASE + p->livery[scheme].colour1 + p->livery[scheme].colour2 * 16) :
 
		(PALETTE_RECOLOR_START + p->livery[scheme].colour1);
 

	
 
	return SPRITE_PALETTE(map << PALETTE_SPRITE_START);
 
}
 

	
 
PalSpriteID GetEnginePalette(EngineID engine_type, PlayerID player)
 
{
 
	return GetEngineColourMap(engine_type, player, INVALID_ENGINE, CT_INVALID);
 
}
 

	
 
PalSpriteID GetVehiclePalette(const Vehicle *v)
 
{
 
	if (v->type == VEH_Train) {
 
		return GetEngineColourMap(
 
			(v->u.rail.first_engine != INVALID_ENGINE && (IsArticulatedPart(v) || UsesWagonOverride(v))) ?
 
				v->u.rail.first_engine : v->engine_type,
 
			v->owner,
 
			v->u.rail.first_engine,
 
			v->cargo_type);
 
	}
 

	
 
	return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v->cargo_type);
 
}
 

	
 
// Save and load of vehicles
 
const SaveLoad _common_veh_desc[] = {
 
	    SLE_VAR(Vehicle, subtype,              SLE_UINT8),
 

	
 
	    SLE_REF(Vehicle, next,                 REF_VEHICLE_OLD),
 
	    SLE_VAR(Vehicle, string_id,            SLE_STRINGID),
 
	SLE_CONDVAR(Vehicle, unitnumber,           SLE_FILE_U8  | SLE_VAR_U16,  0, 7),
 
	SLE_CONDVAR(Vehicle, unitnumber,           SLE_UINT16,                  8, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, owner,                SLE_UINT8),
 
	SLE_CONDVAR(Vehicle, tile,                 SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Vehicle, tile,                 SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, dest_tile,            SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Vehicle, dest_tile,            SLE_UINT32,                  6, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Vehicle, x_pos,                SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Vehicle, x_pos,                SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, y_pos,                SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Vehicle, y_pos,                SLE_UINT32,                  6, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, z_pos,                SLE_UINT8),
 
	    SLE_VAR(Vehicle, direction,            SLE_UINT8),
 

	
 
	    SLE_VAR(Vehicle, cur_image,            SLE_UINT16),
 
	    SLE_VAR(Vehicle, spritenum,            SLE_UINT8),
 
	    SLE_VAR(Vehicle, sprite_width,         SLE_UINT8),
 
	    SLE_VAR(Vehicle, sprite_height,        SLE_UINT8),
 
	    SLE_VAR(Vehicle, z_height,             SLE_UINT8),
 
	    SLE_VAR(Vehicle, x_offs,               SLE_INT8),
 
	    SLE_VAR(Vehicle, y_offs,               SLE_INT8),
 
	    SLE_VAR(Vehicle, engine_type,          SLE_UINT16),
 

	
 
	    SLE_VAR(Vehicle, max_speed,            SLE_UINT16),
 
	    SLE_VAR(Vehicle, cur_speed,            SLE_UINT16),
 
	    SLE_VAR(Vehicle, subspeed,             SLE_UINT8),
 
	    SLE_VAR(Vehicle, acceleration,         SLE_UINT8),
 
	    SLE_VAR(Vehicle, progress,             SLE_UINT8),
 

	
 
	    SLE_VAR(Vehicle, vehstatus,            SLE_UINT8),
 
	SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8  | SLE_VAR_U16,  0, 4),
 
	SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16,                  5, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(Vehicle, cargo_type,           SLE_UINT8),
 
	SLE_CONDVAR(Vehicle, cargo_subtype,        SLE_UINT8,                  35, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, cargo_days,           SLE_UINT8),
 
	SLE_CONDVAR(Vehicle, cargo_source,         SLE_FILE_U8  | SLE_VAR_U16,  0, 6),
 
	SLE_CONDVAR(Vehicle, cargo_source,         SLE_UINT16,                  7, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, cargo_cap,            SLE_UINT16),
 
	    SLE_VAR(Vehicle, cargo_count,          SLE_UINT16),
 

	
 
	    SLE_VAR(Vehicle, day_counter,          SLE_UINT8),
 
	    SLE_VAR(Vehicle, tick_counter,         SLE_UINT8),
 

	
 
	    SLE_VAR(Vehicle, cur_order_index,      SLE_UINT8),
 
	    SLE_VAR(Vehicle, num_orders,           SLE_UINT8),
 

	
 
	/* This next line is for version 4 and prior compatibility.. it temporarily reads
 
	    type and flags (which were both 4 bits) into type. Later on this is
 
	    converted correctly */
 
	SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, type), SLE_UINT8,                 0, 4),
 
	SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
 

	
 
	/* Orders for version 5 and on */
 
	SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, type),  SLE_UINT8,  5, SL_MAX_VERSION),
 
	SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, flags), SLE_UINT8,  5, SL_MAX_VERSION),
 
	SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, dest),  SLE_UINT16, 5, SL_MAX_VERSION),
 

	
 
	/* Refit in current order */
 
	SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, refit_cargo),    SLE_UINT8, 36, SL_MAX_VERSION),
 
	SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, refit_subtype),  SLE_UINT8, 36, SL_MAX_VERSION),
 

	
 
	    SLE_REF(Vehicle, orders,               REF_ORDER),
 

	
 
	SLE_CONDVAR(Vehicle, age,                  SLE_FILE_U16 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, age,                  SLE_INT32,                  31, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, max_age,              SLE_FILE_U16 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, max_age,              SLE_INT32,                  31, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32,                  31, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, service_interval,     SLE_FILE_U16 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, service_interval,     SLE_INT32,                  31, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, reliability,          SLE_UINT16),
 
	    SLE_VAR(Vehicle, reliability_spd_dec,  SLE_UINT16),
 
	    SLE_VAR(Vehicle, breakdown_ctr,        SLE_UINT8),
 
	    SLE_VAR(Vehicle, breakdown_delay,      SLE_UINT8),
 
	    SLE_VAR(Vehicle, breakdowns_since_last_service, SLE_UINT8),
 
	    SLE_VAR(Vehicle, breakdown_chance,     SLE_UINT8),
 
	SLE_CONDVAR(Vehicle, build_year,           SLE_FILE_U8 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, build_year,           SLE_INT32,                 31, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16),
 
	SLE_CONDVAR(Vehicle, load_status,          SLE_UINT8,                 40, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(Vehicle, profit_this_year,     SLE_INT32),
 
	    SLE_VAR(Vehicle, profit_last_year,     SLE_INT32),
 
	    SLE_VAR(Vehicle, value,                SLE_UINT32),
 

	
 
	    SLE_VAR(Vehicle, random_bits,          SLE_UINT8),
 
	    SLE_VAR(Vehicle, waiting_triggers,     SLE_UINT8),
 

	
 
	    SLE_REF(Vehicle, next_shared,          REF_VEHICLE),
 
	    SLE_REF(Vehicle, prev_shared,          REF_VEHICLE),
 

	
 
	// reserve extra space in savegame here. (currently 10 bytes)
 
	SLE_CONDNULL(10,                                                       2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 

	
 
static const SaveLoad _train_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_Train, 0), // Train type. VEH_Train in mem, 0 in file.
 
	SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRail, crash_anim_pos),         SLE_UINT16),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRail, force_proceed),          SLE_UINT8),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRail, railtype),               SLE_UINT8),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRail, track),                  SLE_UINT8),
 

	
 
	SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleRail, flags),                  SLE_UINT8,  2, SL_MAX_VERSION),
 
	SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleRail, days_since_order_progr), SLE_UINT16, 2, SL_MAX_VERSION),
 

	
 
	SLE_CONDNULL(2, 2, 19),
 
	// reserve extra space in savegame here. (currently 11 bytes)
 
	SLE_CONDNULL(11, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _roadveh_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_Road, 1), // Road type. VEH_Road in mem, 1 in file.
 
	SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, state),          SLE_UINT8),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, frame),          SLE_UINT8),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, blocked_ctr),    SLE_UINT16),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, overtaking),     SLE_UINT8),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, overtaking_ctr), SLE_UINT8),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, crashed_ctr),    SLE_UINT16),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, reverse_ctr),    SLE_UINT8),
 

	
 
	SLE_CONDREFX(offsetof(Vehicle, u) + offsetof(VehicleRoad, slot),     REF_ROADSTOPS, 6, SL_MAX_VERSION),
 
	SLE_CONDNULL(1,                                                                     6, SL_MAX_VERSION),
 
	SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleRoad, slot_age), SLE_UINT8,     6, SL_MAX_VERSION),
 
	// reserve extra space in savegame here. (currently 16 bytes)
 
	SLE_CONDNULL(16,                                                                    2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _ship_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_Ship, 2), // Ship type. VEH_Ship in mem, 2 in file.
 
	SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
 
	SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleShip, state), SLE_UINT8),
 

	
 
	// reserve extra space in savegame here. (currently 16 bytes)
 
	SLE_CONDNULL(16, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _aircraft_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_Aircraft, 3), // Aircraft type. VEH_Aircraft in mem, 3 in file.
 
	SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleAir, crashed_counter), SLE_UINT16),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleAir, pos),             SLE_UINT8),
 

	
 
	SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleAir, targetairport),   SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
 
	SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleAir, targetairport),   SLE_UINT16,                5, SL_MAX_VERSION),
 

	
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleAir, state),           SLE_UINT8),
 

	
 
	SLE_CONDVARX(offsetof(Vehicle, u) + offsetof(VehicleAir, previous_pos),    SLE_UINT8,                 2, SL_MAX_VERSION),
 

	
 
	// reserve extra space in savegame here. (currently 15 bytes)
 
	SLE_CONDNULL(15,                                                                                      2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _special_desc[] = {
 
	SLE_WRITEBYTE(Vehicle,type,VEH_Special, 4),
 

	
 
	    SLE_VAR(Vehicle, subtype,       SLE_UINT8),
 

	
 
	SLE_CONDVAR(Vehicle, tile,          SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(Vehicle, tile,          SLE_UINT32,                 6, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Vehicle, x_pos,         SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
 
	SLE_CONDVAR(Vehicle, x_pos,         SLE_INT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, y_pos,         SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
 
	SLE_CONDVAR(Vehicle, y_pos,         SLE_INT32,                  6, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, z_pos,         SLE_UINT8),
 

	
 
	    SLE_VAR(Vehicle, cur_image,     SLE_UINT16),
 
	    SLE_VAR(Vehicle, sprite_width,  SLE_UINT8),
 
	    SLE_VAR(Vehicle, sprite_height, SLE_UINT8),
 
	    SLE_VAR(Vehicle, z_height,      SLE_UINT8),
 
	    SLE_VAR(Vehicle, x_offs,        SLE_INT8),
 
	    SLE_VAR(Vehicle, y_offs,        SLE_INT8),
 
	    SLE_VAR(Vehicle, progress,      SLE_UINT8),
 
	    SLE_VAR(Vehicle, vehstatus,     SLE_UINT8),
 

	
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleSpecial, unk0), SLE_UINT16),
 
	    SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleSpecial, unk2), SLE_UINT8),
 

	
 
	// reserve extra space in savegame here. (currently 16 bytes)
 
	SLE_CONDNULL(16, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _disaster_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_Disaster, 5),
 

	
 
	    SLE_REF(Vehicle, next,          REF_VEHICLE_OLD),
 

	
 
	    SLE_VAR(Vehicle, subtype,       SLE_UINT8),
 
	SLE_CONDVAR(Vehicle, tile,          SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Vehicle, tile,          SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, dest_tile,     SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Vehicle, dest_tile,     SLE_UINT32,                  6, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Vehicle, x_pos,         SLE_FILE_I16 | SLE_VAR_I32,  0, 5),
 
	SLE_CONDVAR(Vehicle, x_pos,         SLE_INT32,                   6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, y_pos,         SLE_FILE_I16 | SLE_VAR_I32,  0, 5),
 
	SLE_CONDVAR(Vehicle, y_pos,         SLE_INT32,                   6, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, z_pos,         SLE_UINT8),
 
	    SLE_VAR(Vehicle, direction,     SLE_UINT8),
 

	
 
	    SLE_VAR(Vehicle, x_offs,        SLE_INT8),
 
	    SLE_VAR(Vehicle, y_offs,        SLE_INT8),
 
	    SLE_VAR(Vehicle, sprite_width,  SLE_UINT8),
 
	    SLE_VAR(Vehicle, sprite_height, SLE_UINT8),
 
	    SLE_VAR(Vehicle, z_height,      SLE_UINT8),
 
	    SLE_VAR(Vehicle, owner,         SLE_UINT8),
 
	    SLE_VAR(Vehicle, vehstatus,     SLE_UINT8),
 
	SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
 
	SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, dest), SLE_UINT16,                5, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(Vehicle, cur_image,     SLE_UINT16),
 
	SLE_CONDVAR(Vehicle, age,           SLE_FILE_U16 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, age,           SLE_INT32,                  31, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, tick_counter,  SLE_UINT8),
 

	
 
	   SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleDisaster, image_override), SLE_UINT16),
 
	   SLE_VARX(offsetof(Vehicle, u) + offsetof(VehicleDisaster, unk2),           SLE_UINT16),
 

	
 
	// reserve extra space in savegame here. (currently 16 bytes)
 
	SLE_CONDNULL(16,                                                 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 

	
 
static const void *_veh_descs[] = {
 
	_train_desc,
 
	_roadveh_desc,
 
	_ship_desc,
 
	_aircraft_desc,
 
	_special_desc,
 
	_disaster_desc,
 
};
 

	
 
// Will be called when the vehicles need to be saved.
 
static void Save_VEHS(void)
 
{
 
	Vehicle *v;
 
	// Write the vehicles
 
	FOR_ALL_VEHICLES(v) {
 
		SlSetArrayIndex(v->index);
 
		SlObject(v, _veh_descs[v->type - 0x10]);
 
	}
 
}
 

	
 
// Will be called when vehicles need to be loaded.
 
static void Load_VEHS(void)
 
{
 
	int index;
 
	Vehicle *v;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		Vehicle *v;
 

	
 
		if (!AddBlockIfNeeded(&_Vehicle_pool, index))
 
			error("Vehicles: failed loading savegame: too many vehicles");
 

	
 
		v = GetVehicle(index);
 
		SlObject(v, _veh_descs[SlReadByte()]);
 

	
 
		/* Old savegames used 'last_station_visited = 0xFF' */
 
		if (CheckSavegameVersion(5) && v->last_station_visited == 0xFF)
 
			v->last_station_visited = INVALID_STATION;
 

	
 
		if (CheckSavegameVersion(5)) {
 
			/* Convert the current_order.type (which is a mix of type and flags, because
 
			 *  in those versions, they both were 4 bits big) to type and flags */
 
			v->current_order.flags = (v->current_order.type & 0xF0) >> 4;
 
			v->current_order.type  =  v->current_order.type & 0x0F;
 
		}
 
	}
 

	
 
	/* Check for shared order-lists (we now use pointers for that) */
 
	if (CheckSavegameVersionOldStyle(5, 2)) {
 
		FOR_ALL_VEHICLES(v) {
 
			Vehicle *u;
 

	
 
			FOR_ALL_VEHICLES_FROM(u, v->index + 1) {
 
				/* If a vehicle has the same orders, add the link to eachother
 
				 *  in both vehicles */
 
				if (v->orders == u->orders) {
 
					v->next_shared = u;
 
					u->prev_shared = v;
 
					break;
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 
const ChunkHandler _veh_chunk_handlers[] = {
 
	{ 'VEHS', Save_VEHS, Load_VEHS, CH_SPARSE_ARRAY | CH_LAST},
 
};
src/vehicle_gui.c
Show inline comments
 
deleted file
src/vehicle_gui.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "player.h"
 
#include "station.h"
 
#include "strings.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "vehicle.h"
 
#include "window.h"
 
#include "engine.h"
 
#include "gui.h"
 
#include "command.h"
 
#include "gfx.h"
 
#include "variables.h"
 
#include "vehicle_gui.h"
 
#include "viewport.h"
 
#include "train.h"
 
#include "newgrf_callbacks.h"
 
#include "newgrf_engine.h"
 
#include "newgrf_text.h"
 
#include "date.h"
 
#include "ship.h"
 
#include "aircraft.h"
 
#include "roadveh.h"
 
#include "depot.h"
 

	
 
typedef struct Sorting {
 
	Listing aircraft;
 
	Listing roadveh;
 
	Listing ship;
 
	Listing train;
 
} Sorting;
 

	
 
static Sorting _sorting;
 

	
 
typedef struct vehiclelist_d {
 
	const Vehicle** sort_list;  // List of vehicles (sorted)
 
	Listing *_sorting;          // pointer to the appropiate subcategory of _sorting
 
	uint16 length_of_sort_list; // Keeps track of how many vehicle pointers sort list got space for
 
	byte vehicle_type;          // The vehicle type that is sorted
 
	list_d l;                   // General list struct
 
} vehiclelist_d;
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vehiclelist_d));
 

	
 
static bool   _internal_sort_order;     // descending/ascending
 

	
 
static RailType _railtype_selected_in_replace_gui;
 

	
 

	
 
typedef int CDECL VehicleSortListingTypeFunction(const void*, const void*);
 

	
 
static VehicleSortListingTypeFunction VehicleNumberSorter;
 
static VehicleSortListingTypeFunction VehicleNameSorter;
 
static VehicleSortListingTypeFunction VehicleAgeSorter;
 
static VehicleSortListingTypeFunction VehicleProfitThisYearSorter;
 
static VehicleSortListingTypeFunction VehicleProfitLastYearSorter;
 
static VehicleSortListingTypeFunction VehicleCargoSorter;
 
static VehicleSortListingTypeFunction VehicleReliabilitySorter;
 
static VehicleSortListingTypeFunction VehicleMaxSpeedSorter;
 
static VehicleSortListingTypeFunction VehicleModelSorter;
 
static VehicleSortListingTypeFunction VehicleValueSorter;
 

	
 
static VehicleSortListingTypeFunction* const _vehicle_sorter[] = {
 
	&VehicleNumberSorter,
 
	&VehicleNameSorter,
 
	&VehicleAgeSorter,
 
	&VehicleProfitThisYearSorter,
 
	&VehicleProfitLastYearSorter,
 
	&VehicleCargoSorter,
 
	&VehicleReliabilitySorter,
 
	&VehicleMaxSpeedSorter,
 
	&VehicleModelSorter,
 
	&VehicleValueSorter,
 
};
 

	
 
static const StringID _vehicle_sort_listing[] = {
 
	STR_SORT_BY_NUMBER,
 
	STR_SORT_BY_DROPDOWN_NAME,
 
	STR_SORT_BY_AGE,
 
	STR_SORT_BY_PROFIT_THIS_YEAR,
 
	STR_SORT_BY_PROFIT_LAST_YEAR,
 
	STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
 
	STR_SORT_BY_RELIABILITY,
 
	STR_SORT_BY_MAX_SPEED,
 
	STR_SORT_BY_MODEL,
 
	STR_SORT_BY_VALUE,
 
	INVALID_STRING_ID
 
};
 

	
 
static const StringID _rail_types_list[] = {
 
	STR_RAIL_VEHICLES,
 
	STR_ELRAIL_VEHICLES,
 
	STR_MONORAIL_VEHICLES,
 
	STR_MAGLEV_VEHICLES,
 
	INVALID_STRING_ID
 
};
 

	
 
void RebuildVehicleLists(void)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 

	
 
		switch (w->window_class) {
 
			case WC_TRAINS_LIST:
 
			case WC_ROADVEH_LIST:
 
			case WC_SHIPS_LIST:
 
			case WC_AIRCRAFT_LIST:
 
				WP(w, vehiclelist_d).l.flags |= VL_REBUILD;
 
				SetWindowDirty(w);
 
				break;
 
		}
 
	}
 
}
 

	
 
void ResortVehicleLists(void)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 

	
 
		switch (w->window_class) {
 
			case WC_TRAINS_LIST:
 
			case WC_ROADVEH_LIST:
 
			case WC_SHIPS_LIST:
 
			case WC_AIRCRAFT_LIST:
 
				WP(w, vehiclelist_d).l.flags |= VL_RESORT;
 
				SetWindowDirty(w);
 
				break;
 
		}
 
	}
 
}
 

	
 
static void BuildVehicleList(vehiclelist_d* vl, PlayerID owner, StationID station, OrderID order, uint16 depot_airport_index, uint16 window_type)
 
{
 
	if (!(vl->l.flags & VL_REBUILD)) return;
 

	
 
	DEBUG(misc, 3, "Building vehicle list for player %d at station %d", owner, station);
 

	
 
	vl->l.list_length = GenerateVehicleSortList(&vl->sort_list, &vl->length_of_sort_list, vl->vehicle_type, owner, station, order, depot_airport_index, window_type);
 

	
 
	vl->l.flags &= ~VL_REBUILD;
 
	vl->l.flags |= VL_RESORT;
 
}
 

	
 
static void SortVehicleList(vehiclelist_d *vl)
 
{
 
	if (!(vl->l.flags & VL_RESORT)) return;
 

	
 
	_internal_sort_order = vl->l.flags & VL_DESC;
 
	qsort((void*)vl->sort_list, vl->l.list_length, sizeof(vl->sort_list[0]),
 
		_vehicle_sorter[vl->l.sort_type]);
 

	
 
	vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
 
	vl->l.flags &= ~VL_RESORT;
 
}
 

	
 
void DepotSortList(Vehicle **v, uint16 length)
 
{
 
	_internal_sort_order = 0;
 
	qsort((void*)v, length, sizeof(v[0]), _vehicle_sorter[0]);
 
}
 

	
 
/* General Vehicle GUI based procedures that are independent of vehicle types */
 
void InitializeVehiclesGuiList(void)
 
{
 
	_railtype_selected_in_replace_gui = RAILTYPE_RAIL;
 
}
 

	
 
// draw the vehicle profit button in the vehicle list window.
 
void DrawVehicleProfitButton(const Vehicle *v, int x, int y)
 
{
 
	uint32 ormod;
 

	
 
	// draw profit-based colored icons
 
	if (v->age <= 365 * 2) {
 
		ormod = PALETTE_TO_GREY;
 
	} else if (v->profit_last_year < 0) {
 
		ormod = PALETTE_TO_RED;
 
	} else if (v->profit_last_year < 10000) {
 
		ormod = PALETTE_TO_YELLOW;
 
	} else {
 
		ormod = PALETTE_TO_GREEN;
 
	}
 
	DrawSprite(SPR_BLOT | ormod, x, y);
 
}
 

	
 
typedef struct RefitOption {
 
	CargoID cargo;
 
	byte subtype;
 
	uint16 value;
 
	EngineID engine;
 
} RefitOption;
 

	
 
typedef struct RefitList {
 
	uint num_lines;
 
	RefitOption *items;
 
} RefitList;
 

	
 
static RefitList *BuildRefitList(const Vehicle *v)
 
{
 
	uint max_lines = 256;
 
	RefitOption *refit = calloc(max_lines, sizeof(*refit));
 
	RefitList *list = calloc(1, sizeof(*list));
 
	Vehicle *u = (Vehicle*)v;
 
	uint num_lines = 0;
 
	uint i;
 

	
 
	do {
 
		CargoID cid;
 
		uint32 cmask = EngInfo(u->engine_type)->refit_mask;
 
		byte callbackmask = EngInfo(u->engine_type)->callbackmask;
 

	
 
		/* Skip this engine if it has no capacity */
 
		if (u->cargo_cap == 0) continue;
 

	
 
		/* Loop through all cargos in the refit mask */
 
		for (cid = 0; cmask != 0 && num_lines < max_lines; cmask >>= 1, cid++) {
 
			CargoID lcid;
 

	
 
			/* Skip cargo type if it's not listed */
 
			if (!HASBIT(cmask, 0)) continue;
 

	
 
			lcid = _local_cargo_id_ctype[cid];
 
			if (lcid == CT_INVALID) continue;
 

	
 
			/* Check the vehicle's callback mask for cargo suffixes */
 
			if (HASBIT(callbackmask, CBM_CARGO_SUFFIX)) {
 
				/* Make a note of the original cargo type. It has to be
 
				 * changed to test the cargo & subtype... */
 
				CargoID temp_cargo = u->cargo_type;
 
				byte temp_subtype  = u->cargo_subtype;
 
				byte refit_cyc;
 

	
 
				u->cargo_type = lcid;
 

	
 
				for (refit_cyc = 0; refit_cyc < 16 && num_lines < max_lines; refit_cyc++) {
 
					bool duplicate = false;
 
					uint16 callback;
 

	
 
					u->cargo_subtype = refit_cyc;
 
					callback = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, u->engine_type, u);
 

	
 
					if (callback == 0xFF) callback = CALLBACK_FAILED;
 
					if (refit_cyc != 0 && callback == CALLBACK_FAILED) break;
 

	
 
					/* Check if this cargo and subtype combination are listed */
 
					for (i = 0; i < num_lines && !duplicate; i++) {
 
						if (refit[i].cargo == lcid && refit[i].value == callback) duplicate = true;
 
					}
 

	
 
					if (duplicate) continue;
 

	
 
					refit[num_lines].cargo   = lcid;
 
					refit[num_lines].subtype = refit_cyc;
 
					refit[num_lines].value   = callback;
 
					refit[num_lines].engine  = u->engine_type;
 
					num_lines++;
 
				}
 

	
 
				/* Reset the vehicle's cargo type */
 
				u->cargo_type    = temp_cargo;
 
				u->cargo_subtype = temp_subtype;
 
			} else {
 
				/* No cargo suffix callback -- use no subtype */
 
				bool duplicate = false;
 

	
 
				for (i = 0; i < num_lines && !duplicate; i++) {
 
					if (refit[i].cargo == lcid && refit[i].value == CALLBACK_FAILED) duplicate = true;
 
				}
 

	
 
				if (!duplicate) {
 
					refit[num_lines].cargo   = lcid;
 
					refit[num_lines].subtype = 0;
 
					refit[num_lines].value   = CALLBACK_FAILED;
 
					refit[num_lines].engine  = INVALID_ENGINE;
 
					num_lines++;
 
				}
 
			}
 
		}
 
	} while (v->type == VEH_Train && (u = u->next) != NULL && num_lines < max_lines);
 

	
 
	list->num_lines = num_lines;
 
	list->items = refit;
 

	
 
	return list;
 
}
 

	
 
/** Draw the list of available refit options for a consist.
 
 * Draw the list and highlight the selected refit option (if any)
 
 * @param *v first vehicle in consist to get the refit-options of
 
 * @param sel selected refit cargo-type in the window
 
 * @return the refit option that is hightlighted, NULL if none
 
 */
 
static RefitOption *DrawVehicleRefitWindow(const RefitList *list, int sel, uint pos, uint rows, uint delta)
 
{
 
	RefitOption *refit = list->items;
 
	RefitOption *selected = NULL;
 
	uint num_lines = list->num_lines;
 
	uint y = 31;
 
	uint i;
 

	
 
	/* Draw the list, and find the selected cargo (by its position in list) */
 
	for (i = 0; i < num_lines; i++) {
 
		byte colour = 16;
 
		if (sel == 0) {
 
			selected = &refit[i];
 
			colour = 12;
 
		}
 

	
 
		if (i >= pos && i < pos + rows) {
 
			/* Draw the cargo name */
 
			int last_x = DrawString(2, y, _cargoc.names_s[refit[i].cargo], colour);
 

	
 
			/* If the callback succeeded, draw the cargo suffix */
 
			if (refit[i].value != CALLBACK_FAILED) {
 
				DrawString(last_x + 1, y, GetGRFStringID(GetEngineGRFID(refit[i].engine), 0xD000 + refit[i].value), colour);
 
			}
 
			y += delta;
 
		}
 

	
 
		sel--;
 
	}
 

	
 
	return selected;
 
}
 

	
 
static void VehicleRefitWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			Vehicle *v = GetVehicle(w->window_number);
 

	
 
			if (v->type == VEH_Train) {
 
				uint length = CountVehiclesInChain(v);
 

	
 
				if (length != WP(w, refit_d).length) {
 
					/* Consist length has changed, so rebuild the refit list */
 
					free(WP(w, refit_d).list->items);
 
					free(WP(w, refit_d).list);
 
					WP(w, refit_d).list = BuildRefitList(v);
 
					WP(w, refit_d).length = length;
 
				}
 
			}
 

	
 
			SetVScrollCount(w, WP(w, refit_d).list->num_lines);
 

	
 
			SetDParam(0, v->string_id);
 
			SetDParam(1, v->unitnumber);
 
			DrawWindowWidgets(w);
 

	
 
			WP(w,refit_d).cargo = DrawVehicleRefitWindow(WP(w, refit_d).list, WP(w, refit_d).sel, w->vscroll.pos, w->vscroll.cap, w->resize.step_height);
 

	
 
			if (WP(w,refit_d).cargo != NULL) {
 
				int32 cost = 0;
 
				switch (GetVehicle(w->window_number)->type) {
 
					case VEH_Train:    cost = CMD_REFIT_RAIL_VEHICLE; break;
 
					case VEH_Road:     cost = CMD_REFIT_ROAD_VEH;     break;
 
					case VEH_Ship:     cost = CMD_REFIT_SHIP;         break;
 
					case VEH_Aircraft: cost = CMD_REFIT_AIRCRAFT;     break;
 
				}
 

	
 
				cost = DoCommand(v->tile, v->index, WP(w,refit_d).cargo->cargo | WP(w,refit_d).cargo->subtype << 8, DC_QUERY_COST, cost);
 
				if (!CmdFailed(cost)) {
 
					SetDParam(0, WP(w,refit_d).cargo->cargo);
 
					SetDParam(1, _returned_refit_capacity);
 
					SetDParam(2, cost);
 
					DrawString(2, w->widget[5].top + 1, STR_9840_NEW_CAPACITY_COST_OF_REFIT, 0);
 
				}
 
			}
 
		}	break;
 

	
 
		case WE_CLICK:
 
			switch (e->we.click.widget) {
 
				case 3: { // listbox
 
					int y = e->we.click.pt.y - w->widget[3].top;
 
					if (y >= 0) {
 
						WP(w,refit_d).sel = (y / (int)w->resize.step_height) + w->vscroll.pos;
 
						SetWindowDirty(w);
 
					}
 
				} break;
 
				case 6: // refit button
 
					if (WP(w,refit_d).cargo != NULL) {
 
						const Vehicle *v = GetVehicle(w->window_number);
 

	
 
						if (WP(w, refit_d).order == INVALID_VEH_ORDER_ID) {
 
							int command = 0;
 

	
 
							switch (v->type) {
 
								case VEH_Train:    command = CMD_REFIT_RAIL_VEHICLE | CMD_MSG(STR_RAIL_CAN_T_REFIT_VEHICLE);  break;
 
								case VEH_Road:     command = CMD_REFIT_ROAD_VEH     | CMD_MSG(STR_REFIT_ROAD_VEHICLE_CAN_T);  break;
 
								case VEH_Ship:     command = CMD_REFIT_SHIP         | CMD_MSG(STR_9841_CAN_T_REFIT_SHIP);     break;
 
								case VEH_Aircraft: command = CMD_REFIT_AIRCRAFT     | CMD_MSG(STR_A042_CAN_T_REFIT_AIRCRAFT); break;
 
							}
 
							if (DoCommandP(v->tile, v->index, WP(w,refit_d).cargo->cargo | WP(w,refit_d).cargo->subtype << 8, NULL, command)) DeleteWindow(w);
 
						} else {
 
							if (DoCommandP(v->tile, v->index, WP(w,refit_d).cargo->cargo | WP(w,refit_d).cargo->subtype << 8 | WP(w, refit_d).order << 16, NULL, CMD_ORDER_REFIT)) DeleteWindow(w);
 
						}
 
					}
 
					break;
 
			}
 
			break;
 

	
 
		case WE_RESIZE:
 
			w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
 
			w->widget[3].data = (w->vscroll.cap << 8) + 1;
 
			break;
 

	
 
		case WE_DESTROY:
 
			free(WP(w, refit_d).list->items);
 
			free(WP(w, refit_d).list);
 
			break;
 
	}
 
}
 

	
 

	
 
static const Widget _vehicle_refit_widgets[] = {
 
	{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                            STR_018B_CLOSE_WINDOW},
 
	{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   239,     0,    13, STR_983B_REFIT,                      STR_018C_WINDOW_TITLE_DRAG_THIS},
 
	{    WWT_TEXTBTN,   RESIZE_NONE,    14,     0,   239,    14,    27, STR_983F_SELECT_CARGO_TYPE_TO_CARRY, STR_983D_SELECT_TYPE_OF_CARGO_FOR},
 
	{     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   227,    28,   139, 0x801,                               STR_EMPTY},
 
	{  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   228,   239,    28,   139, 0x0,                                 STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
	{      WWT_PANEL,     RESIZE_TB,    14,     0,   239,   140,   161, 0x0,                                 STR_NULL},
 
	{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   227,   162,   173, 0x0,                                 STR_NULL},
 
	{  WWT_RESIZEBOX,     RESIZE_TB,    14,   228,   239,   162,   173, 0x0,                                 STR_RESIZE_BUTTON},
 
	{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _vehicle_refit_desc = {
 
	WDP_AUTO, WDP_AUTO, 240, 174,
 
	WC_VEHICLE_REFIT,WC_VEHICLE_VIEW,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_vehicle_refit_widgets,
 
	VehicleRefitWndProc,
 
};
 

	
 
/** Show the refit window for a vehicle
 
* @param *v The vehicle to show the refit window for
 
*/
 
void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_VEHICLE_REFIT, v->index);
 

	
 
	w = AllocateWindowDescFront(&_vehicle_refit_desc, v->index);
 
	WP(w, refit_d).order = order;
 

	
 
	if (w != NULL) {
 
		w->caption_color = v->owner;
 
		w->vscroll.cap = 8;
 
		w->resize.step_height = 14;
 
		WP(w, refit_d).sel  = -1;
 
		WP(w, refit_d).list = BuildRefitList(v);
 
		if (v->type == VEH_Train) WP(w, refit_d).length = CountVehiclesInChain(v);
 
		SetVScrollCount(w, WP(w, refit_d).list->num_lines);
 

	
 
		switch (v->type) {
 
			case VEH_Train:
 
				w->widget[3].tooltips = STR_RAIL_SELECT_TYPE_OF_CARGO_FOR;
 
				w->widget[6].data     = STR_RAIL_REFIT_VEHICLE;
 
				w->widget[6].tooltips = STR_RAIL_REFIT_TO_CARRY_HIGHLIGHTED;
 
				break;
 
			case VEH_Road:
 
				w->widget[3].tooltips = STR_ROAD_SELECT_TYPE_OF_CARGO_FOR;
 
				w->widget[6].data     = STR_REFIT_ROAD_VEHICLE;
 
				w->widget[6].tooltips = STR_REFIT_ROAD_VEHICLE_TO_CARRY_HIGHLIGHTED;
 
				break;
 
			case VEH_Ship:
 
				w->widget[3].tooltips = STR_983D_SELECT_TYPE_OF_CARGO_FOR;
 
				w->widget[6].data     = STR_983C_REFIT_SHIP;
 
				w->widget[6].tooltips = STR_983E_REFIT_SHIP_TO_CARRY_HIGHLIGHTED;
 
				break;
 
			case VEH_Aircraft:
 
				w->widget[3].tooltips = STR_A03E_SELECT_TYPE_OF_CARGO_FOR;
 
				w->widget[6].data     = STR_A03D_REFIT_AIRCRAFT;
 
				w->widget[6].tooltips = STR_A03F_REFIT_AIRCRAFT_TO_CARRY;
 
				break;
 
			default: NOT_REACHED();
 
		}
 
	}
 
}
 

	
 
/* Display additional text from NewGRF in the purchase information window */
 
uint ShowAdditionalText(int x, int y, uint w, EngineID engine)
 
{
 
	uint16 callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, NULL);
 
	if (callback == CALLBACK_FAILED) return 0;
 

	
 
	// STR_02BD is used to start the string with {BLACK}
 
	SetDParam(0, GetGRFStringID(GetEngineGRFID(engine), 0xD000 + callback));
 
	return DrawStringMultiLine(x, y, STR_02BD, w);
 
}
 

	
 
/* Count the number of bits that are set in a mask */
 
static uint CountBits(uint32 mask)
 
{
 
	uint c = 0;
 
	for (; mask != 0; mask >>= 1) if (HASBIT(mask, 0)) c++;
 
	return c;
 
}
 

	
 
/* Display list of cargo types of the engine, for the purchase information window */
 
uint ShowRefitOptionsList(int x, int y, uint w, EngineID engine)
 
{
 
	/* List of cargo types of this engine */
 
	uint32 cmask = EngInfo(engine)->refit_mask;
 
	/* List of cargo types available in this climate */
 
	uint32 lmask = _landscape_global_cargo_mask[_opt.landscape];
 
	char *b = _userstring;
 

	
 
	/* Draw nothing if the engine is not refittable */
 
	if (CountBits(cmask) <= 1) return 0;
 

	
 
	b = InlineString(b, STR_PURCHASE_INFO_REFITTABLE_TO);
 

	
 
	if (cmask == lmask) {
 
		/* Engine can be refitted to all types in this climate */
 
		b = InlineString(b, STR_PURCHASE_INFO_ALL_TYPES);
 
	} else {
 
		CargoID cid;
 

	
 
		/* Check if we are able to refit to more cargo types and unable to. If
 
		 * so, invert the cargo types to list those that we can't refit to. */
 
		if (CountBits(cmask ^ lmask) < CountBits(cmask)) {
 
			cmask ^= lmask;
 
			b = InlineString(b, STR_PURCHASE_INFO_ALL_BUT);
 
		}
 

	
 
		/* Add each cargo type to the list */
 
		for (cid = 0; cmask != 0; cmask >>= 1, cid++) {
 
			if (!HASBIT(cmask, 0)) continue;
 

	
 
			b = InlineString(b, _cargoc.names_s[_local_cargo_id_ctype[cid]]);
 
			if (cmask > 1) b = strecpy(b, ", ", lastof(_userstring));
 
		}
 
	}
 

	
 
	/* Terminate and display the completed string */
 
	*b = '\0';
 
	return DrawStringMultiLine(x, y, STR_SPEC_USERSTRING, w);
 
}
 

	
 

	
 
// if the sorting criteria had the same value, sort vehicle by unitnumber
 
#define VEHICLEUNITNUMBERSORTER(r, a, b) {if (r == 0) {r = a->unitnumber - b->unitnumber;}}
 

	
 
static int CDECL VehicleNumberSorter(const void *a, const void *b)
 
{
 
	const Vehicle* va = *(const Vehicle**)a;
 
	const Vehicle* vb = *(const Vehicle**)b;
 
	int r = va->unitnumber - vb->unitnumber;
 

	
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
static int CDECL VehicleNameSorter(const void *a, const void *b)
 
{
 
	static const Vehicle *last_vehicle[2] = { NULL, NULL };
 
	static char           last_name[2][64] = { "", "" };
 

	
 
	const Vehicle* va = *(const Vehicle**)a;
 
	const Vehicle* vb = *(const Vehicle**)b;
 
	int r;
 

	
 
	if (va != last_vehicle[0]) {
 
		last_vehicle[0] = va;
 
		if (IsCustomName(va->string_id)) {
 
			GetString(last_name[0], va->string_id, lastof(last_name[0]));
 
		} else {
 
			last_name[0][0] = '\0';
 
		}
 
	}
 

	
 
	if (vb != last_vehicle[1]) {
 
		last_vehicle[1] = vb;
 
		if (IsCustomName(vb->string_id)) {
 
			GetString(last_name[1], vb->string_id, lastof(last_name[1]));
 
		} else {
 
			last_name[1][0] = '\0';
 
		}
 
	}
 

	
 
	r = strcmp(last_name[0], last_name[1]); // sort by name
 

	
 
	VEHICLEUNITNUMBERSORTER(r, va, vb);
 

	
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
static int CDECL VehicleAgeSorter(const void *a, const void *b)
 
{
 
	const Vehicle* va = *(const Vehicle**)a;
 
	const Vehicle* vb = *(const Vehicle**)b;
 
	int r = va->age - vb->age;
 

	
 
	VEHICLEUNITNUMBERSORTER(r, va, vb);
 

	
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
static int CDECL VehicleProfitThisYearSorter(const void *a, const void *b)
 
{
 
	const Vehicle* va = *(const Vehicle**)a;
 
	const Vehicle* vb = *(const Vehicle**)b;
 
	int r = va->profit_this_year - vb->profit_this_year;
 

	
 
	VEHICLEUNITNUMBERSORTER(r, va, vb);
 

	
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
static int CDECL VehicleProfitLastYearSorter(const void *a, const void *b)
 
{
 
	const Vehicle* va = *(const Vehicle**)a;
 
	const Vehicle* vb = *(const Vehicle**)b;
 
	int r = va->profit_last_year - vb->profit_last_year;
 

	
 
	VEHICLEUNITNUMBERSORTER(r, va, vb);
 

	
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
static int CDECL VehicleCargoSorter(const void *a, const void *b)
 
{
 
	const Vehicle* va = *(const Vehicle**)a;
 
	const Vehicle* vb = *(const Vehicle**)b;
 
	const Vehicle* v;
 
	AcceptedCargo cargoa;
 
	AcceptedCargo cargob;
 
	int r = 0;
 
	int i;
 

	
 
	memset(cargoa, 0, sizeof(cargoa));
 
	memset(cargob, 0, sizeof(cargob));
 
	for (v = va; v != NULL; v = v->next) cargoa[v->cargo_type] += v->cargo_cap;
 
	for (v = vb; v != NULL; v = v->next) cargob[v->cargo_type] += v->cargo_cap;
 

	
 
	for (i = 0; i < NUM_CARGO; i++) {
 
		r = cargoa[i] - cargob[i];
 
		if (r != 0) break;
 
	}
 

	
 
	VEHICLEUNITNUMBERSORTER(r, va, vb);
 

	
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
static int CDECL VehicleReliabilitySorter(const void *a, const void *b)
 
{
 
	const Vehicle* va = *(const Vehicle**)a;
 
	const Vehicle* vb = *(const Vehicle**)b;
 
	int r = va->reliability - vb->reliability;
 

	
 
	VEHICLEUNITNUMBERSORTER(r, va, vb);
 

	
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
static int CDECL VehicleMaxSpeedSorter(const void *a, const void *b)
 
{
 
	const Vehicle* va = *(const Vehicle**)a;
 
	const Vehicle* vb = *(const Vehicle**)b;
 
	int max_speed_a = 0xFFFF, max_speed_b = 0xFFFF;
 
	int r;
 
	const Vehicle *ua = va, *ub = vb;
 

	
 
	if (va->type == VEH_Train && vb->type == VEH_Train) {
 
		do {
 
			if (RailVehInfo(ua->engine_type)->max_speed != 0)
 
				max_speed_a = min(max_speed_a, RailVehInfo(ua->engine_type)->max_speed);
 
		} while ((ua = ua->next) != NULL);
 

	
 
		do {
 
			if (RailVehInfo(ub->engine_type)->max_speed != 0)
 
				max_speed_b = min(max_speed_b, RailVehInfo(ub->engine_type)->max_speed);
 
		} while ((ub = ub->next) != NULL);
 

	
 
		r = max_speed_a - max_speed_b;
 
	} else {
 
		r = va->max_speed - vb->max_speed;
 
	}
 

	
 
	VEHICLEUNITNUMBERSORTER(r, va, vb);
 

	
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
static int CDECL VehicleModelSorter(const void *a, const void *b)
 
{
 
	const Vehicle* va = *(const Vehicle**)a;
 
	const Vehicle* vb = *(const Vehicle**)b;
 
	int r = va->engine_type - vb->engine_type;
 

	
 
	VEHICLEUNITNUMBERSORTER(r, va, vb);
 

	
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
static int CDECL VehicleValueSorter(const void *a, const void *b)
 
{
 
	const Vehicle* va = *(const Vehicle**)a;
 
	const Vehicle* vb = *(const Vehicle**)b;
 
	const Vehicle *u;
 
	int valuea = 0, valueb = 0;
 
	int r;
 

	
 
	for (u = va; u != NULL; u = u->next) valuea += u->value;
 
	for (u = vb; u != NULL; u = u->next) valueb += u->value;
 

	
 
	r = valuea - valueb;
 

	
 
	VEHICLEUNITNUMBERSORTER(r, va, vb);
 

	
 
	return (_internal_sort_order & 1) ? -r : r;
 
}
 

	
 
// this define is to match engine.c, but engine.c keeps it to itself
 
// ENGINE_AVAILABLE is used in ReplaceVehicleWndProc
 
#define ENGINE_AVAILABLE ((e->flags & 1 && HASBIT(info->climates, _opt.landscape)) || HASBIT(e->player_avail, _local_player))
 

	
 
/*  if show_outdated is selected, it do not sort psudo engines properly but it draws all engines
 
 * if used compined with show_cars set to false, it will work as intended. Replace window do it like that
 
 *  this was a big hack even before show_outdated was added. Stupid newgrf :p
 
 */
 
static void train_engine_drawing_loop(int *x, int *y, int *pos, int *sel, EngineID *selected_id, RailType railtype,
 
	uint8 lines_drawn, bool is_engine, bool show_cars, bool show_outdated, bool show_compatible)
 
{
 
	EngineID j;
 
	byte colour;
 
	const Player *p = GetPlayer(_local_player);
 

	
 
	for (j = 0; j < NUM_TRAIN_ENGINES; j++) {
 
		EngineID i = GetRailVehAtPosition(j);
 
		const Engine *e = GetEngine(i);
 
		const RailVehicleInfo *rvi = RailVehInfo(i);
 
		const EngineInfo *info = EngInfo(i);
 

	
 
		if (!EngineHasReplacementForPlayer(p, i) && p->num_engines[i] == 0 && show_outdated) continue;
 

	
 
		if ((rvi->power == 0 && !show_cars) || (rvi->power != 0 && show_cars))  // show wagons or engines (works since wagons do not have power)
 
			continue;
 

	
 
		if (*sel == 0) *selected_id = j;
 

	
 

	
 
		colour = *sel == 0 ? 0xC : 0x10;
 
		if (!(ENGINE_AVAILABLE && show_outdated && RailVehInfo(i)->power && IsCompatibleRail(e->railtype, railtype))) {
 
			if ((!IsCompatibleRail(e->railtype, railtype) && show_compatible)
 
				|| (e->railtype != railtype && !show_compatible)
 
				|| !(rvi->flags & RVI_WAGON) != is_engine ||
 
				!HASBIT(e->player_avail, _local_player))
 
				continue;
 
#if 0
 
		} else {
 
			// TODO find a nice red colour for vehicles being replaced
 
			if ( _autoreplace_array[i] != i )
 
				colour = *sel == 0 ? 0x44 : 0x45;
 
#endif
 
		}
 

	
 
		if (IS_INT_INSIDE(--*pos, -lines_drawn, 0)) {
 
			DrawString(*x + 59, *y + 2, GetCustomEngineName(i),
 
				colour);
 
			// show_outdated is true only for left side, which is where we show old replacements
 
			DrawTrainEngine(*x + 29, *y + 6, i, (p->num_engines[i] == 0 && show_outdated) ?
 
				PALETTE_CRASH : GetEnginePalette(i, _local_player));
 
			if ( show_outdated ) {
 
				SetDParam(0, p->num_engines[i]);
 
				DrawStringRightAligned(213, *y+5, STR_TINY_BLACK, 0);
 
			}
 
			*y += 14;
 
		}
 
		--*sel;
 
	}
 
}
 

	
 

	
 
static void SetupScrollStuffForReplaceWindow(Window *w)
 
{
 
	EngineID selected_id[2] = { INVALID_ENGINE, INVALID_ENGINE };
 
	const Player* p = GetPlayer(_local_player);
 
	uint sel[2];
 
	uint count = 0;
 
	uint count2 = 0;
 
	EngineID i;
 

	
 
	sel[0] = WP(w,replaceveh_d).sel_index[0];
 
	sel[1] = WP(w,replaceveh_d).sel_index[1];
 

	
 
	switch (WP(w,replaceveh_d).vehicletype) {
 
		case VEH_Train: {
 
			RailType railtype = _railtype_selected_in_replace_gui;
 

	
 
			w->widget[13].color = _player_colors[_local_player]; // sets the colour of that art thing
 
			w->widget[16].color = _player_colors[_local_player]; // sets the colour of that art thing
 

	
 
			for (i = 0; i < NUM_TRAIN_ENGINES; i++) {
 
				EngineID eid = GetRailVehAtPosition(i);
 
				const Engine* e = GetEngine(eid);
 
				const EngineInfo* info = EngInfo(eid);
 

	
 
				// left window contains compatible engines while right window only contains engines of the selected type
 
				if (ENGINE_AVAILABLE &&
 
						(RailVehInfo(eid)->power != 0) == (WP(w, replaceveh_d).wagon_btnstate != 0)) {
 
					if (IsCompatibleRail(e->railtype, railtype) && (p->num_engines[eid] > 0 || EngineHasReplacementForPlayer(p, eid))) {
 
						if (sel[0] == count) selected_id[0] = eid;
 
						count++;
 
					}
 
					if (e->railtype == railtype && HASBIT(e->player_avail, _local_player)) {
 
						if (sel[1] == count2) selected_id[1] = eid;
 
						count2++;
 
					}
 
				}
 
			}
 
			break;
 
		}
 

	
 
		case VEH_Road: {
 
			for (i = ROAD_ENGINES_INDEX; i < ROAD_ENGINES_INDEX + NUM_ROAD_ENGINES; i++) {
 
				if (p->num_engines[i] > 0 || EngineHasReplacementForPlayer(p, i)) {
 
					if (sel[0] == count) selected_id[0] = i;
 
					count++;
 
				}
 
			}
 

	
 
			if (selected_id[0] != INVALID_ENGINE) { // only draw right array if we have anything in the left one
 
				CargoID cargo = RoadVehInfo(selected_id[0])->cargo_type;
 

	
 
				for (i = ROAD_ENGINES_INDEX; i < ROAD_ENGINES_INDEX + NUM_ROAD_ENGINES; i++) {
 
					if (cargo == RoadVehInfo(i)->cargo_type &&
 
							HASBIT(GetEngine(i)->player_avail, _local_player)) {
 
						if (sel[1] == count2) selected_id[1] = i;
 
						count2++;
 
					}
 
				}
 
			}
 
			break;
 
		}
 

	
 
		case VEH_Ship: {
 
			for (i = SHIP_ENGINES_INDEX; i < SHIP_ENGINES_INDEX + NUM_SHIP_ENGINES; i++) {
 
				if (p->num_engines[i] > 0 || EngineHasReplacementForPlayer(p, i)) {
 
					if (sel[0] == count) selected_id[0] = i;
 
					count++;
 
				}
 
			}
 

	
 
			if (selected_id[0] != INVALID_ENGINE) {
 
				const ShipVehicleInfo* svi = ShipVehInfo(selected_id[0]);
 
				CargoID cargo = svi->cargo_type;
 
				byte refittable = svi->refittable;
 

	
 
				for (i = SHIP_ENGINES_INDEX; i < SHIP_ENGINES_INDEX + NUM_SHIP_ENGINES; i++) {
 
					if (HASBIT(GetEngine(i)->player_avail, _local_player) && (
 
								ShipVehInfo(i)->cargo_type == cargo ||
 
								ShipVehInfo(i)->refittable & refittable
 
							)) {
 
						if (sel[1] == count2) selected_id[1] = i;
 
						count2++;
 
					}
 
				}
 
			}
 
			break;
 
		}
 

	
 
		case VEH_Aircraft: {
 
			for (i = AIRCRAFT_ENGINES_INDEX; i < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES; i++) {
 
				if (p->num_engines[i] > 0 || EngineHasReplacementForPlayer(p, i)) {
 
					if (sel[0] == count) selected_id[0] = i;
 
					count++;
 
				}
 
			}
 

	
 
			if (selected_id[0] != INVALID_ENGINE) {
 
				byte subtype = AircraftVehInfo(selected_id[0])->subtype;
 

	
 
				for (i = AIRCRAFT_ENGINES_INDEX; i < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES; i++) {
 
					if (HASBIT(GetEngine(i)->player_avail, _local_player) &&
 
							(subtype & AIR_CTOL) == (AircraftVehInfo(i)->subtype & AIR_CTOL)) {
 
						if (sel[1] == count2) selected_id[1] = i;
 
						count2++;
 
					}
 
				}
 
			}
 
			break;
 
		}
 
	}
 
	// sets up the number of items in each list
 
	SetVScrollCount(w, count);
 
	SetVScroll2Count(w, count2);
 
	WP(w,replaceveh_d).sel_engine[0] = selected_id[0];
 
	WP(w,replaceveh_d).sel_engine[1] = selected_id[1];
 

	
 
	WP(w,replaceveh_d).count[0] = count;
 
	WP(w,replaceveh_d).count[1] = count2;
 
	return;
 
}
 

	
 

	
 
static void DrawEngineArrayInReplaceWindow(Window *w, int x, int y, int x2, int y2, int pos, int pos2,
 
	int sel1, int sel2, EngineID selected_id1, EngineID selected_id2)
 
{
 
	int sel[2];
 
	EngineID selected_id[2];
 
	const Player *p = GetPlayer(_local_player);
 

	
 
	sel[0] = sel1;
 
	sel[1] = sel2;
 

	
 
	selected_id[0] = selected_id1;
 
	selected_id[1] = selected_id2;
 

	
 
	switch (WP(w,replaceveh_d).vehicletype) {
 
		case VEH_Train: {
 
			RailType railtype = _railtype_selected_in_replace_gui;
 
			DrawString(157, w->widget[14].top + 1, _rail_types_list[railtype], 0x10);
 
			/* draw sorting criteria string */
 

	
 
			/* Ensure that custom engines which substituted wagons
 
			 * are sorted correctly.
 
			 * XXX - DO NOT EVER DO THIS EVER AGAIN! GRRR hacking in wagons as
 
			 * engines to get more types.. Stays here until we have our own format
 
			 * then it is exit!!! */
 
			if (WP(w,replaceveh_d).wagon_btnstate) {
 
				train_engine_drawing_loop(&x, &y, &pos, &sel[0], &selected_id[0], railtype, w->vscroll.cap, true, false, true, true); // True engines
 
				train_engine_drawing_loop(&x2, &y2, &pos2, &sel[1], &selected_id[1], railtype, w->vscroll.cap, true, false, false, false); // True engines
 
				train_engine_drawing_loop(&x2, &y2, &pos2, &sel[1], &selected_id[1], railtype, w->vscroll.cap, false, false, false, false); // Feeble wagons
 
			} else {
 
				train_engine_drawing_loop(&x, &y, &pos, &sel[0], &selected_id[0], railtype, w->vscroll.cap, false, true, true, true);
 
				train_engine_drawing_loop(&x2, &y2, &pos2, &sel[1], &selected_id[1], railtype, w->vscroll.cap, false, true, false, true);
 
			}
 
			break;
 
		}
 

	
 
		case VEH_Road: {
 
			int num = NUM_ROAD_ENGINES;
 
			const Engine* e = GetEngine(ROAD_ENGINES_INDEX);
 
			EngineID engine_id = ROAD_ENGINES_INDEX;
 

	
 
			do {
 
				if (p->num_engines[engine_id] > 0 || EngineHasReplacementForPlayer(p, engine_id)) {
 
					if (IS_INT_INSIDE(--pos, -w->vscroll.cap, 0)) {
 
						DrawString(x+59, y+2, GetCustomEngineName(engine_id), sel[0]==0 ? 0xC : 0x10);
 
						DrawRoadVehEngine(x+29, y+6, engine_id, p->num_engines[engine_id] > 0 ? GetEnginePalette(engine_id, _local_player) : PALETTE_CRASH);
 
						SetDParam(0, p->num_engines[engine_id]);
 
						DrawStringRightAligned(213, y+5, STR_TINY_BLACK, 0);
 
						y += 14;
 
					}
 
				sel[0]--;
 
				}
 

	
 
				if (selected_id[0] != INVALID_ENGINE) {
 
					byte cargo = RoadVehInfo(selected_id[0])->cargo_type;
 

	
 
					if (RoadVehInfo(engine_id)->cargo_type == cargo && HASBIT(e->player_avail, _local_player)) {
 
						if (IS_INT_INSIDE(--pos2, -w->vscroll.cap, 0) && RoadVehInfo(engine_id)->cargo_type == cargo) {
 
							DrawString(x2+59, y2+2, GetCustomEngineName(engine_id), sel[1]==0 ? 0xC : 0x10);
 
							DrawRoadVehEngine(x2+29, y2+6, engine_id, GetEnginePalette(engine_id, _local_player));
 
							y2 += 14;
 
						}
 
						sel[1]--;
 
					}
 
				}
 
			} while (++engine_id, ++e,--num);
 
			break;
 
		}
 

	
 
		case VEH_Ship: {
 
			int num = NUM_SHIP_ENGINES;
 
			const Engine* e = GetEngine(SHIP_ENGINES_INDEX);
 
			EngineID engine_id = SHIP_ENGINES_INDEX;
 

	
 
			do {
 
				if (p->num_engines[engine_id] > 0 || EngineHasReplacementForPlayer(p, engine_id)) {
 
					if (IS_INT_INSIDE(--pos, -w->vscroll.cap, 0)) {
 
						DrawString(x+75, y+7, GetCustomEngineName(engine_id), sel[0]==0 ? 0xC : 0x10);
 
						DrawShipEngine(x+35, y+10, engine_id, p->num_engines[engine_id] > 0 ? GetEnginePalette(engine_id, _local_player) : PALETTE_CRASH);
 
						SetDParam(0, p->num_engines[engine_id]);
 
						DrawStringRightAligned(213, y+15, STR_TINY_BLACK, 0);
 
						y += 24;
 
					}
 
					sel[0]--;
 
				}
 

	
 
				if (selected_id[0] != INVALID_ENGINE) {
 
					CargoID cargo = ShipVehInfo(selected_id[0])->cargo_type;
 
					bool refittable = ShipVehInfo(selected_id[0])->refittable;
 

	
 
					if (HASBIT(e->player_avail, _local_player) && ( cargo == ShipVehInfo(engine_id)->cargo_type || refittable & ShipVehInfo(engine_id)->refittable)) {
 
						if (IS_INT_INSIDE(--pos2, -w->vscroll.cap, 0)) {
 
							DrawString(x2+75, y2+7, GetCustomEngineName(engine_id), sel[1]==0 ? 0xC : 0x10);
 
							DrawShipEngine(x2+35, y2+10, engine_id, GetEnginePalette(engine_id, _local_player));
 
							y2 += 24;
 
						}
 
						sel[1]--;
 
					}
 
				}
 
			} while (++engine_id, ++e, --num);
 
			break;
 
		}   //end of ship
 

	
 
		case VEH_Aircraft: {
 
			int num = NUM_AIRCRAFT_ENGINES;
 
			const Engine* e = GetEngine(AIRCRAFT_ENGINES_INDEX);
 
			EngineID engine_id = AIRCRAFT_ENGINES_INDEX;
 

	
 
			do {
 
				if (p->num_engines[engine_id] > 0 || EngineHasReplacementForPlayer(p, engine_id)) {
 
					if (sel[0] == 0) selected_id[0] = engine_id;
 
					if (IS_INT_INSIDE(--pos, -w->vscroll.cap, 0)) {
 
						DrawString(x+62, y+7, GetCustomEngineName(engine_id), sel[0]==0 ? 0xC : 0x10);
 
						DrawAircraftEngine(x+29, y+10, engine_id, p->num_engines[engine_id] > 0 ? GetEnginePalette(engine_id, _local_player) : PALETTE_CRASH);
 
						SetDParam(0, p->num_engines[engine_id]);
 
						DrawStringRightAligned(213, y+15, STR_TINY_BLACK, 0);
 
						y += 24;
 
					}
 
					sel[0]--;
 
				}
 

	
 
				if (selected_id[0] != INVALID_ENGINE) {
 
					byte subtype = AircraftVehInfo(selected_id[0])->subtype;
 

	
 
					if ((subtype & AIR_CTOL) == (AircraftVehInfo(engine_id)->subtype & AIR_CTOL) &&
 
							HASBIT(e->player_avail, _local_player)) {
 
						if (sel[1] == 0) selected_id[1] = engine_id;
 
						if (IS_INT_INSIDE(--pos2, -w->vscroll.cap, 0)) {
 
							DrawString(x2+62, y2+7, GetCustomEngineName(engine_id), sel[1]==0 ? 0xC : 0x10);
 
							DrawAircraftEngine(x2+29, y2+10, engine_id, GetEnginePalette(engine_id, _local_player));
 
							y2 += 24;
 
						}
 
						sel[1]--;
 
					}
 
				}
 
			} while (++engine_id, ++e,--num);
 
			break;
 
		}   // end of aircraft
 
	}
 
}
 

	
 
static void DrawVehiclePurchaseInfo(const int x, const int y, uint w, const EngineID engine_number)
 
{
 
	switch (GetEngine(engine_number)->type) {
 
		case VEH_Train:
 
			if ((RailVehInfo(engine_number)->flags & RVI_WAGON) == 0) {
 
				DrawTrainEnginePurchaseInfo(x, y, w, engine_number);
 
			} else {
 
				DrawTrainWagonPurchaseInfo(x, y, w, engine_number);
 
			}
 
			break;
 

	
 
		case VEH_Road: DrawRoadVehPurchaseInfo(x, y, w, engine_number);      break;
 
		case VEH_Ship: DrawShipPurchaseInfo(x, y, w, engine_number);         break;
 
		case VEH_Aircraft: DrawAircraftPurchaseInfo(x, y, w, engine_number); break;
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
 
{
 
	static const StringID _vehicle_type_names[] = {
 
		STR_019F_TRAIN,
 
		STR_019C_ROAD_VEHICLE,
 
		STR_019E_SHIP,
 
		STR_019D_AIRCRAFT
 
	};
 

	
 
	switch (e->event) {
 
		case WE_PAINT: {
 
				Player *p = GetPlayer(_local_player);
 
				int pos = w->vscroll.pos;
 
				EngineID selected_id[2] = { INVALID_ENGINE, INVALID_ENGINE };
 
				int x = 1;
 
				int y = 15;
 
				int pos2 = w->vscroll2.pos;
 
				int x2 = 1 + 228;
 
				int y2 = 15;
 
				int sel[2];
 
				byte i;
 
				sel[0] = WP(w,replaceveh_d).sel_index[0];
 
				sel[1] = WP(w,replaceveh_d).sel_index[1];
 

	
 
				SetupScrollStuffForReplaceWindow(w);
 

	
 
				selected_id[0] = WP(w,replaceveh_d).sel_engine[0];
 
				selected_id[1] = WP(w,replaceveh_d).sel_engine[1];
 

	
 
				// Disable the "Start Replacing" button if:
 
				//    Either list is empty
 
				// or Both lists have the same vehicle selected
 
				// or The selected replacement engine has a replacement (to prevent loops)
 
				// or The right list (new replacement) has the existing replacement vehicle selected
 
				SetWindowWidgetDisabledState(w, 4,
 
						selected_id[0] == INVALID_ENGINE ||
 
						selected_id[1] == INVALID_ENGINE ||
 
						selected_id[0] == selected_id[1] ||
 
						EngineReplacementForPlayer(p, selected_id[1]) != INVALID_ENGINE ||
 
						EngineReplacementForPlayer(p, selected_id[0]) == selected_id[1]);
 

	
 
				// Disable the "Stop Replacing" button if:
 
				//    The left list (existing vehicle) is empty
 
				// or The selected vehicle has no replacement set up
 
				SetWindowWidgetDisabledState(w, 6,
 
						selected_id[0] == INVALID_ENGINE ||
 
						!EngineHasReplacementForPlayer(p, selected_id[0]));
 

	
 
				// now the actual drawing of the window itself takes place
 
				SetDParam(0, _vehicle_type_names[WP(w, replaceveh_d).vehicletype - VEH_Train]);
 

	
 
				if (WP(w, replaceveh_d).vehicletype == VEH_Train) {
 
					// set on/off for renew_keep_length
 
					SetDParam(1, p->renew_keep_length ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF);
 

	
 
					// set wagon/engine button
 
					SetDParam(2, WP(w, replaceveh_d).wagon_btnstate ? STR_ENGINES : STR_WAGONS);
 
				}
 

	
 
				DrawWindowWidgets(w);
 

	
 
				// sets up the string for the vehicle that is being replaced to
 
				if (selected_id[0] != INVALID_ENGINE) {
 
					if (!EngineHasReplacementForPlayer(p, selected_id[0])) {
 
						SetDParam(0, STR_NOT_REPLACING);
 
					} else {
 
						SetDParam(0, GetCustomEngineName(EngineReplacementForPlayer(p, selected_id[0])));
 
					}
 
				} else {
 
					SetDParam(0, STR_NOT_REPLACING_VEHICLE_SELECTED);
 
				}
 

	
 
				DrawString(145, w->widget[5].top + 1, STR_02BD, 0x10);
 

	
 
				/* now we draw the two arrays according to what we just counted */
 
				DrawEngineArrayInReplaceWindow(w, x, y, x2, y2, pos, pos2, sel[0], sel[1], selected_id[0], selected_id[1]);
 

	
 
				WP(w,replaceveh_d).sel_engine[0] = selected_id[0];
 
				WP(w,replaceveh_d).sel_engine[1] = selected_id[1];
 
				/* now we draw the info about the vehicles we selected */
 
				for (i = 0 ; i < 2 ; i++) {
 
					if (selected_id[i] != INVALID_ENGINE) {
 
						const Widget *wi = &w->widget[i == 0 ? 3 : 11];
 
						DrawVehiclePurchaseInfo(wi->left + 2 , wi->top + 1, wi->right - wi->left - 2, selected_id[i]);
 
					}
 
				}
 
			} break;   // end of paint
 

	
 
		case WE_CLICK: {
 
			// these 3 variables is used if any of the lists is clicked
 
			uint16 click_scroll_pos = w->vscroll2.pos;
 
			uint16 click_scroll_cap = w->vscroll2.cap;
 
			byte click_side = 1;
 

	
 
			switch (e->we.click.widget) {
 
				case 12:
 
					WP(w, replaceveh_d).wagon_btnstate = !(WP(w, replaceveh_d).wagon_btnstate);
 
					SetWindowDirty(w);
 
					break;
 

	
 
				case 14:
 
				case 15: /* Railtype selection dropdown menu */
 
					ShowDropDownMenu(w, _rail_types_list, _railtype_selected_in_replace_gui, 15, 0, ~GetPlayer(_local_player)->avail_railtypes);
 
					break;
 

	
 
				case 17: /* toggle renew_keep_length */
 
					DoCommandP(0, 5, GetPlayer(_local_player)->renew_keep_length ? 0 : 1, NULL, CMD_SET_AUTOREPLACE);
 
					break;
 

	
 
				case 4: { /* Start replacing */
 
					EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
 
					EngineID veh_to = WP(w, replaceveh_d).sel_engine[1];
 
					DoCommandP(0, 3, veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE);
 
					break;
 
				}
 

	
 
				case 6: { /* Stop replacing */
 
					EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
 
					DoCommandP(0, 3, veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE);
 
					break;
 
				}
 

	
 
				case 7:
 
					// sets up that the left one was clicked. The default values are for the right one (9)
 
					// this way, the code for 9 handles both sides
 
					click_scroll_pos = w->vscroll.pos;
 
					click_scroll_cap = w->vscroll.cap;
 
					click_side = 0;
 
					/* FALL THROUGH */
 

	
 
				case 9: {
 
					uint i = (e->we.click.pt.y - 14) / w->resize.step_height;
 
					if (i < click_scroll_cap) {
 
						WP(w,replaceveh_d).sel_index[click_side] = i + click_scroll_pos;
 
						SetWindowDirty(w);
 
					}
 
					break;
 
				}
 
			}
 
			break;
 
		}
 

	
 
		case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
			_railtype_selected_in_replace_gui = e->we.dropdown.index;
 
			/* Reset scrollbar positions */
 
			w->vscroll.pos  = 0;
 
			w->vscroll2.pos = 0;
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case WE_RESIZE:
 
			w->vscroll.cap  += e->we.sizing.diff.y / (int)w->resize.step_height;
 
			w->vscroll2.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
 

	
 
			w->widget[7].data = (w->vscroll.cap  << 8) + 1;
 
			w->widget[9].data = (w->vscroll2.cap << 8) + 1;
 
			break;
 
	}
 
}
 

	
 
static const Widget _replace_rail_vehicle_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,       STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   443,     0,    13, STR_REPLACE_VEHICLES_WHITE, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    14,   444,   455,     0,    13, STR_NULL,       STR_STICKY_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,     0,   227,   126,   227, 0x0,            STR_NULL},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   138,   240,   251, STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,   139,   316,   228,   239, 0x0,            STR_REPLACE_HELP_REPLACE_INFO_TAB},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   306,   443,   240,   251, STR_REPLACE_VEHICLES_STOP,  STR_REPLACE_HELP_STOP_BUTTON},
 
{     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   215,    14,   125, 0x801,          STR_REPLACE_HELP_LEFT_ARRAY},
 
{  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   216,   227,    14,   125, STR_NULL,       STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{     WWT_MATRIX, RESIZE_BOTTOM,    14,   228,   443,    14,   125, 0x801,          STR_REPLACE_HELP_RIGHT_ARRAY},
 
{ WWT_SCROLL2BAR, RESIZE_BOTTOM,    14,   444,   455,    14,   125, STR_NULL,       STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,     RESIZE_TB,    14,   228,   455,   126,   227, 0x0,            STR_NULL},
 
// train specific stuff
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   138,   228,   239, STR_REPLACE_ENGINE_WAGON_SELECT,       STR_REPLACE_ENGINE_WAGON_SELECT_HELP},  // widget 12
 
{      WWT_PANEL,     RESIZE_TB,    14,   139,   153,   240,   251, 0x0,            STR_NULL},
 
{      WWT_PANEL,     RESIZE_TB,    14,   154,   277,   240,   251, 0x0,            STR_REPLACE_HELP_RAILTYPE},
 
{    WWT_TEXTBTN,     RESIZE_TB,    14,   278,   289,   240,   251, STR_0225,       STR_REPLACE_HELP_RAILTYPE},
 
{      WWT_PANEL,     RESIZE_TB,    14,   290,   305,   240,   251, 0x0,            STR_NULL},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   317,   455,   228,   239, STR_REPLACE_REMOVE_WAGON,       STR_REPLACE_REMOVE_WAGON_HELP},
 
// end of train specific stuff
 
{  WWT_RESIZEBOX,     RESIZE_TB,    14,   444,   455,   240,   251, STR_NULL,       STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _replace_road_vehicle_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                    STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   443,     0,    13, STR_REPLACE_VEHICLES_WHITE,  STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    14,   444,   455,     0,    13, STR_NULL,                    STR_STICKY_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,     0,   227,   126,   217, 0x0,                         STR_NULL},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   138,   218,   229, STR_REPLACE_VEHICLES_START,  STR_REPLACE_HELP_START_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,   139,   305,   218,   229, 0x0,                         STR_REPLACE_HELP_REPLACE_INFO_TAB},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   306,   443,   218,   229, STR_REPLACE_VEHICLES_STOP,   STR_REPLACE_HELP_STOP_BUTTON},
 
{     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   215,    14,   125, 0x801,                       STR_REPLACE_HELP_LEFT_ARRAY},
 
{  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   216,   227,    14,   125, STR_NULL,                    STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{     WWT_MATRIX, RESIZE_BOTTOM,    14,   228,   443,    14,   125, 0x801,                       STR_REPLACE_HELP_RIGHT_ARRAY},
 
{ WWT_SCROLL2BAR, RESIZE_BOTTOM,    14,   444,   455,    14,   125, STR_NULL,                    STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,     RESIZE_TB,    14,   228,   455,   126,   217, 0x0,                         STR_NULL},
 
{  WWT_RESIZEBOX,     RESIZE_TB,    14,   444,   455,   218,   229, STR_NULL,                    STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _replace_ship_aircraft_vehicle_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                    STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   443,     0,    13, STR_REPLACE_VEHICLES_WHITE,  STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{  WWT_STICKYBOX,   RESIZE_NONE,    14,   444,   455,     0,    13, STR_NULL,                    STR_STICKY_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,     0,   227,   110,   201, 0x0,                         STR_NULL},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   138,   202,   213, STR_REPLACE_VEHICLES_START,  STR_REPLACE_HELP_START_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,   139,   305,   202,   213, 0x0,                         STR_REPLACE_HELP_REPLACE_INFO_TAB},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   306,   443,   202,   213, STR_REPLACE_VEHICLES_STOP,   STR_REPLACE_HELP_STOP_BUTTON},
 
{     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   215,    14,   109, 0x401,                       STR_REPLACE_HELP_LEFT_ARRAY},
 
{  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   216,   227,    14,   109, STR_NULL,                    STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{     WWT_MATRIX, RESIZE_BOTTOM,    14,   228,   443,    14,   109, 0x401,                       STR_REPLACE_HELP_RIGHT_ARRAY},
 
{ WWT_SCROLL2BAR, RESIZE_BOTTOM,    14,   444,   455,    14,   109, STR_NULL,                    STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,     RESIZE_TB,    14,   228,   455,   110,   201, 0x0,                         STR_NULL},
 
{  WWT_RESIZEBOX,     RESIZE_TB,    14,   444,   455,   202,   213, STR_NULL,                    STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _replace_rail_vehicle_desc = {
 
	WDP_AUTO, WDP_AUTO, 456, 252,
 
	WC_REPLACE_VEHICLE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_replace_rail_vehicle_widgets,
 
	ReplaceVehicleWndProc
 
};
 

	
 
static const WindowDesc _replace_road_vehicle_desc = {
 
	WDP_AUTO, WDP_AUTO, 456, 230,
 
	WC_REPLACE_VEHICLE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_replace_road_vehicle_widgets,
 
	ReplaceVehicleWndProc
 
};
 

	
 
static const WindowDesc _replace_ship_aircraft_vehicle_desc = {
 
	WDP_AUTO, WDP_AUTO, 456, 214,
 
	WC_REPLACE_VEHICLE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_replace_ship_aircraft_vehicle_widgets,
 
	ReplaceVehicleWndProc
 
};
 

	
 

	
 
static void ShowReplaceVehicleWindow(byte vehicletype)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype);
 

	
 
	switch (vehicletype) {
 
		case VEH_Train:
 
			w = AllocateWindowDescFront(&_replace_rail_vehicle_desc, vehicletype);
 
			w->vscroll.cap  = 8;
 
			w->resize.step_height = 14;
 
			WP(w, replaceveh_d).wagon_btnstate = true;
 
			break;
 
		case VEH_Road:
 
			w = AllocateWindowDescFront(&_replace_road_vehicle_desc, vehicletype);
 
			w->vscroll.cap  = 8;
 
			w->resize.step_height = 14;
 
			break;
 
		case VEH_Ship:
 
		case VEH_Aircraft:
 
			w = AllocateWindowDescFront(&_replace_ship_aircraft_vehicle_desc, vehicletype);
 
			w->vscroll.cap  = 4;
 
			w->resize.step_height = 24;
 
			break;
 
		default: return;
 
	}
 

	
 
	w->caption_color = _local_player;
 
	WP(w, replaceveh_d).vehicletype = vehicletype;
 
	w->vscroll2.cap = w->vscroll.cap;   // these two are always the same
 
}
 

	
 
void InitializeGUI(void)
 
{
 
	memset(&_sorting, 0, sizeof(_sorting));
 
}
 

	
 
/** Assigns an already open vehicle window to a new vehicle.
 
 * Assigns an already open vehicle window to a new vehicle. If the vehicle got
 
 * any sub window open (orders and so on) it will change owner too.
 
 * @param *from_v the current owner of the window
 
 * @param *to_v the new owner of the window
 
 */
 
void ChangeVehicleViewWindow(const Vehicle *from_v, const Vehicle *to_v)
 
{
 
	Window *w;
 

	
 
	w = FindWindowById(WC_VEHICLE_VIEW, from_v->index);
 
	if (w != NULL) {
 
		w->window_number = to_v->index;
 
		WP(w, vp_d).follow_vehicle = to_v->index;
 
		SetWindowDirty(w);
 

	
 
		w = FindWindowById(WC_VEHICLE_ORDERS, from_v->index);
 
		if (w != NULL) {
 
			w->window_number = to_v->index;
 
			SetWindowDirty(w);
 
		}
 

	
 
		w = FindWindowById(WC_VEHICLE_REFIT, from_v->index);
 
		if (w != NULL) {
 
			w->window_number = to_v->index;
 
			SetWindowDirty(w);
 
		}
 

	
 
		w = FindWindowById(WC_VEHICLE_DETAILS, from_v->index);
 
		if (w != NULL) {
 
			w->window_number = to_v->index;
 
			SetWindowDirty(w);
 
		}
 
	}
 
}
 

	
 
/*
 
 * Start of functions regarding vehicle list windows
 
 */
 

	
 
enum {
 
	PLY_WND_PRC__OFFSET_TOP_WIDGET = 26,
 
	PLY_WND_PRC__SIZE_OF_ROW_SMALL = 26,
 
	PLY_WND_PRC__SIZE_OF_ROW_BIG   = 36,
 
};
 

	
 
enum VehicleListWindowWidgets {
 
	VLW_WIDGET_CLOSEBOX = 0,
 
	VLW_WIDGET_CAPTION,
 
	VLW_WIDGET_STICKY,
 
	VLW_WIDGET_SORT_ORDER,
 
	VLW_WIDGET_SORT_BY_TEXT,
 
	VLW_WIDGET_SORT_BY_PULLDOWN,
 
	VLW_WIDGET_EMPTY_TOP_RIGHT,
 
	VLW_WIDGET_LIST,
 
	VLW_WIDGET_SCROLLBAR,
 
	VLW_WIDGET_OTHER_PLAYER_FILLER,
 
	VLW_WIDGET_NEW_VEHICLES,
 
	VLW_WIDGET_MANAGE_VEHICLES,
 
	VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
 
	VLW_WIDGET_STOP_ALL,
 
	VLW_WIDGET_START_ALL,
 
	VLW_WIDGET_EMPTY_BOTTOM_RIGHT,
 
	VLW_WIDGET_RESIZE,
 
};
 

	
 
static const Widget _vehicle_list_widgets[] = {
 
	{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,             STR_018B_CLOSE_WINDOW},
 
	{    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   247,     0,    13, 0x0,                  STR_018C_WINDOW_TITLE_DRAG_THIS},
 
	{  WWT_STICKYBOX,     RESIZE_LR,    14,   248,   259,     0,    13, 0x0,                  STR_STICKY_BUTTON},
 
	{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    80,    14,    25, STR_SORT_BY,          STR_SORT_ORDER_TIP},
 
	{      WWT_PANEL,   RESIZE_NONE,    14,    81,   235,    14,    25, 0x0,                  STR_SORT_CRITERIA_TIP},
 
	{    WWT_TEXTBTN,   RESIZE_NONE,    14,   236,   247,    14,    25, STR_0225,             STR_SORT_CRITERIA_TIP},
 
	{      WWT_PANEL,  RESIZE_RIGHT,    14,   248,   259,    14,    25, 0x0,                  STR_NULL},
 
	{     WWT_MATRIX,     RESIZE_RB,    14,     0,   247,    26,   169, 0x0,                  STR_NULL},
 
	{  WWT_SCROLLBAR,    RESIZE_LRB,    14,   248,   259,    26,   169, 0x0,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
	/* Widget to be shown for other players hiding the following 6 widgets */
 
	{      WWT_PANEL,    RESIZE_RTB,    14,     0,   247,   170,   181, 0x0,                  STR_NULL},
 

	
 
	{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   105,   170,   181, 0x0,                  STR_AVAILABLE_ENGINES_TIP},
 
	{    WWT_TEXTBTN,     RESIZE_TB,    14,   106,   211,   170,   181, STR_MANAGE_LIST,      STR_MANAGE_LIST_TIP},
 
	{    WWT_TEXTBTN,     RESIZE_TB,    14,   212,   223,   170,   181, STR_0225,             STR_MANAGE_LIST_TIP},
 

	
 
	{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,   224,   235,   170,   181, SPR_FLAG_VEH_STOPPED, STR_MASS_STOP_LIST_TIP},
 
	{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,   236,   247,   170,   181, SPR_FLAG_VEH_RUNNING, STR_MASS_START_LIST_TIP},
 
	{      WWT_PANEL,    RESIZE_RTB,    14,   248,   247,   170,   181, 0x0,                  STR_NULL},
 
	{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   248,   259,   170,   181, 0x0,                  STR_RESIZE_BUTTON},
 
	{   WIDGETS_END},
 
};
 

	
 
static void CreateVehicleListWindow(Window *w)
 
{
 
	vehiclelist_d *vl = &WP(w, vehiclelist_d);
 
	uint16 window_type = w->window_number & VLW_MASK;
 
	PlayerID player = GB(w->window_number, 0, 8);
 

	
 
	vl->vehicle_type = GB(w->window_number, 11, 5);
 
	vl->length_of_sort_list = 0;
 
	vl->sort_list = NULL;
 
	w->caption_color = player;
 

	
 
	/* Hide the widgets that we will not use in this window
 
	 * Some windows contains actions only fit for the owner */
 
	if (player == _local_player) {
 
		HideWindowWidget(w, VLW_WIDGET_OTHER_PLAYER_FILLER);
 
		SetWindowWidgetDisabledState(w, VLW_WIDGET_NEW_VEHICLES, window_type != VLW_STANDARD);
 
	} else {
 
		SetWindowWidgetsHiddenState(w, true,
 
			VLW_WIDGET_NEW_VEHICLES,
 
			VLW_WIDGET_MANAGE_VEHICLES,
 
			VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
 
			VLW_WIDGET_STOP_ALL,
 
			VLW_WIDGET_START_ALL,
 
			VLW_WIDGET_EMPTY_BOTTOM_RIGHT,
 
			WIDGET_LIST_END);
 
	}
 

	
 
	/* Set up the window widgets */
 
	switch (vl->vehicle_type) {
 
		case VEH_Train:
 
			w->widget[VLW_WIDGET_LIST].tooltips          = STR_883D_TRAINS_CLICK_ON_TRAIN_FOR;
 
			w->widget[VLW_WIDGET_NEW_VEHICLES].data = STR_8815_NEW_VEHICLES;
 
			break;
 

	
 
		case VEH_Road:
 
			w->widget[VLW_WIDGET_LIST].tooltips          = STR_901A_ROAD_VEHICLES_CLICK_ON;
 
			w->widget[VLW_WIDGET_NEW_VEHICLES].data = STR_9004_NEW_VEHICLES;
 
			break;
 

	
 
		case VEH_Ship:
 
			w->widget[VLW_WIDGET_LIST].tooltips          = STR_9823_SHIPS_CLICK_ON_SHIP_FOR;
 
			w->widget[VLW_WIDGET_NEW_VEHICLES].data = STR_9804_NEW_SHIPS;
 
			break;
 

	
 
		case VEH_Aircraft:
 
			w->widget[VLW_WIDGET_LIST].tooltips          = STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT;
 
			w->widget[VLW_WIDGET_NEW_VEHICLES].data = STR_A003_NEW_AIRCRAFT;
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 

	
 
	switch (window_type) {
 
		case VLW_SHARED_ORDERS:
 
			w->widget[VLW_WIDGET_CAPTION].data  = STR_VEH_WITH_SHARED_ORDERS_LIST;
 
			break;
 
		case VLW_STANDARD: /* Company Name - standard widget setup */
 
			switch (vl->vehicle_type) {
 
				case VEH_Train:    w->widget[VLW_WIDGET_CAPTION].data = STR_881B_TRAINS;        break;
 
				case VEH_Road:     w->widget[VLW_WIDGET_CAPTION].data = STR_9001_ROAD_VEHICLES; break;
 
				case VEH_Ship:     w->widget[VLW_WIDGET_CAPTION].data = STR_9805_SHIPS;         break;
 
				case VEH_Aircraft: w->widget[VLW_WIDGET_CAPTION].data = STR_A009_AIRCRAFT;      break;
 
				default: NOT_REACHED(); break;
 
			}
 
			break;
 
		case VLW_STATION_LIST: /* Station Name */
 
			switch (vl->vehicle_type) {
 
				case VEH_Train:    w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_TRAINS;        break;
 
				case VEH_Road:     w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_ROAD_VEHICLES; break;
 
				case VEH_Ship:     w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_SHIPS;         break;
 
				case VEH_Aircraft: w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_AIRCRAFT;      break;
 
				default: NOT_REACHED(); break;
 
			}
 
			break;
 

	
 
		case VLW_DEPOT_LIST:
 
			switch (vl->vehicle_type) {
 
				case VEH_Train:    w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_TRAIN_DEPOT;    break;
 
				case VEH_Road:     w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_ROADVEH_DEPOT;  break;
 
				case VEH_Ship:     w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_SHIP_DEPOT;     break;
 
				case VEH_Aircraft: w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_AIRCRAFT_DEPOT; break;
 
				default: NOT_REACHED(); break;
 
			}
 
			break;
 
		default: NOT_REACHED(); break;
 
	}
 

	
 
	switch (vl->vehicle_type) {
 
		case VEH_Train:
 
			w->resize.step_width = 1;
 
			/* Fallthrough */
 
		case VEH_Road:
 
			w->vscroll.cap = 7;
 
			w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_SMALL;
 
			w->resize.height = 220 - (PLY_WND_PRC__SIZE_OF_ROW_SMALL * 3); // Minimum of 4 vehicles
 
			break;
 
		case VEH_Ship:
 
		case VEH_Aircraft:
 
			w->vscroll.cap = 4;
 
			w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_BIG;
 
			break;
 
		default: NOT_REACHED();
 
	}
 

	
 
	w->widget[VLW_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
 

	
 
	/* Set up sorting. Make the window-specific _sorting variable
 
		* point to the correct global _sorting struct so we are freed
 
		* from having conditionals during window operation */
 
	switch (vl->vehicle_type) {
 
		case VEH_Train:    vl->_sorting = &_sorting.train; break;
 
		case VEH_Road:     vl->_sorting = &_sorting.roadveh; break;
 
		case VEH_Ship:     vl->_sorting = &_sorting.ship; break;
 
		case VEH_Aircraft: vl->_sorting = &_sorting.aircraft; break;
 
		default: NOT_REACHED(); break;
 
	}
 

	
 
	vl->l.flags = VL_REBUILD | (vl->_sorting->order << (VL_DESC - 1));
 
	vl->l.sort_type = vl->_sorting->criteria;
 
	vl->sort_list = NULL;
 
	vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;	// Set up resort timer
 
}
 

	
 
static void DrawSmallOrderList(const Vehicle *v, int x, int y)
 
{
 
	const Order *order;
 
	int sel, i = 0;
 

	
 
	sel = v->cur_order_index;
 

	
 
	FOR_VEHICLE_ORDERS(v, order) {
 
		if (sel == 0) DrawString(x - 6, y, STR_SMALL_RIGHT_ARROW, 16);
 
		sel--;
 

	
 
		if (order->type == OT_GOTO_STATION) {
 
			if (v->type == VEH_Ship && IsBuoy(GetStation(order->dest))) continue;
 

	
 
			SetDParam(0, order->dest);
 
			DrawString(x, y, STR_A036, 0);
 

	
 
			y += 6;
 
			if (++i == 4) break;
 
		}
 
	}
 
}
 

	
 
static void DrawVehicleListWindow(Window *w)
 
{
 
	vehiclelist_d *vl = &WP(w, vehiclelist_d);
 
	int x = 2;
 
	int y = PLY_WND_PRC__OFFSET_TOP_WIDGET;
 
	int max;
 
	int i;
 
	const PlayerID owner = (PlayerID)w->caption_color;
 
	const Player *p = GetPlayer(owner);
 
	const uint16 window_type = w->window_number & VLW_MASK;
 
	const StationID station          = (window_type == VLW_STATION_LIST)  ? GB(w->window_number, 16, 16) : INVALID_STATION;
 
	const OrderID order              = (window_type == VLW_SHARED_ORDERS) ? GB(w->window_number, 16, 16) : INVALID_ORDER;
 
	const uint16 depot_airport_index = (window_type == VLW_DEPOT_LIST)    ? GB(w->window_number, 16, 16) : INVALID_STATION;
 

	
 
	BuildVehicleList(vl, owner, station, order, depot_airport_index, window_type);
 
	SortVehicleList(vl);
 
	SetVScrollCount(w, vl->l.list_length);
 

	
 
	/* draw the widgets */
 
	switch (window_type) {
 
		case VLW_SHARED_ORDERS: /* Shared Orders */
 
			if (vl->l.list_length == 0) {
 
				/* We can't open this window without vehicles using this order
 
				 * and we should close the window when deleting the order      */
 
				NOT_REACHED();
 
			}
 
			SetDParam(0, w->vscroll.count);
 
			break;
 

	
 
		case VLW_STANDARD: /* Company Name */
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			SetDParam(2, w->vscroll.count);
 
			break;
 

	
 
		case VLW_STATION_LIST: /* Station Name */
 
			SetDParam(0, station);
 
			SetDParam(1, w->vscroll.count);
 
			break;
 

	
 
		case VLW_DEPOT_LIST:
 
			switch (vl->vehicle_type) {
 
				case VEH_Train:    SetDParam(0, STR_8800_TRAIN_DEPOT);        break;
 
				case VEH_Road:     SetDParam(0, STR_9003_ROAD_VEHICLE_DEPOT); break;
 
				case VEH_Ship:     SetDParam(0, STR_9803_SHIP_DEPOT);         break;
 
				case VEH_Aircraft: SetDParam(0, STR_A002_AIRCRAFT_HANGAR);    break;
 
				default: NOT_REACHED(); break;
 
			}
 
			if (vl->vehicle_type == VEH_Aircraft) {
 
				SetDParam(1, depot_airport_index); // Airport name
 
			} else {
 
				SetDParam(1, GetDepot(depot_airport_index)->town_index);
 
			}
 
			SetDParam(2, w->vscroll.count);
 
			break;
 
		default: NOT_REACHED(); break;
 
	}
 

	
 
	SetWindowWidgetsDisabledState(w, vl->l.list_length == 0,
 
		VLW_WIDGET_MANAGE_VEHICLES,
 
		VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
 
		VLW_WIDGET_STOP_ALL,
 
		VLW_WIDGET_START_ALL,
 
		WIDGET_LIST_END);
 

	
 
	DrawWindowWidgets(w);
 

	
 
	/* draw sorting criteria string */
 
	DrawString(85, 15, _vehicle_sort_listing[vl->l.sort_type], 0x10);
 
	/* draw arrow pointing up/down for ascending/descending sorting */
 
	DoDrawString(vl->l.flags & VL_DESC ? DOWNARROW : UPARROW, 69, 15, 0x10);
 

	
 
	max = min(w->vscroll.pos + w->vscroll.cap, vl->l.list_length);
 
	for (i = w->vscroll.pos; i < max; ++i) {
 
		const Vehicle *v = vl->sort_list[i];
 
		StringID str;
 

	
 
		SetDParam(0, v->profit_this_year);
 
		SetDParam(1, v->profit_last_year);
 

	
 
		DrawVehicleImage(v, x + 19, y + 6, w->widget[VLW_WIDGET_LIST].right - w->widget[VLW_WIDGET_LIST].left - 20, 0, INVALID_VEHICLE);
 
		DrawString(x + 19, y + w->resize.step_height - 8, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
 

	
 
		if ((v->type == VEH_Train    && v->string_id != STR_SV_TRAIN_NAME)   ||
 
			(v->type == VEH_Road     && v->string_id != STR_SV_ROADVEH_NAME) ||
 
			(v->type == VEH_Ship     && v->string_id != STR_SV_SHIP_NAME)    ||
 
			(v->type == VEH_Aircraft && v->string_id != STR_SV_AIRCRAFT_NAME)) {
 

	
 
			/* The vehicle got a name so we will print it */
 
			SetDParam(0, v->string_id);
 
			DrawString(x + 19, y, STR_01AB, 0);
 
		}
 

	
 
		if (w->resize.step_height == PLY_WND_PRC__SIZE_OF_ROW_BIG) DrawSmallOrderList(v, x + 138, y);
 

	
 
		if (IsVehicleInDepot(v)) {
 
			str = STR_021F;
 
		} else {
 
			str = (v->age > v->max_age - 366) ? STR_00E3 : STR_00E2;
 
		}
 

	
 
		SetDParam(0, v->unitnumber);
 
		DrawString(x, y + 2, str, 0);
 

	
 
		DrawVehicleProfitButton(v, x, y + 13);
 

	
 
		y += w->resize.step_height;
 
	}
 
}
 

	
 
/*
 
 * bitmask for w->window_number
 
 * 0-7 PlayerID (owner)
 
 * 8-10 window type (use flags in vehicle_gui.h)
 
 * 11-15 vehicle type (using VEH_, but can be compressed to fewer bytes if needed)
 
 * 16-31 StationID or OrderID depending on window type (bit 8-10)
 
 **/
 
void PlayerVehWndProc(Window *w, WindowEvent *e)
 
{
 
	vehiclelist_d *vl = &WP(w, vehiclelist_d);
 

	
 
	switch (e->event) {
 
		case WE_CREATE:
 
			CreateVehicleListWindow(w);
 
			break;
 

	
 
		case WE_PAINT:
 
			DrawVehicleListWindow(w);
 
			break;
 

	
 
		case WE_CLICK: {
 
			switch (e->we.click.widget) {
 
				case VLW_WIDGET_SORT_ORDER: /* Flip sorting method ascending/descending */
 
					vl->l.flags ^= VL_DESC;
 
					vl->l.flags |= VL_RESORT;
 

	
 
					vl->_sorting->order = !!(vl->l.flags & VL_DESC);
 
					SetWindowDirty(w);
 
					break;
 
				case VLW_WIDGET_SORT_BY_TEXT: case VLW_WIDGET_SORT_BY_PULLDOWN:/* Select sorting criteria dropdown menu */
 
					ShowDropDownMenu(w, _vehicle_sort_listing, vl->l.sort_type, VLW_WIDGET_SORT_BY_PULLDOWN, 0, 0);
 
					return;
 
				case VLW_WIDGET_LIST: { /* Matrix to show vehicles */
 
					uint32 id_v = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / w->resize.step_height;
 
					const Vehicle *v;
 

	
 
					if (id_v >= w->vscroll.cap) return; // click out of bounds
 

	
 
					id_v += w->vscroll.pos;
 

	
 
					if (id_v >= vl->l.list_length) return; // click out of list bound
 

	
 
					v = vl->sort_list[id_v];
 

	
 
					switch (vl->vehicle_type) {
 
						case VEH_Train: ShowTrainViewWindow(v); break;
 
						case VEH_Road: ShowRoadVehViewWindow(v); break;
 
						case VEH_Ship: ShowShipViewWindow(v); break;
 
						case VEH_Aircraft: ShowAircraftViewWindow(v); break;
 
						default: NOT_REACHED(); break;
 
					}
 
				} break;
 

	
 
				case VLW_WIDGET_NEW_VEHICLES:
 
					switch (vl->vehicle_type) {
 
						case VEH_Train: ShowBuildTrainWindow(0); break;
 
						case VEH_Road:  ShowBuildRoadVehWindow(0); break;
 
						case VEH_Ship:  ShowBuildShipWindow(0); break;
 
						case VEH_Aircraft: ShowBuildVehicleWindow(0, vl->vehicle_type); break;
 
					}
 
					break;
 

	
 
				case VLW_WIDGET_MANAGE_VEHICLES:
 
				case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
 
					static StringID action_str[] = {
 
						STR_REPLACE_VEHICLES,
 
						STR_SEND_FOR_SERVICING,
 
						STR_NULL,
 
						INVALID_STRING_ID
 
					};
 

	
 
					static const StringID depot_name[] = {
 
						STR_SEND_TRAIN_TO_DEPOT,
 
						STR_SEND_ROAD_VEHICLE_TO_DEPOT,
 
						STR_SEND_SHIP_TO_DEPOT,
 
						STR_SEND_AIRCRAFT_TO_HANGAR
 
					};
 

	
 
					/* XXX - Substite string since the dropdown cannot handle dynamic strings */
 
					action_str[2] = depot_name[vl->vehicle_type - VEH_Train];
 
					ShowDropDownMenu(w, action_str, 0, VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN, 0, 0);
 
					break;
 
				}
 

	
 
				case VLW_WIDGET_STOP_ALL:
 
				case VLW_WIDGET_START_ALL:
 
					DoCommandP(0, GB(w->window_number, 16, 16), (w->window_number & VLW_MASK) | (1 << 6) | (e->we.click.widget == VLW_WIDGET_START_ALL ? (1 << 5) : 0) | vl->vehicle_type, NULL, CMD_MASS_START_STOP);
 
					break;
 
			}
 
		}	break;
 

	
 
		case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
			switch (e->we.dropdown.button) {
 
				case VLW_WIDGET_SORT_BY_PULLDOWN:
 
					if (vl->l.sort_type != e->we.dropdown.index) {
 
						// value has changed -> resort
 
						vl->l.flags |= VL_RESORT;
 
						vl->l.sort_type = e->we.dropdown.index;
 
						vl->_sorting->criteria = vl->l.sort_type;
 
					}
 
					break;
 
				case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN:
 
					assert(vl->l.list_length != 0);
 

	
 
					switch (e->we.dropdown.index) {
 
						case 0: /* Replace window */
 
							ShowReplaceVehicleWindow(vl->vehicle_type);
 
							break;
 
						case 1: /* Send for servicing */
 
							DoCommandP(0, GB(w->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
 
								(w->window_number & VLW_MASK) | DEPOT_MASS_SEND | DEPOT_SERVICE,
 
								NULL,
 
								CMD_SEND_TO_DEPOT(vl->vehicle_type));
 
							break;
 
						case 2: /* Send to Depots */
 
							DoCommandP(0, GB(w->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
 
								(w->window_number & VLW_MASK) | DEPOT_MASS_SEND,
 
								NULL,
 
								CMD_SEND_TO_DEPOT(vl->vehicle_type));
 
							break;
 

	
 
						default: NOT_REACHED();
 
					}
 
					break;
 
				default: NOT_REACHED();
 
			}
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case WE_DESTROY:
 
			free((void*)vl->sort_list);
 
			break;
 

	
 
		case WE_TICK: /* resort the list every 20 seconds orso (10 days) */
 
			if (--vl->l.resort_timer == 0) {
 
				StationID station = ((w->window_number & VLW_MASK) == VLW_STATION_LIST) ? GB(w->window_number, 16, 16) : INVALID_STATION;
 
				PlayerID owner = (PlayerID)w->caption_color;
 

	
 
				DEBUG(misc, 3, "Periodic resort %d list player %d at station %d", vl->vehicle_type, owner, station);
 
				vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
 
				vl->l.flags |= VL_RESORT;
 
				SetWindowDirty(w);
 
			}
 
			break;
 

	
 
		case WE_RESIZE: /* Update the scroll + matrix */
 
			w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
 
			w->widget[VLW_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
 
			break;
 
	}
 
}
 

	
 
static const WindowDesc _player_vehicle_list_train_desc = {
 
	WDP_AUTO, WDP_AUTO, 260, 182,
 
	WC_TRAINS_LIST, 0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_vehicle_list_widgets,
 
	PlayerVehWndProc
 
};
 

	
 
static const WindowDesc _player_vehicle_list_road_veh_desc = {
 
	WDP_AUTO, WDP_AUTO, 260, 182,
 
	WC_ROADVEH_LIST,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_vehicle_list_widgets,
 
	PlayerVehWndProc
 
};
 

	
 
static const WindowDesc _player_vehicle_list_ship_desc = {
 
	WDP_AUTO, WDP_AUTO, 260, 182,
 
	WC_SHIPS_LIST,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_vehicle_list_widgets,
 
	PlayerVehWndProc
 
};
 

	
 
static const WindowDesc _player_vehicle_list_aircraft_desc = {
 
	WDP_AUTO, WDP_AUTO, 260, 182,
 
	WC_AIRCRAFT_LIST,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_vehicle_list_widgets,
 
	PlayerVehWndProc
 
};
 

	
 
static void ShowVehicleListWindowLocal(PlayerID player, byte vehicle_type, StationID station, OrderID order, uint16 depot_airport_index)
 
{
 
	Window *w;
 
	WindowNumber num;
 

	
 
	if (!IsValidPlayer(player)) return;
 

	
 
	num = (vehicle_type << 11) | player;
 
	if (order != INVALID_ORDER) {
 
		num |= (order << 16) | VLW_SHARED_ORDERS;
 
	} else if (depot_airport_index != INVALID_STATION) {
 
		num |= (depot_airport_index << 16) | VLW_DEPOT_LIST;
 
	} else if (station == INVALID_STATION) {
 
		num |= VLW_STANDARD;
 
	} else {
 
		num |= (station << 16) | VLW_STATION_LIST;
 
	}
 

	
 
	/* The vehicle list windows have been unified. Just some strings need
 
	 * to be changed which happens in the WE_CREATE event and resizing
 
	 * some of the windows to the correct size */
 
	switch (vehicle_type) {
 
		default: NOT_REACHED();
 
		case VEH_Train:
 
			w = AllocateWindowDescFront(&_player_vehicle_list_train_desc, num);
 
			if (w != NULL) ResizeWindow(w, 65, 38);
 
			break;
 
		case VEH_Road:
 
			w = AllocateWindowDescFront(&_player_vehicle_list_road_veh_desc, num);
 
			if (w != NULL) ResizeWindow(w, 0, 38);
 
			break;
 
		case VEH_Ship:
 
			w = AllocateWindowDescFront(&_player_vehicle_list_ship_desc, num);
 
			break;
 
		case VEH_Aircraft:
 
			w = AllocateWindowDescFront(&_player_vehicle_list_aircraft_desc, num);
 
			break;
 
	}
 

	
 
	if (w != NULL) {
 
		/* Set the minimum window size to the current window size */
 
		w->resize.width = w->width;
 
		w->resize.height = w->height;
 
	}
 
}
 

	
 
void ShowVehicleListWindow(PlayerID player, StationID station, byte vehicle_type)
 
{
 
	ShowVehicleListWindowLocal(player, vehicle_type, station, INVALID_ORDER, INVALID_STATION);
 
}
 

	
 
void ShowVehWithSharedOrders(Vehicle *v, byte vehicle_type)
 
{
 
	if (v->orders == NULL) return; // no shared list to show
 
	ShowVehicleListWindowLocal(v->owner, vehicle_type, INVALID_STATION, v->orders->index, INVALID_STATION);
 
}
 

	
 
void ShowVehDepotOrders(PlayerID player, byte vehicle_type, TileIndex depot_tile)
 
{
 
	uint16 depot_airport_index;
 

	
 
	if (vehicle_type == VEH_Aircraft) {
 
		depot_airport_index = GetStationIndex(depot_tile);
 
	} else {
 
		Depot *depot = GetDepotByTile(depot_tile);
 
		if (depot == NULL) return; // no depot to show
 
		depot_airport_index = depot->index;
 
	}
 
	ShowVehicleListWindowLocal(player, vehicle_type, INVALID_STATION, INVALID_ORDER, depot_airport_index);
 
}
src/video/dedicated_v.c
Show inline comments
 
deleted file
src/video/dedicated_v.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../openttd.h"
 
#include "../debug.h"
 
#include "../functions.h"
 
#include "../gfx.h"
 
#include "../network/network.h"
 
#include "../window.h"
 
#include "../console.h"
 
#include "../variables.h"
 
#include "../genworld.h"
 
#include "dedicated_v.h"
 

	
 
#ifdef BEOS_NET_SERVER
 
#include <net/socket.h>
 
#endif
 

	
 
#ifdef __OS2__
 
#	include <sys/time.h> /* gettimeofday */
 
#	include <sys/types.h>
 
#	include <unistd.h>
 
#	include <conio.h>
 

	
 
#	define INCL_DOS
 
#	include <os2.h>
 

	
 
#	define STDIN 0  /* file descriptor for standard input */
 

	
 
/**
 
 * Switches OpenTTD to a console app at run-time, instead of a PM app
 
 * Necessary to see stdout, etc. */
 
static void OS2_SwitchToConsoleMode(void)
 
{
 
	PPIB pib;
 
	PTIB tib;
 

	
 
	DosGetInfoBlocks(&tib, &pib);
 

	
 
	// Change flag from PM to VIO
 
	pib->pib_ultype = 3;
 
}
 
#endif
 

	
 
#ifdef UNIX
 
#	include <sys/time.h> /* gettimeofday */
 
#	include <sys/types.h>
 
#	include <unistd.h>
 
#	include <signal.h>
 
#	define STDIN 0  /* file descriptor for standard input */
 

	
 
/* Signal handlers */
 
static void DedicatedSignalHandler(int sig)
 
{
 
	_exit_game = true;
 
	signal(sig, DedicatedSignalHandler);
 
}
 
#endif
 

	
 
#ifdef WIN32
 
#include <windows.h> /* GetTickCount */
 
#include <conio.h>
 
#include <time.h>
 
#include <tchar.h>
 
static HANDLE _hInputReady, _hWaitForInputHandling;
 
static HANDLE _hThread; // Thread to close
 
static char _win_console_thread_buffer[200];
 

	
 
/* Windows Console thread. Just loop and signal when input has been received */
 
static void WINAPI CheckForConsoleInput(void)
 
{
 
	while (true) {
 
		fgets(_win_console_thread_buffer, lengthof(_win_console_thread_buffer), stdin);
 
		/* Signal input waiting that input is read and wait for it being handled
 
		 * SignalObjectAndWait() should be used here, but it's unsupported in Win98< */
 
		SetEvent(_hInputReady);
 
		WaitForSingleObject(_hWaitForInputHandling, INFINITE);
 
	}
 
}
 

	
 
static void CreateWindowsConsoleThread(void)
 
{
 
	DWORD dwThreadId;
 
	/* Create event to signal when console input is ready */
 
	_hInputReady = CreateEvent(NULL, false, false, NULL);
 
	_hWaitForInputHandling = CreateEvent(NULL, false, false, NULL);
 
	if (_hInputReady == NULL || _hWaitForInputHandling == NULL) error("Cannot create console event!");
 

	
 
	_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CheckForConsoleInput, NULL, 0, &dwThreadId);
 
	if (_hThread == NULL) error("Cannot create console thread!");
 

	
 
	DEBUG(driver, 2, "Windows console thread started");
 
}
 

	
 
static void CloseWindowsConsoleThread(void)
 
{
 
	CloseHandle(_hThread);
 
	CloseHandle(_hInputReady);
 
	CloseHandle(_hWaitForInputHandling);
 
	DEBUG(driver, 2, "Windows console thread shut down");
 
}
 

	
 
#endif
 

	
 

	
 
static void *_dedicated_video_mem;
 

	
 
extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm);
 
extern void SwitchMode(int new_mode);
 

	
 

	
 
static const char *DedicatedVideoStart(const char * const *parm)
 
{
 
	_screen.width = _screen.pitch = _cur_resolution[0];
 
	_screen.height = _cur_resolution[1];
 
	_dedicated_video_mem = malloc(_cur_resolution[0]*_cur_resolution[1]);
 

	
 
	SetDebugString("net=6");
 

	
 
#ifdef WIN32
 
	// For win32 we need to allocate a console (debug mode does the same)
 
	CreateConsole();
 
	CreateWindowsConsoleThread();
 
	SetConsoleTitle(_T("OpenTTD Dedicated Server"));
 
#endif
 

	
 
#ifdef __OS2__
 
	// For OS/2 we also need to switch to console mode instead of PM mode
 
	OS2_SwitchToConsoleMode();
 
#endif
 

	
 
	DEBUG(driver, 1, "Loading dedicated server");
 
	return NULL;
 
}
 

	
 
static void DedicatedVideoStop(void)
 
{
 
#ifdef WIN32
 
	CloseWindowsConsoleThread();
 
#endif
 
	free(_dedicated_video_mem);
 
}
 

	
 
static void DedicatedVideoMakeDirty(int left, int top, int width, int height) {}
 
static bool DedicatedVideoChangeRes(int w, int h) { return false; }
 
static void DedicatedVideoFullScreen(bool fs) {}
 

	
 
#if defined(UNIX) || defined(__OS2__)
 
static bool InputWaiting(void)
 
{
 
	struct timeval tv;
 
	fd_set readfds;
 

	
 
	tv.tv_sec = 0;
 
	tv.tv_usec = 1;
 

	
 
	FD_ZERO(&readfds);
 
	FD_SET(STDIN, &readfds);
 

	
 
	/* don't care about writefds and exceptfds: */
 
	return select(STDIN + 1, &readfds, NULL, NULL, &tv) > 0;
 
}
 

	
 
static uint32 GetTime(void)
 
{
 
	struct timeval tim;
 

	
 
	gettimeofday(&tim, NULL);
 
	return tim.tv_usec / 1000 + tim.tv_sec * 1000;
 
}
 

	
 
#else
 

	
 
static bool InputWaiting(void)
 
{
 
	return WaitForSingleObject(_hInputReady, 1) == WAIT_OBJECT_0;
 
}
 

	
 
static uint32 GetTime(void)
 
{
 
	return GetTickCount();
 
}
 

	
 
#endif
 

	
 
static void DedicatedHandleKeyInput(void)
 
{
 
	static char input_line[200] = "";
 

	
 
	if (!InputWaiting()) return;
 

	
 
	if (_exit_game) return;
 

	
 
#if defined(UNIX) || defined(__OS2__)
 
	if (fgets(input_line, lengthof(input_line), stdin) == NULL) return;
 
#else
 
	/* Handle console input, and singal console thread, it can accept input again */
 
	strncpy(input_line, _win_console_thread_buffer, lengthof(input_line));
 
	SetEvent(_hWaitForInputHandling);
 
#endif
 

	
 
	/* XXX - strtok() does not 'forget' \n\r if it is the first character! */
 
	strtok(input_line, "\r\n"); // Forget about the final \n (or \r)
 
	{ /* Remove any special control characters */
 
		uint i;
 
		for (i = 0; i < lengthof(input_line); i++) {
 
			if (input_line[i] == '\n' || input_line[i] == '\r') // cut missed beginning '\0'
 
				input_line[i] = '\0';
 

	
 
			if (input_line[i] == '\0')
 
				break;
 

	
 
			if (!IS_INT_INSIDE(input_line[i], ' ', 256))
 
				input_line[i] = ' ';
 
		}
 
	}
 

	
 
	IConsoleCmdExec(input_line); // execute command
 
}
 

	
 
static void DedicatedVideoMainLoop(void)
 
{
 
	uint32 cur_ticks = GetTime();
 
	uint32 next_tick = cur_ticks + 30;
 

	
 
	/* Signal handlers */
 
#ifdef UNIX
 
	signal(SIGTERM, DedicatedSignalHandler);
 
	signal(SIGINT, DedicatedSignalHandler);
 
	signal(SIGQUIT, DedicatedSignalHandler);
 
#endif
 

	
 
	// Load the dedicated server stuff
 
	_is_network_server = true;
 
	_network_dedicated = true;
 
	_network_playas = PLAYER_SPECTATOR;
 
	_local_player = PLAYER_SPECTATOR;
 

	
 
	/* If SwitchMode is SM_LOAD, it means that the user used the '-g' options */
 
	if (_switch_mode != SM_LOAD) {
 
		StartNewGameWithoutGUI(GENERATE_NEW_SEED);
 
		SwitchMode(_switch_mode);
 
		_switch_mode = SM_NONE;
 
	} else {
 
		_switch_mode = SM_NONE;
 
		/* First we need to test if the savegame can be loaded, else we will end up playing the
 
		 *  intro game... */
 
		if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) {
 
			/* Loading failed, pop out.. */
 
			DEBUG(net, 0, "Loading requested map failed, aborting");
 
			_networking = false;
 
		} else {
 
			/* We can load this game, so go ahead */
 
			SwitchMode(SM_LOAD);
 
		}
 
	}
 

	
 
	// Done loading, start game!
 

	
 
	if (!_networking) {
 
		DEBUG(net, 0, "Dedicated server could not be started, aborting");
 
		return;
 
	}
 

	
 
	while (!_exit_game) {
 
		uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
 
		InteractiveRandom(); // randomness
 

	
 
		if (!_dedicated_forks)
 
			DedicatedHandleKeyInput();
 

	
 
		cur_ticks = GetTime();
 
		if (cur_ticks >= next_tick || cur_ticks < prev_cur_ticks) {
 
			next_tick = cur_ticks + 30;
 

	
 
			GameLoop();
 
			_screen.dst_ptr = _dedicated_video_mem;
 
			UpdateWindows();
 
		}
 
		CSleep(1);
 
	}
 
}
 

	
 
const HalVideoDriver _dedicated_video_driver = {
 
	DedicatedVideoStart,
 
	DedicatedVideoStop,
 
	DedicatedVideoMakeDirty,
 
	DedicatedVideoMainLoop,
 
	DedicatedVideoChangeRes,
 
	DedicatedVideoFullScreen,
 
};
 

	
 
#endif /* ENABLE_NETWORK */
src/video/null_v.c
Show inline comments
 
deleted file
src/video/null_v.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "../gfx.h"
 
#include "../variables.h"
 
#include "../window.h"
 
#include "null_v.h"
 

	
 
static void* _null_video_mem = NULL;
 

	
 
static const char* NullVideoStart(const char* const* parm)
 
{
 
	_screen.width = _screen.pitch = _cur_resolution[0];
 
	_screen.height = _cur_resolution[1];
 
	_null_video_mem = malloc(_cur_resolution[0] * _cur_resolution[1]);
 
	return NULL;
 
}
 

	
 
static void NullVideoStop(void) { free(_null_video_mem); }
 

	
 
static void NullVideoMakeDirty(int left, int top, int width, int height) {}
 

	
 
static void NullVideoMainLoop(void)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < 1000; i++) {
 
		GameLoop();
 
		_screen.dst_ptr = _null_video_mem;
 
		UpdateWindows();
 
	}
 
}
 

	
 
static bool NullVideoChangeRes(int w, int h) { return false; }
 
static void NullVideoFullScreen(bool fs) {}
 

	
 
const HalVideoDriver _null_video_driver = {
 
	NullVideoStart,
 
	NullVideoStop,
 
	NullVideoMakeDirty,
 
	NullVideoMainLoop,
 
	NullVideoChangeRes,
 
	NullVideoFullScreen,
 
};
src/video/sdl_v.c
Show inline comments
 
deleted file
src/video/sdl_v.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 

	
 
#ifdef WITH_SDL
 

	
 
#include "../openttd.h"
 
#include "../debug.h"
 
#include "../functions.h"
 
#include "../gfx.h"
 
#include "../macros.h"
 
#include "../sdl.h"
 
#include "../window.h"
 
#include "../network/network.h"
 
#include "../variables.h"
 
#include "sdl_v.h"
 
#include <SDL.h>
 

	
 
static SDL_Surface *_sdl_screen;
 
static bool _all_modes;
 

	
 
#define MAX_DIRTY_RECTS 100
 
static SDL_Rect _dirty_rects[MAX_DIRTY_RECTS];
 
static int _num_dirty_rects;
 

	
 
static void SdlVideoMakeDirty(int left, int top, int width, int height)
 
{
 
	if (_num_dirty_rects < MAX_DIRTY_RECTS) {
 
		_dirty_rects[_num_dirty_rects].x = left;
 
		_dirty_rects[_num_dirty_rects].y = top;
 
		_dirty_rects[_num_dirty_rects].w = width;
 
		_dirty_rects[_num_dirty_rects].h = height;
 
	}
 
	_num_dirty_rects++;
 
}
 

	
 
static void UpdatePalette(uint start, uint count)
 
{
 
	SDL_Color pal[256];
 
	uint i;
 

	
 
	for (i = 0; i != count; i++) {
 
		pal[i].r = _cur_palette[start + i].r;
 
		pal[i].g = _cur_palette[start + i].g;
 
		pal[i].b = _cur_palette[start + i].b;
 
		pal[i].unused = 0;
 
	}
 

	
 
	SDL_CALL SDL_SetColors(_sdl_screen, pal, start, count);
 
}
 

	
 
static void InitPalette(void)
 
{
 
	UpdatePalette(0, 256);
 
}
 

	
 
static void CheckPaletteAnim(void)
 
{
 
	if (_pal_last_dirty != -1) {
 
		UpdatePalette(_pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1);
 
		_pal_last_dirty = -1;
 
	}
 
}
 

	
 
static void DrawSurfaceToScreen(void)
 
{
 
	int n = _num_dirty_rects;
 
	if (n != 0) {
 
		_num_dirty_rects = 0;
 
		if (n > MAX_DIRTY_RECTS)
 
			SDL_CALL SDL_UpdateRect(_sdl_screen, 0, 0, 0, 0);
 
		else
 
			SDL_CALL SDL_UpdateRects(_sdl_screen, n, _dirty_rects);
 
	}
 
}
 

	
 
static const uint16 default_resolutions[][2] = {
 
	{ 640,  480},
 
	{ 800,  600},
 
	{1024,  768},
 
	{1152,  864},
 
	{1280,  800},
 
	{1280,  960},
 
	{1280, 1024},
 
	{1400, 1050},
 
	{1600, 1200},
 
	{1680, 1050},
 
	{1920, 1200}
 
};
 

	
 
static void GetVideoModes(void)
 
{
 
	int i;
 
	SDL_Rect **modes;
 

	
 
	modes = SDL_CALL SDL_ListModes(NULL, SDL_SWSURFACE + (_fullscreen ? SDL_FULLSCREEN : 0));
 

	
 
	if (modes == NULL)
 
		error("sdl: no modes available");
 

	
 
	_all_modes = (modes == (void*)-1);
 

	
 
	if (_all_modes) {
 
		// all modes available, put some default ones here
 
		memcpy(_resolutions, default_resolutions, sizeof(default_resolutions));
 
		_num_resolutions = lengthof(default_resolutions);
 
	} else {
 
		int n = 0;
 
		for (i = 0; modes[i]; i++) {
 
			int w = modes[i]->w;
 
			int h = modes[i]->h;
 
			if (IS_INT_INSIDE(w, 640, MAX_SCREEN_WIDTH + 1) &&
 
					IS_INT_INSIDE(h, 480, MAX_SCREEN_HEIGHT + 1)) {
 
				int j;
 
				for (j = 0; j < n; j++) {
 
					if (_resolutions[j][0] == w && _resolutions[j][1] == h) break;
 
				}
 

	
 
				if (j == n) {
 
					_resolutions[j][0] = w;
 
					_resolutions[j][1] = h;
 
					if (++n == lengthof(_resolutions)) break;
 
				}
 
			}
 
		}
 
		_num_resolutions = n;
 
		SortResolutions(_num_resolutions);
 
	}
 
}
 

	
 
static void GetAvailableVideoMode(int *w, int *h)
 
{
 
	int i;
 
	int best;
 
	uint delta;
 

	
 
	// all modes available?
 
	if (_all_modes) return;
 

	
 
	// is the wanted mode among the available modes?
 
	for (i = 0; i != _num_resolutions; i++) {
 
		if (*w == _resolutions[i][0] && *h == _resolutions[i][1]) return;
 
	}
 

	
 
	// use the closest possible resolution
 
	best = 0;
 
	delta = abs((_resolutions[0][0] - *w) * (_resolutions[0][1] - *h));
 
	for (i = 1; i != _num_resolutions; ++i) {
 
		uint newdelta = abs((_resolutions[i][0] - *w) * (_resolutions[i][1] - *h));
 
		if (newdelta < delta) {
 
			best = i;
 
			delta = newdelta;
 
		}
 
	}
 
	*w = _resolutions[best][0];
 
	*h = _resolutions[best][1];
 
}
 

	
 
#ifndef ICON_DIR
 
#define ICON_DIR "media"
 
#endif
 

	
 
#ifdef WIN32
 
/* Let's redefine the LoadBMP macro with because we are dynamically
 
 * loading SDL and need to 'SDL_CALL' all functions */
 
#undef SDL_LoadBMP
 
#define SDL_LoadBMP(file)	SDL_LoadBMP_RW(SDL_CALL SDL_RWFromFile(file, "rb"), 1)
 
#endif
 

	
 
static bool CreateMainSurface(int w, int h)
 
{
 
	extern const char _openttd_revision[];
 
	SDL_Surface *newscreen, *icon;
 
	char caption[50];
 

	
 
	GetAvailableVideoMode(&w, &h);
 

	
 
	DEBUG(driver, 1, "SDL: using mode %dx%d", w, h);
 

	
 
	/* Give the application an icon */
 
	icon = SDL_CALL SDL_LoadBMP(ICON_DIR PATHSEP "openttd.32.bmp");
 
	if (icon != NULL) {
 
		/* Get the colourkey, which will be magenta */
 
		uint32 rgbmap = SDL_CALL SDL_MapRGB(icon->format, 255, 0, 255);
 

	
 
		SDL_CALL SDL_SetColorKey(icon, SDL_SRCCOLORKEY, rgbmap);
 
		SDL_CALL SDL_WM_SetIcon(icon, NULL);
 
		SDL_CALL SDL_FreeSurface(icon);
 
	}
 

	
 
	// DO NOT CHANGE TO HWSURFACE, IT DOES NOT WORK
 
	newscreen = SDL_CALL SDL_SetVideoMode(w, h, 8, SDL_SWSURFACE | SDL_HWPALETTE | (_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE));
 
	if (newscreen == NULL)
 
		return false;
 

	
 
	_screen.width = newscreen->w;
 
	_screen.height = newscreen->h;
 
	_screen.pitch = newscreen->pitch;
 

	
 
	_sdl_screen = newscreen;
 
	InitPalette();
 

	
 
	snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
 
	SDL_CALL SDL_WM_SetCaption(caption, caption);
 
	SDL_CALL SDL_ShowCursor(0);
 

	
 
	GameSizeChanged();
 

	
 
	return true;
 
}
 

	
 
typedef struct VkMapping {
 
	uint16 vk_from;
 
	byte vk_count;
 
	byte map_to;
 
} VkMapping;
 

	
 
#define AS(x, z) {x, 0, z}
 
#define AM(x, y, z, w) {x, y - x, z}
 

	
 
static const VkMapping _vk_mapping[] = {
 
	// Pageup stuff + up/down
 
	AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN),
 
	AS(SDLK_UP,     WKC_UP),
 
	AS(SDLK_DOWN,   WKC_DOWN),
 
	AS(SDLK_LEFT,   WKC_LEFT),
 
	AS(SDLK_RIGHT,  WKC_RIGHT),
 

	
 
	AS(SDLK_HOME,   WKC_HOME),
 
	AS(SDLK_END,    WKC_END),
 

	
 
	AS(SDLK_INSERT, WKC_INSERT),
 
	AS(SDLK_DELETE, WKC_DELETE),
 

	
 
	// Map letters & digits
 
	AM(SDLK_a, SDLK_z, 'A', 'Z'),
 
	AM(SDLK_0, SDLK_9, '0', '9'),
 

	
 
	AS(SDLK_ESCAPE,    WKC_ESC),
 
	AS(SDLK_PAUSE,     WKC_PAUSE),
 
	AS(SDLK_BACKSPACE, WKC_BACKSPACE),
 

	
 
	AS(SDLK_SPACE,     WKC_SPACE),
 
	AS(SDLK_RETURN,    WKC_RETURN),
 
	AS(SDLK_TAB,       WKC_TAB),
 

	
 
	// Function keys
 
	AM(SDLK_F1, SDLK_F12, WKC_F1, WKC_F12),
 

	
 
	// Numeric part.
 
	// What is the virtual keycode for numeric enter??
 
	AM(SDLK_KP0, SDLK_KP9, WKC_NUM_0, WKC_NUM_9),
 
	AS(SDLK_KP_DIVIDE,   WKC_NUM_DIV),
 
	AS(SDLK_KP_MULTIPLY, WKC_NUM_MUL),
 
	AS(SDLK_KP_MINUS,    WKC_NUM_MINUS),
 
	AS(SDLK_KP_PLUS,     WKC_NUM_PLUS),
 
	AS(SDLK_KP_ENTER,    WKC_NUM_ENTER),
 
	AS(SDLK_KP_PERIOD,   WKC_NUM_DECIMAL)
 
};
 

	
 
static uint32 ConvertSdlKeyIntoMy(SDL_keysym *sym)
 
{
 
	const VkMapping *map;
 
	uint key = 0;
 

	
 
	for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
 
		if ((uint)(sym->sym - map->vk_from) <= map->vk_count) {
 
			key = sym->sym - map->vk_from + map->map_to;
 
			break;
 
		}
 
	}
 

	
 
	// check scancode for BACKQUOTE key, because we want the key left of "1", not anything else (on non-US keyboards)
 
#if defined(WIN32) || defined(__OS2__)
 
	if (sym->scancode == 41) key = WKC_BACKQUOTE;
 
#elif defined(__APPLE__)
 
	if (sym->scancode == 10) key = WKC_BACKQUOTE;
 
#elif defined(__MORPHOS__)
 
	if (sym->scancode == 0)  key = WKC_BACKQUOTE;  // yes, that key is code '0' under MorphOS :)
 
#elif defined(__BEOS__)
 
	if (sym->scancode == 17) key = WKC_BACKQUOTE;
 
#elif defined(__SVR4) && defined(__sun)
 
	if (sym->scancode == 60) key = WKC_BACKQUOTE;
 
	if (sym->scancode == 49) key = WKC_BACKSPACE;
 
#elif defined(__sgi__)
 
	if (sym->scancode == 22) key = WKC_BACKQUOTE;
 
#else
 
	if (sym->scancode == 49) key = WKC_BACKQUOTE;
 
#endif
 

	
 
	// META are the command keys on mac
 
	if (sym->mod & KMOD_META)  key |= WKC_META;
 
	if (sym->mod & KMOD_SHIFT) key |= WKC_SHIFT;
 
	if (sym->mod & KMOD_CTRL)  key |= WKC_CTRL;
 
	if (sym->mod & KMOD_ALT)   key |= WKC_ALT;
 
	// these two lines really help porting hotkey combos. Uncomment to use -- Bjarni
 
#if 0
 
	DEBUG(driver, 0, "Scancode character pressed %u", sym->scancode);
 
	DEBUG(driver, 0, "Unicode character pressed %u", sym->unicode);
 
#endif
 
	return (key << 16) + sym->unicode;
 
}
 

	
 
static int PollEvent(void)
 
{
 
	SDL_Event ev;
 

	
 
	if (!SDL_CALL SDL_PollEvent(&ev)) return -2;
 

	
 
	switch (ev.type) {
 
		case SDL_MOUSEMOTION:
 
			if (_cursor.fix_at) {
 
				int dx = ev.motion.x - _cursor.pos.x;
 
				int dy = ev.motion.y - _cursor.pos.y;
 
				if (dx != 0 || dy != 0) {
 
					_cursor.delta.x += dx;
 
					_cursor.delta.y += dy;
 
					SDL_CALL SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y);
 
				}
 
			} else {
 
				_cursor.delta.x = ev.motion.x - _cursor.pos.x;
 
				_cursor.delta.y = ev.motion.y - _cursor.pos.y;
 
				_cursor.pos.x = ev.motion.x;
 
				_cursor.pos.y = ev.motion.y;
 
				_cursor.dirty = true;
 
			}
 
			HandleMouseEvents();
 
			break;
 

	
 
		case SDL_MOUSEBUTTONDOWN:
 
			if (_rightclick_emulate && SDL_CALL SDL_GetModState() & KMOD_CTRL) {
 
				ev.button.button = SDL_BUTTON_RIGHT;
 
			}
 

	
 
			switch (ev.button.button) {
 
				case SDL_BUTTON_LEFT:
 
					_left_button_down = true;
 
					break;
 

	
 
				case SDL_BUTTON_RIGHT:
 
					_right_button_down = true;
 
					_right_button_clicked = true;
 
					break;
 

	
 
				case SDL_BUTTON_WHEELUP:   _cursor.wheel--; break;
 
				case SDL_BUTTON_WHEELDOWN: _cursor.wheel++; break;
 

	
 
				default: break;
 
			}
 
			HandleMouseEvents();
 
			break;
 

	
 
		case SDL_MOUSEBUTTONUP:
 
			if (_rightclick_emulate) {
 
				_right_button_down = false;
 
				_left_button_down = false;
 
				_left_button_clicked = false;
 
			} else if (ev.button.button == SDL_BUTTON_LEFT) {
 
				_left_button_down = false;
 
				_left_button_clicked = false;
 
			} else if (ev.button.button == SDL_BUTTON_RIGHT) {
 
				_right_button_down = false;
 
			}
 
			HandleMouseEvents();
 
			break;
 

	
 
		case SDL_ACTIVEEVENT:
 
			if (!(ev.active.state & SDL_APPMOUSEFOCUS)) break;
 

	
 
			if (ev.active.gain) { // mouse entered the window, enable cursor
 
				_cursor.in_window = true;
 
			} else {
 
				UndrawMouseCursor(); // mouse left the window, undraw cursor
 
				_cursor.in_window = false;
 
			}
 
			break;
 

	
 
		case SDL_QUIT: HandleExitGameRequest(); break;
 

	
 
		case SDL_KEYDOWN: /* Toggle full-screen on ALT + ENTER/F */
 
			if ((ev.key.keysym.mod & (KMOD_ALT | KMOD_META)) &&
 
					(ev.key.keysym.sym == SDLK_RETURN || ev.key.keysym.sym == SDLK_f)) {
 
				ToggleFullScreen(!_fullscreen);
 
			} else {
 
				HandleKeypress(ConvertSdlKeyIntoMy(&ev.key.keysym));
 
			}
 
			break;
 

	
 
		case SDL_VIDEORESIZE: {
 
			int w = clamp(ev.resize.w, 64, MAX_SCREEN_WIDTH);
 
			int h = clamp(ev.resize.h, 64, MAX_SCREEN_HEIGHT);
 
			ChangeResInGame(w, h);
 
			break;
 
		}
 
	}
 
	return -1;
 
}
 

	
 
static const char *SdlVideoStart(const char * const *parm)
 
{
 
	char buf[30];
 

	
 
	const char *s = SdlOpen(SDL_INIT_VIDEO);
 
	if (s != NULL) return s;
 

	
 
	SDL_CALL SDL_VideoDriverName(buf, 30);
 
	DEBUG(driver, 1, "SDL: using driver '%s'", buf);
 

	
 
	GetVideoModes();
 
	CreateMainSurface(_cur_resolution[0], _cur_resolution[1]);
 
	MarkWholeScreenDirty();
 

	
 
	SDL_CALL SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
 
	SDL_CALL SDL_EnableUNICODE(1);
 
	return NULL;
 
}
 

	
 
static void SdlVideoStop(void)
 
{
 
	SdlClose(SDL_INIT_VIDEO);
 
}
 

	
 
static void SdlVideoMainLoop(void)
 
{
 
	uint32 cur_ticks = SDL_CALL SDL_GetTicks();
 
	uint32 next_tick = cur_ticks + 30;
 
	uint32 pal_tick = 0;
 
	uint32 mod;
 
	int numkeys;
 
	Uint8 *keys;
 

	
 
	for (;;) {
 
		uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
 
		InteractiveRandom(); // randomness
 

	
 
		while (PollEvent() == -1) {}
 
		if (_exit_game) return;
 

	
 
		mod = SDL_CALL SDL_GetModState();
 
		keys = SDL_CALL SDL_GetKeyState(&numkeys);
 
#if defined(_DEBUG)
 
		if (_shift_pressed)
 
#else
 
		/* Speedup when pressing tab, except when using ALT+TAB
 
		 * to switch to another application */
 
		if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0)
 
#endif
 
		{
 
			if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
 
		} else if (_fast_forward & 2) {
 
			_fast_forward = 0;
 
		}
 

	
 
		cur_ticks = SDL_CALL SDL_GetTicks();
 
		if (cur_ticks >= next_tick || (_fast_forward && !_pause) || cur_ticks < prev_cur_ticks) {
 
			next_tick = cur_ticks + 30;
 

	
 
			_ctrl_pressed  = !!(mod & KMOD_CTRL);
 
			_shift_pressed = !!(mod & KMOD_SHIFT);
 
#ifdef _DEBUG
 
			_dbg_screen_rect = !!(mod & KMOD_CAPS);
 
#endif
 

	
 
			// determine which directional keys are down
 
			_dirkeys =
 
				(keys[SDLK_LEFT]  ? 1 : 0) |
 
				(keys[SDLK_UP]    ? 2 : 0) |
 
				(keys[SDLK_RIGHT] ? 4 : 0) |
 
				(keys[SDLK_DOWN]  ? 8 : 0);
 
			GameLoop();
 

	
 
			_screen.dst_ptr = _sdl_screen->pixels;
 
			UpdateWindows();
 
			if (++pal_tick > 4) {
 
				CheckPaletteAnim();
 
				pal_tick = 1;
 
			}
 
			DrawSurfaceToScreen();
 
		} else {
 
			SDL_CALL SDL_Delay(1);
 
			_screen.dst_ptr = _sdl_screen->pixels;
 
			DrawTextMessage();
 
			DrawMouseCursor();
 
			DrawSurfaceToScreen();
 
		}
 
	}
 
}
 

	
 
static bool SdlVideoChangeRes(int w, int h)
 
{
 
	return CreateMainSurface(w, h);
 
}
 

	
 
static void SdlVideoFullScreen(bool full_screen)
 
{
 
	_fullscreen = full_screen;
 
	GetVideoModes(); // get the list of available video modes
 
	if (_num_resolutions == 0 || !_video_driver->change_resolution(_cur_resolution[0], _cur_resolution[1])) {
 
		// switching resolution failed, put back full_screen to original status
 
		_fullscreen ^= true;
 
	}
 
}
 

	
 
const HalVideoDriver _sdl_video_driver = {
 
	SdlVideoStart,
 
	SdlVideoStop,
 
	SdlVideoMakeDirty,
 
	SdlVideoMainLoop,
 
	SdlVideoChangeRes,
 
	SdlVideoFullScreen,
 
};
 

	
 
#endif
src/video/win32_v.c
Show inline comments
 
deleted file
src/video/win32_v.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "../functions.h"
 
#include "../gfx.h"
 
#include "../macros.h"
 
#include "../network/network.h"
 
#include "../variables.h"
 
#include "../win32.h"
 
#include "../window.h"
 
#include "win32_v.h"
 
#include <windows.h>
 
#include <tchar.h>
 

	
 
static struct {
 
	HWND main_wnd;
 
	HBITMAP dib_sect;
 
	Pixel *bitmap_bits;
 
	Pixel *buffer_bits;
 
	Pixel *alloced_bits;
 
	HPALETTE gdi_palette;
 
	int width;
 
	int height;
 
	int width_org;
 
	int height_org;
 
	bool fullscreen;
 
	bool double_size;
 
	bool has_focus;
 
	bool running;
 
} _wnd;
 

	
 
bool _force_full_redraw;
 
bool _double_size;
 
bool _window_maximize;
 
uint _display_hz;
 
uint _fullscreen_bpp;
 
static uint16 _bck_resolution[2];
 

	
 
static void MakePalette(void)
 
{
 
	LOGPALETTE *pal;
 
	uint i;
 

	
 
	pal = alloca(sizeof(LOGPALETTE) + (256-1) * sizeof(PALETTEENTRY));
 

	
 
	pal->palVersion = 0x300;
 
	pal->palNumEntries = 256;
 

	
 
	for (i = 0; i != 256; i++) {
 
		pal->palPalEntry[i].peRed   = _cur_palette[i].r;
 
		pal->palPalEntry[i].peGreen = _cur_palette[i].g;
 
		pal->palPalEntry[i].peBlue  = _cur_palette[i].b;
 
		pal->palPalEntry[i].peFlags = 0;
 

	
 
	}
 
	_wnd.gdi_palette = CreatePalette(pal);
 
	if (_wnd.gdi_palette == NULL) error("CreatePalette failed!\n");
 
}
 

	
 
static void UpdatePalette(HDC dc, uint start, uint count)
 
{
 
	RGBQUAD rgb[256];
 
	uint i;
 

	
 
	for (i = 0; i != count; i++) {
 
		rgb[i].rgbRed   = _cur_palette[start + i].r;
 
		rgb[i].rgbGreen = _cur_palette[start + i].g;
 
		rgb[i].rgbBlue  = _cur_palette[start + i].b;
 
		rgb[i].rgbReserved = 0;
 
	}
 

	
 
	SetDIBColorTable(dc, start, count, rgb);
 
}
 

	
 
typedef struct {
 
	byte vk_from;
 
	byte vk_count;
 
	byte map_to;
 
} VkMapping;
 

	
 
#define AS(x, z) {x, 0, z}
 
#define AM(x, y, z, w) {x, y - x, z}
 

	
 
static const VkMapping _vk_mapping[] = {
 
	// Pageup stuff + up/down
 
	AM(VK_PRIOR,VK_DOWN, WKC_PAGEUP, WKC_DOWN),
 
	// Map letters & digits
 
	AM('A','Z','A','Z'),
 
	AM('0','9','0','9'),
 

	
 
	AS(VK_ESCAPE,   WKC_ESC),
 
	AS(VK_PAUSE,    WKC_PAUSE),
 
	AS(VK_BACK,     WKC_BACKSPACE),
 
	AM(VK_INSERT,   VK_DELETE, WKC_INSERT, WKC_DELETE),
 

	
 
	AS(VK_SPACE,    WKC_SPACE),
 
	AS(VK_RETURN,   WKC_RETURN),
 
	AS(VK_TAB,      WKC_TAB),
 

	
 
	// Function keys
 
	AM(VK_F1, VK_F12, WKC_F1, WKC_F12),
 

	
 
	// Numeric part.
 
	// What is the virtual keycode for numeric enter??
 
	AM(VK_NUMPAD0, VK_NUMPAD9, WKC_NUM_0, WKC_NUM_9),
 
	AS(VK_DIVIDE,   WKC_NUM_DIV),
 
	AS(VK_MULTIPLY, WKC_NUM_MUL),
 
	AS(VK_SUBTRACT, WKC_NUM_MINUS),
 
	AS(VK_ADD,      WKC_NUM_PLUS),
 
	AS(VK_DECIMAL,  WKC_NUM_DECIMAL)
 
};
 

	
 
static uint MapWindowsKey(uint sym)
 
{
 
	const VkMapping *map;
 
	uint key = 0;
 

	
 
	for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
 
		if ((uint)(sym - map->vk_from) <= map->vk_count) {
 
			key = sym - map->vk_from + map->map_to;
 
			break;
 
		}
 
	}
 

	
 
	if (GetAsyncKeyState(VK_SHIFT)   < 0) key |= WKC_SHIFT;
 
	if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL;
 
	if (GetAsyncKeyState(VK_MENU)    < 0) key |= WKC_ALT;
 
	return key;
 
}
 

	
 
static bool AllocateDibSection(int w, int h);
 

	
 
static void ClientSizeChanged(int w, int h)
 
{
 
	if (_wnd.double_size) {
 
		w /= 2;
 
		h /= 2;
 
	}
 

	
 
	// allocate new dib section of the new size
 
	if (AllocateDibSection(w, h)) {
 
		// mark all palette colors dirty
 
		_pal_first_dirty = 0;
 
		_pal_last_dirty = 255;
 
		GameSizeChanged();
 

	
 
		// redraw screen
 
		if (_wnd.running) {
 
			_screen.dst_ptr = _wnd.buffer_bits;
 
			UpdateWindows();
 
		}
 
	}
 
}
 

	
 
#ifdef _DEBUG
 
// Keep this function here..
 
// It allows you to redraw the screen from within the MSVC debugger
 
int RedrawScreenDebug(void)
 
{
 
	HDC dc,dc2;
 
	static int _fooctr;
 
	HBITMAP old_bmp;
 
	HPALETTE old_palette;
 

	
 
	_screen.dst_ptr = _wnd.buffer_bits;
 
	UpdateWindows();
 

	
 
	dc = GetDC(_wnd.main_wnd);
 
	dc2 = CreateCompatibleDC(dc);
 

	
 
	old_bmp = SelectObject(dc2, _wnd.dib_sect);
 
	old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
 
	BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
 
	SelectPalette(dc, old_palette, TRUE);
 
	SelectObject(dc2, old_bmp);
 
	DeleteDC(dc2);
 
	ReleaseDC(_wnd.main_wnd, dc);
 

	
 
	return _fooctr++;
 
}
 
#endif
 

	
 
/* Windows 95 will not have a WM_MOUSELEAVE message, so define it if needed */
 
#if !defined(WM_MOUSELEAVE)
 
#define WM_MOUSELEAVE 0x02A3
 
#endif
 
#define TID_POLLMOUSE 1
 
#define MOUSE_POLL_DELAY 75
 

	
 
static void CALLBACK TrackMouseTimerProc(HWND hwnd, UINT msg, UINT event, DWORD time)
 
{
 
	RECT rc;
 
	POINT pt;
 

	
 
	/* Get the rectangle of our window and translate it to screen coordinates.
 
	 * Compare this with the current screen coordinates of the mouse and if it
 
	 * falls outside of the area or our window we have left the window. */
 
	GetClientRect(hwnd, &rc);
 
	MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)(LPRECT)&rc, 2);
 
	GetCursorPos(&pt);
 

	
 
	if (!PtInRect(&rc, pt) || (WindowFromPoint(pt) != hwnd)) {
 
		KillTimer(hwnd, event);
 
		PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L);
 
	}
 
}
 

	
 
static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
{
 
	switch (msg) {
 
		case WM_CREATE:
 
			SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
 
			break;
 

	
 
		case WM_PAINT: {
 
			PAINTSTRUCT ps;
 
			HDC dc,dc2;
 
			HBITMAP old_bmp;
 
			HPALETTE old_palette;
 

	
 
			BeginPaint(hwnd, &ps);
 
			dc = ps.hdc;
 
			dc2 = CreateCompatibleDC(dc);
 
			old_bmp = SelectObject(dc2, _wnd.dib_sect);
 
			old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
 

	
 
			if (_pal_last_dirty != -1) {
 
				UpdatePalette(dc2, _pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1);
 
				_pal_last_dirty = -1;
 
			}
 

	
 
			BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY);
 
			SelectPalette(dc, old_palette, TRUE);
 
			SelectObject(dc2, old_bmp);
 
			DeleteDC(dc2);
 
			EndPaint(hwnd, &ps);
 
			return 0;
 
		}
 

	
 
		case WM_PALETTECHANGED:
 
			if ((HWND)wParam == hwnd) return 0;
 
			/* FALLTHROUGH */
 

	
 
		case WM_QUERYNEWPALETTE: {
 
			HDC hDC = GetWindowDC(hwnd);
 
			HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE);
 
			UINT nChanged = RealizePalette(hDC);
 

	
 
			SelectPalette(hDC, hOldPalette, TRUE);
 
			ReleaseDC(hwnd, hDC);
 
			if (nChanged) InvalidateRect(hwnd, NULL, FALSE);
 
			return 0;
 
		}
 

	
 
		case WM_CLOSE:
 
			HandleExitGameRequest();
 
			return 0;
 

	
 
		case WM_DESTROY:
 
			if (_window_maximize) {
 
				_cur_resolution[0] = _bck_resolution[0];
 
				_cur_resolution[1] = _bck_resolution[1];
 
			}
 
			return 0;
 

	
 
		case WM_LBUTTONDOWN:
 
			SetCapture(hwnd);
 
			_left_button_down = true;
 
			HandleMouseEvents();
 
			return 0;
 

	
 
		case WM_LBUTTONUP:
 
			ReleaseCapture();
 
			_left_button_down = false;
 
			_left_button_clicked = false;
 
			HandleMouseEvents();
 
			return 0;
 

	
 
		case WM_RBUTTONDOWN:
 
			SetCapture(hwnd);
 
			_right_button_down = true;
 
			_right_button_clicked = true;
 
			HandleMouseEvents();
 
			return 0;
 

	
 
		case WM_RBUTTONUP:
 
			ReleaseCapture();
 
			_right_button_down = false;
 
			HandleMouseEvents();
 
			return 0;
 

	
 
		case WM_MOUSELEAVE:
 
			UndrawMouseCursor();
 
			_cursor.in_window = false;
 

	
 
			if (!_left_button_down && !_right_button_down) MyShowCursor(true);
 
			HandleMouseEvents();
 
			return 0;
 

	
 
		case WM_MOUSEMOVE: {
 
			int x = (int16)LOWORD(lParam);
 
			int y = (int16)HIWORD(lParam);
 
			POINT pt;
 

	
 
			/* If the mouse was not in the window and it has moved it means it has
 
			 * come into the window, so start drawing the mouse. Also start
 
			 * tracking the mouse for exiting the window */
 
			if (!_cursor.in_window) {
 
				_cursor.in_window = true;
 
				SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc);
 

	
 
				DrawMouseCursor();
 
			}
 

	
 
			if (_wnd.double_size) {
 
				x /= 2;
 
				y /= 2;
 
			}
 

	
 
			if (_cursor.fix_at) {
 
				int dx = x - _cursor.pos.x;
 
				int dy = y - _cursor.pos.y;
 
				if (dx != 0 || dy != 0) {
 
					_cursor.delta.x += dx;
 
					_cursor.delta.y += dy;
 

	
 
					pt.x = _cursor.pos.x;
 
					pt.y = _cursor.pos.y;
 

	
 
					if (_wnd.double_size) {
 
						pt.x *= 2;
 
						pt.y *= 2;
 
					}
 
					ClientToScreen(hwnd, &pt);
 
					SetCursorPos(pt.x, pt.y);
 
				}
 
			} else {
 
				_cursor.delta.x += x - _cursor.pos.x;
 
				_cursor.delta.y += y - _cursor.pos.y;
 
				_cursor.pos.x = x;
 
				_cursor.pos.y = y;
 
				_cursor.dirty = true;
 
			}
 
			MyShowCursor(false);
 
			HandleMouseEvents();
 
			return 0;
 
		}
 

	
 
		case WM_KEYDOWN: {
 
			// this is the rewritten ascii input function
 
			// it disables windows deadkey handling --> more linux like :D
 
			wchar_t w = 0;
 
			byte ks[256];
 
			uint scancode;
 
			uint32 pressed_key;
 

	
 
			GetKeyboardState(ks);
 
			if (ToUnicode(wParam, 0, ks, &w, 1, 0) != 1) {
 
				/* On win9x ToUnicode always fails, so fall back to ToAscii */
 
				if (ToAscii(wParam, 0, ks, &w, 0) != 1) w = 0; // no translation was possible
 
			}
 

	
 
			pressed_key = w | MapWindowsKey(wParam) << 16;
 

	
 
			scancode = GB(lParam, 16, 8);
 
			if (scancode == 41) pressed_key = w | WKC_BACKQUOTE << 16;
 

	
 
			if (GB(pressed_key, 16, 16) == ('D' | WKC_CTRL) && !_wnd.fullscreen) {
 
				_double_size ^= 1;
 
				_wnd.double_size = _double_size;
 
				ClientSizeChanged(_wnd.width, _wnd.height);
 
				MarkWholeScreenDirty();
 
			}
 
			HandleKeypress(pressed_key);
 
			break;
 
		}
 

	
 
		case WM_SYSKEYDOWN: /* user presses F10 or Alt, both activating the title-menu */
 
			switch (wParam) {
 
				case VK_RETURN:
 
				case 'F': /* Full Screen on ALT + ENTER/F */
 
					ToggleFullScreen(!_wnd.fullscreen);
 
					return 0;
 

	
 
				case VK_MENU: /* Just ALT */
 
					return 0; // do nothing
 

	
 
				case VK_F10: /* F10, ignore activation of menu */
 
					HandleKeypress(MapWindowsKey(wParam) << 16);
 
					return 0;
 

	
 
				default: /* ALT in combination with something else */
 
					HandleKeypress(MapWindowsKey(wParam) << 16);
 
					break;
 
			}
 
			break;
 

	
 
		case WM_SIZE:
 
			if (wParam != SIZE_MINIMIZED) {
 
				/* Set maximized flag when we maximize (obviously), but also when we
 
				 * switched to fullscreen from a maximized state */
 
				_window_maximize = (wParam == SIZE_MAXIMIZED || (_window_maximize && _fullscreen));
 
				if (_window_maximize) {
 
					_bck_resolution[0] = _cur_resolution[0];
 
					_bck_resolution[1] = _cur_resolution[1];
 
				}
 
				ClientSizeChanged(LOWORD(lParam), HIWORD(lParam));
 
			}
 
			return 0;
 

	
 
		case WM_SIZING: {
 
			RECT* r = (RECT*)lParam;
 
			RECT r2;
 
			int w, h;
 

	
 
			SetRect(&r2, 0, 0, 0, 0);
 
			AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
 

	
 
			w = r->right - r->left - (r2.right - r2.left);
 
			h = r->bottom - r->top - (r2.bottom - r2.top);
 
			if (_wnd.double_size) {
 
				w /= 2;
 
				h /= 2;
 
			}
 
			w = clamp(w, 64, MAX_SCREEN_WIDTH);
 
			h = clamp(h, 64, MAX_SCREEN_HEIGHT);
 
			if (_wnd.double_size) {
 
				w *= 2;
 
				h *= 2;
 
			}
 
			SetRect(&r2, 0, 0, w, h);
 

	
 
			AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE);
 
			w = r2.right - r2.left;
 
			h = r2.bottom - r2.top;
 

	
 
			switch (wParam) {
 
				case WMSZ_BOTTOM:
 
					r->bottom = r->top + h;
 
					break;
 

	
 
				case WMSZ_BOTTOMLEFT:
 
					r->bottom = r->top + h;
 
					r->left = r->right - w;
 
					break;
 

	
 
				case WMSZ_BOTTOMRIGHT:
 
					r->bottom = r->top + h;
 
					r->right = r->left + w;
 
					break;
 

	
 
				case WMSZ_LEFT:
 
					r->left = r->right - w;
 
					break;
 

	
 
				case WMSZ_RIGHT:
 
					r->right = r->left + w;
 
					break;
 

	
 
				case WMSZ_TOP:
 
					r->top = r->bottom - h;
 
					break;
 

	
 
				case WMSZ_TOPLEFT:
 
					r->top = r->bottom - h;
 
					r->left = r->right - w;
 
					break;
 

	
 
				case WMSZ_TOPRIGHT:
 
					r->top = r->bottom - h;
 
					r->right = r->left + w;
 
					break;
 
			}
 
			return TRUE;
 
		}
 

	
 
// needed for wheel
 
#if !defined(WM_MOUSEWHEEL)
 
# define WM_MOUSEWHEEL 0x020A
 
#endif  //WM_MOUSEWHEEL
 
#if !defined(GET_WHEEL_DELTA_WPARAM)
 
# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
 
#endif  //GET_WHEEL_DELTA_WPARAM
 

	
 
		case WM_MOUSEWHEEL: {
 
			int delta = GET_WHEEL_DELTA_WPARAM(wParam);
 

	
 
			if (delta < 0) {
 
				_cursor.wheel++;
 
			} else if (delta > 0) {
 
				_cursor.wheel--;
 
			}
 
			HandleMouseEvents();
 
			return 0;
 
		}
 

	
 
		case WM_ACTIVATEAPP:
 
			_wnd.has_focus = (bool)wParam;
 
			break;
 
	}
 
	return DefWindowProc(hwnd, msg, wParam, lParam);
 
}
 

	
 
static void RegisterWndClass(void)
 
{
 
	static bool registered = false;
 

	
 
	if (!registered) {
 
		HINSTANCE hinst = GetModuleHandle(NULL);
 
		WNDCLASS wnd = {
 
			0,
 
			WndProcGdi,
 
			0,
 
			0,
 
			hinst,
 
			LoadIcon(hinst, MAKEINTRESOURCE(100)),
 
			LoadCursor(NULL, IDC_ARROW),
 
			0,
 
			0,
 
			_T("OTTD")
 
		};
 

	
 
		registered = true;
 
		if (!RegisterClass(&wnd)) error("RegisterClass failed");
 
	}
 
}
 

	
 
static void MakeWindow(bool full_screen)
 
{
 
	_fullscreen = full_screen;
 

	
 
	_wnd.double_size = _double_size && !full_screen;
 

	
 
	// recreate window?
 
	if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) {
 
		DestroyWindow(_wnd.main_wnd);
 
		_wnd.main_wnd = 0;
 
	}
 

	
 
	if (full_screen) {
 
		DEVMODE settings;
 

	
 
		memset(&settings, 0, sizeof(settings));
 
		settings.dmSize = sizeof(settings);
 
		settings.dmFields =
 
			(_fullscreen_bpp != 0 ? DM_BITSPERPEL : 0) |
 
			DM_PELSWIDTH |
 
			DM_PELSHEIGHT |
 
			(_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0);
 
		settings.dmBitsPerPel = _fullscreen_bpp;
 
		settings.dmPelsWidth  = _wnd.width_org;
 
		settings.dmPelsHeight = _wnd.height_org;
 
		settings.dmDisplayFrequency = _display_hz;
 

	
 
		if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) {
 
			MakeWindow(false);
 
			return;
 
		}
 
	} else if (_wnd.fullscreen) {
 
		// restore display?
 
		ChangeDisplaySettings(NULL, 0);
 
	}
 

	
 
	{
 
		RECT r;
 
		DWORD style, showstyle;
 
		int x, y, w, h;
 

	
 
		showstyle = SW_SHOWNORMAL;
 
		_wnd.fullscreen = full_screen;
 
		if (_wnd.fullscreen) {
 
			style = WS_POPUP;
 
			SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org);
 
		} else {
 
			style = WS_OVERLAPPEDWINDOW;
 
			/* On window creation, check if we were in maximize mode before */
 
			if (_window_maximize) showstyle = SW_SHOWMAXIMIZED;
 
			SetRect(&r, 0, 0, _wnd.width, _wnd.height);
 
		}
 

	
 
		AdjustWindowRect(&r, style, FALSE);
 
		w = r.right - r.left;
 
		h = r.bottom - r.top;
 
		x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
 
		y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
 

	
 
		if (_wnd.main_wnd) {
 
			ShowWindow(_wnd.main_wnd, SW_SHOWNORMAL); // remove maximize-flag
 
			SetWindowPos(_wnd.main_wnd, 0, x, y, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
 
		} else {
 
			extern const char _openttd_revision[];
 
			TCHAR Windowtitle[50];
 

	
 
			_sntprintf(Windowtitle, sizeof(Windowtitle), _T("OpenTTD %s"), MB_TO_WIDE(_openttd_revision));
 

	
 
			_wnd.main_wnd = CreateWindow(_T("OTTD"), Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0);
 
			if (_wnd.main_wnd == NULL) error("CreateWindow failed");
 
			ShowWindow(_wnd.main_wnd, showstyle);
 
		}
 
	}
 
	GameSizeChanged(); // invalidate all windows, force redraw
 
}
 

	
 
static bool AllocateDibSection(int w, int h)
 
{
 
	BITMAPINFO *bi;
 
	HDC dc;
 

	
 
	w = clamp(w, 64, MAX_SCREEN_WIDTH);
 
	h = clamp(h, 64, MAX_SCREEN_HEIGHT);
 

	
 
	if (w == _screen.width && h == _screen.height)
 
		return false;
 

	
 
	_screen.width = w;
 
	_screen.pitch = ALIGN(w, 4);
 
	_screen.height = h;
 

	
 
	if (_wnd.alloced_bits) {
 
		free(_wnd.alloced_bits);
 
		_wnd.alloced_bits = NULL;
 
	}
 

	
 
	bi = alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
 
	memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256);
 
	bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 

	
 
	if (_wnd.double_size) {
 
		w = ALIGN(w, 4);
 
		_wnd.alloced_bits = _wnd.buffer_bits = malloc(w * h);
 
		w *= 2;
 
		h *= 2;
 
	}
 

	
 
	bi->bmiHeader.biWidth = _wnd.width = w;
 
	bi->bmiHeader.biHeight = -(_wnd.height = h);
 

	
 
	bi->bmiHeader.biPlanes = 1;
 
	bi->bmiHeader.biBitCount = 8;
 
	bi->bmiHeader.biCompression = BI_RGB;
 

	
 
	if (_wnd.dib_sect) DeleteObject(_wnd.dib_sect);
 

	
 
	dc = GetDC(0);
 
	_wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (VOID**)&_wnd.bitmap_bits, NULL, 0);
 
	if (_wnd.dib_sect == NULL) error("CreateDIBSection failed");
 
	ReleaseDC(0, dc);
 

	
 
	if (!_wnd.double_size) _wnd.buffer_bits = _wnd.bitmap_bits;
 

	
 
	return true;
 
}
 

	
 
static const uint16 default_resolutions[][2] = {
 
	{  640,  480 },
 
	{  800,  600 },
 
	{ 1024,  768 },
 
	{ 1152,  864 },
 
	{ 1280,  800 },
 
	{ 1280,  960 },
 
	{ 1280, 1024 },
 
	{ 1400, 1050 },
 
	{ 1600, 1200 },
 
	{ 1680, 1050 },
 
	{ 1920, 1200 }
 
};
 

	
 
static void FindResolutions(void)
 
{
 
	uint n = 0;
 
	uint i;
 
	DEVMODEA dm;
 

	
 
	/* XXX - EnumDisplaySettingsW crashes with unicows.dll on Windows95
 
	 * Doesn't really matter since we don't pass a string anyways, but still
 
	 * a letdown */
 
	for (i = 0; EnumDisplaySettingsA(NULL, i, &dm) != 0; i++) {
 
		if (dm.dmBitsPerPel == 8 && IS_INT_INSIDE(dm.dmPelsWidth, 640, MAX_SCREEN_WIDTH + 1) &&
 
				IS_INT_INSIDE(dm.dmPelsHeight, 480, MAX_SCREEN_HEIGHT + 1)) {
 
			uint j;
 

	
 
			for (j = 0; j < n; j++) {
 
				if (_resolutions[j][0] == dm.dmPelsWidth && _resolutions[j][1] == dm.dmPelsHeight) break;
 
			}
 

	
 
			/* In the previous loop we have checked already existing/added resolutions if
 
			 * they are the same as the new ones. If this is not the case (j == n); we have
 
			 * looped all and found none, add the new one to the list. If we have reached the
 
			 * maximum amount of resolutions, then quit querying the display */
 
			if (j == n) {
 
				_resolutions[j][0] = dm.dmPelsWidth;
 
				_resolutions[j][1] = dm.dmPelsHeight;
 
				if (++n == lengthof(_resolutions)) break;
 
			}
 
		}
 
	}
 

	
 
	/* We have found no resolutions, show the default list */
 
	if (n == 0) {
 
		memcpy(_resolutions, default_resolutions, sizeof(default_resolutions));
 
		n = lengthof(default_resolutions);
 
	}
 

	
 
	_num_resolutions = n;
 
	SortResolutions(_num_resolutions);
 
}
 

	
 

	
 
static const char *Win32GdiStart(const char * const *parm)
 
{
 
	memset(&_wnd, 0, sizeof(_wnd));
 

	
 
	RegisterWndClass();
 

	
 
	MakePalette();
 

	
 
	FindResolutions();
 

	
 
	// fullscreen uses those
 
	_wnd.width_org = _cur_resolution[0];
 
	_wnd.height_org = _cur_resolution[1];
 

	
 
	AllocateDibSection(_cur_resolution[0], _cur_resolution[1]);
 
	MarkWholeScreenDirty();
 

	
 
	MakeWindow(_fullscreen);
 

	
 
	return NULL;
 
}
 

	
 
static void Win32GdiStop(void)
 
{
 
	DeleteObject(_wnd.gdi_palette);
 
	DeleteObject(_wnd.dib_sect);
 
	DestroyWindow(_wnd.main_wnd);
 

	
 
	if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0);
 
	if (_double_size) {
 
		_cur_resolution[0] *= 2;
 
		_cur_resolution[1] *= 2;
 
	}
 

	
 
	MyShowCursor(true);
 
}
 

	
 
// simple upscaler by 2
 
static void filter(int left, int top, int width, int height)
 
{
 
	uint p = _screen.pitch;
 
	const Pixel *s = _wnd.buffer_bits + top * p + left;
 
	Pixel *d = _wnd.bitmap_bits + top * p * 4 + left * 2;
 

	
 
	for (; height > 0; height--) {
 
		int i;
 

	
 
		for (i = 0; i != width; i++) {
 
			d[i * 2] = d[i * 2 + 1] = d[i * 2 + p * 2] = d[i * 2 + 1 + p * 2] = s[i];
 
		}
 
		s += p;
 
		d += p * 4;
 
	}
 
}
 

	
 
static void Win32GdiMakeDirty(int left, int top, int width, int height)
 
{
 
	RECT r = { left, top, left + width, top + height };
 

	
 
	if (_wnd.double_size) {
 
		filter(left, top, width, height);
 
		r.left *= 2;
 
		r.top *= 2;
 
		r.right *= 2;
 
		r.bottom *= 2;
 
	}
 
	InvalidateRect(_wnd.main_wnd, &r, FALSE);
 
}
 

	
 
static void CheckPaletteAnim(void)
 
{
 
	if (_pal_last_dirty == -1)
 
		return;
 
	InvalidateRect(_wnd.main_wnd, NULL, FALSE);
 
}
 

	
 
static void Win32GdiMainLoop(void)
 
{
 
	MSG mesg;
 
	uint32 cur_ticks = GetTickCount();
 
	uint32 next_tick = cur_ticks + 30;
 

	
 
	_wnd.running = true;
 

	
 
	for (;;) {
 
		uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
 

	
 
		while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) {
 
			InteractiveRandom(); // randomness
 
			DispatchMessage(&mesg);
 
		}
 
		if (_exit_game) return;
 

	
 
#if defined(_DEBUG)
 
		if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 &&
 
#else
 
		/* Speed up using TAB, but disable for ALT+TAB of course */
 
		if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0 &&
 
#endif
 
			  !_networking && _game_mode != GM_MENU) {
 
			_fast_forward |= 2;
 
		} else if (_fast_forward & 2) {
 
			_fast_forward = 0;
 
		}
 

	
 
		cur_ticks = GetTickCount();
 
		if (cur_ticks >= next_tick || (_fast_forward && !_pause) || cur_ticks < prev_cur_ticks) {
 
			next_tick = cur_ticks + 30;
 
			_ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0;
 
			_shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0;
 
#ifdef _DEBUG
 
			_dbg_screen_rect = _wnd.has_focus && GetAsyncKeyState(VK_CAPITAL)<0;
 
#endif
 

	
 
			// determine which directional keys are down
 
			if (_wnd.has_focus) {
 
				_dirkeys =
 
					(GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
 
					(GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
 
					(GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
 
					(GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
 
			} else {
 
				_dirkeys = 0;
 
			}
 

	
 
			GameLoop();
 
			_cursor.delta.x = _cursor.delta.y = 0;
 

	
 
			if (_force_full_redraw) MarkWholeScreenDirty();
 

	
 
			GdiFlush();
 
			_screen.dst_ptr = _wnd.buffer_bits;
 
			UpdateWindows();
 
			CheckPaletteAnim();
 
		} else {
 
			Sleep(1);
 
			GdiFlush();
 
			_screen.dst_ptr = _wnd.buffer_bits;
 
			DrawTextMessage();
 
			DrawMouseCursor();
 
		}
 
	}
 
}
 

	
 
static bool Win32GdiChangeRes(int w, int h)
 
{
 
	_wnd.width = _wnd.width_org = w;
 
	_wnd.height = _wnd.height_org = h;
 

	
 
	MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching
 

	
 
	return true;
 
}
 

	
 
static void Win32GdiFullScreen(bool full_screen)
 
{
 
	MakeWindow(full_screen);
 
}
 

	
 
const HalVideoDriver _win32_video_driver = {
 
	Win32GdiStart,
 
	Win32GdiStop,
 
	Win32GdiMakeDirty,
 
	Win32GdiMainLoop,
 
	Win32GdiChangeRes,
 
	Win32GdiFullScreen,
 
};
src/viewport.c
Show inline comments
 
deleted file
src/viewport.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "gui.h"
 
#include "spritecache.h"
 
#include "strings.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "map.h"
 
#include "viewport.h"
 
#include "window.h"
 
#include "vehicle.h"
 
#include "station.h"
 
#include "gfx.h"
 
#include "town.h"
 
#include "signs.h"
 
#include "waypoint.h"
 
#include "variables.h"
 
#include "train.h"
 

	
 
#define VIEWPORT_DRAW_MEM (65536 * 2)
 

	
 
/* viewport.c */
 
// XXX - maximum viewports is maximum windows - 2 (main toolbar + status bar)
 
static ViewPort _viewports[25 - 2];
 
static uint32 _active_viewports;    ///< bitmasked variable where each bit signifies if a viewport is in use or not
 
assert_compile(lengthof(_viewports) < sizeof(_active_viewports) * 8);
 

	
 
static bool _added_tile_sprite;
 
static bool _offset_ground_sprites;
 

	
 
/* The in-game coordiante system looks like this *
 
 *                                               *
 
 *                    ^ Z                        *
 
 *                    |                          *
 
 *                    |                          *
 
 *                    |                          *
 
 *                    |                          *
 
 *                 /     \                       *
 
 *              /           \                    *
 
 *           /                 \                 *
 
 *        /                       \              *
 
 *   X <                             > Y         *
 
 */
 

	
 
typedef struct StringSpriteToDraw {
 
	uint16 string;
 
	uint16 color;
 
	struct StringSpriteToDraw *next;
 
	int32 x;
 
	int32 y;
 
	uint32 params[2];
 
	uint16 width;
 
} StringSpriteToDraw;
 

	
 
typedef struct TileSpriteToDraw {
 
	uint32 image;
 
	struct TileSpriteToDraw *next;
 
	int32 x;
 
	int32 y;
 
	byte z;
 
} TileSpriteToDraw;
 

	
 
typedef struct ChildScreenSpriteToDraw {
 
	uint32 image;
 
	int32 x;
 
	int32 y;
 
	struct ChildScreenSpriteToDraw *next;
 
} ChildScreenSpriteToDraw;
 

	
 
typedef struct ParentSpriteToDraw {
 
	uint32 image;
 
	int32 left;
 
	int32 top;
 
	int32 right;
 
	int32 bottom;
 
	int32 xmin;
 
	int32 ymin;
 
	int32 xmax;
 
	int32 ymax;
 
	ChildScreenSpriteToDraw *child;
 
	byte unk16;
 
	byte zmin;
 
	byte zmax;
 
} ParentSpriteToDraw;
 

	
 
// Quick hack to know how much memory to reserve when allocating from the spritelist
 
// to prevent a buffer overflow.
 
#define LARGEST_SPRITELIST_STRUCT ParentSpriteToDraw
 

	
 
typedef struct ViewportDrawer {
 
	DrawPixelInfo dpi;
 

	
 
	byte *spritelist_mem;
 
	const byte *eof_spritelist_mem;
 

	
 
	StringSpriteToDraw **last_string, *first_string;
 
	TileSpriteToDraw **last_tile, *first_tile;
 

	
 
	ChildScreenSpriteToDraw **last_child;
 

	
 
	ParentSpriteToDraw **parent_list;
 
	ParentSpriteToDraw * const *eof_parent_list;
 

	
 
	byte combine_sprites;
 

	
 
	int offs_x, offs_y; // used when drawing ground sprites relative
 
} ViewportDrawer;
 

	
 
static ViewportDrawer *_cur_vd;
 

	
 
TileHighlightData _thd;
 
static TileInfo *_cur_ti;
 

	
 
extern void SmallMapCenterOnCurrentPos(Window *w);
 

	
 
static Point MapXYZToViewport(const ViewPort *vp, uint x, uint y, uint z)
 
{
 
	Point p = RemapCoords(x, y, z);
 
	p.x -= vp->virtual_width / 2;
 
	p.y -= vp->virtual_height / 2;
 
	return p;
 
}
 

	
 
void InitViewports(void) {
 
	memset(_viewports, 0, sizeof(_viewports));
 
	_active_viewports = 0;
 
}
 

	
 
void DeleteWindowViewport(Window *w)
 
{
 
	CLRBIT(_active_viewports, w->viewport - _viewports);
 
	w->viewport->width = 0;
 
	w->viewport = NULL;
 
}
 

	
 
void AssignWindowViewport(Window *w, int x, int y,
 
	int width, int height, uint32 follow_flags, byte zoom)
 
{
 
	ViewPort *vp;
 
	Point pt;
 
	uint32 bit;
 

	
 
	for (vp = _viewports, bit = 0; ; vp++, bit++) {
 
		assert(vp != endof(_viewports));
 
		if (vp->width == 0) break;
 
	}
 
	SETBIT(_active_viewports, bit);
 

	
 
	vp->left = x + w->left;
 
	vp->top = y + w->top;
 
	vp->width = width;
 
	vp->height = height;
 

	
 
	vp->zoom = zoom;
 

	
 
	vp->virtual_width = width << zoom;
 
	vp->virtual_height = height << zoom;
 

	
 
	if (follow_flags & 0x80000000) {
 
		const Vehicle *veh;
 

	
 
		WP(w, vp_d).follow_vehicle = (VehicleID)(follow_flags & 0xFFFF);
 
		veh = GetVehicle(WP(w, vp_d).follow_vehicle);
 
		pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
 
	} else {
 
		uint x = TileX(follow_flags) * TILE_SIZE;
 
		uint y = TileY(follow_flags) * TILE_SIZE;
 

	
 
		WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
 
		pt = MapXYZToViewport(vp, x, y, GetSlopeZ(x, y));
 
	}
 

	
 
	WP(w, vp_d).scrollpos_x = pt.x;
 
	WP(w, vp_d).scrollpos_y = pt.y;
 
	w->viewport = vp;
 
	vp->virtual_left = 0;//pt.x;
 
	vp->virtual_top = 0;//pt.y;
 
}
 

	
 
static Point _vp_move_offs;
 

	
 
static void DoSetViewportPosition(Window* const *wz, int left, int top, int width, int height)
 
{
 

	
 
	for (; wz != _last_z_window; wz++) {
 
		const Window *w = *wz;
 

	
 
		if (left + width > w->left &&
 
				w->left + w->width > left &&
 
				top + height > w->top &&
 
				w->top + w->height > top) {
 

	
 
			if (left < w->left) {
 
				DoSetViewportPosition(wz, left, top, w->left - left, height);
 
				DoSetViewportPosition(wz, left + (w->left - left), top, width - (w->left - left), height);
 
				return;
 
			}
 

	
 
			if (left + width > w->left + w->width) {
 
				DoSetViewportPosition(wz, left, top, (w->left + w->width - left), height);
 
				DoSetViewportPosition(wz, left + (w->left + w->width - left), top, width - (w->left + w->width - left) , height);
 
				return;
 
			}
 

	
 
			if (top < w->top) {
 
				DoSetViewportPosition(wz, left, top, width, (w->top - top));
 
				DoSetViewportPosition(wz, left, top + (w->top - top), width, height - (w->top - top));
 
				return;
 
			}
 

	
 
			if (top + height > w->top + w->height) {
 
				DoSetViewportPosition(wz, left, top, width, (w->top + w->height - top));
 
				DoSetViewportPosition(wz, left, top + (w->top + w->height - top), width , height - (w->top + w->height - top));
 
				return;
 
			}
 

	
 
			return;
 
		}
 
	}
 

	
 
	{
 
		int xo = _vp_move_offs.x;
 
		int yo = _vp_move_offs.y;
 

	
 
		if (abs(xo) >= width || abs(yo) >= height) {
 
			/* fully_outside */
 
			RedrawScreenRect(left, top, left + width, top + height);
 
			return;
 
		}
 

	
 
		GfxScroll(left, top, width, height, xo, yo);
 

	
 
		if (xo > 0) {
 
			RedrawScreenRect(left, top, xo + left, top + height);
 
			left += xo;
 
			width -= xo;
 
		} else if (xo < 0) {
 
			RedrawScreenRect(left+width+xo, top, left+width, top + height);
 
			width += xo;
 
		}
 

	
 
		if (yo > 0) {
 
			RedrawScreenRect(left, top, width+left, top + yo);
 
		} else if (yo < 0) {
 
			RedrawScreenRect(left, top + height + yo, width+left, top + height);
 
		}
 
	}
 
}
 

	
 
static void SetViewportPosition(Window *w, int x, int y)
 
{
 
	ViewPort *vp = w->viewport;
 
	int old_left = vp->virtual_left;
 
	int old_top = vp->virtual_top;
 
	int i;
 
	int left, top, width, height;
 

	
 
	vp->virtual_left = x;
 
	vp->virtual_top = y;
 

	
 
	old_left >>= vp->zoom;
 
	old_top >>= vp->zoom;
 
	x >>= vp->zoom;
 
	y >>= vp->zoom;
 

	
 
	old_left -= x;
 
	old_top -= y;
 

	
 
	if (old_top == 0 && old_left == 0) return;
 

	
 
	_vp_move_offs.x = old_left;
 
	_vp_move_offs.y = old_top;
 

	
 
	left = vp->left;
 
	top = vp->top;
 
	width = vp->width;
 
	height = vp->height;
 

	
 
	if (left < 0) {
 
		width += left;
 
		left = 0;
 
	}
 

	
 
	i = left + width - _screen.width;
 
	if (i >= 0) width -= i;
 

	
 
	if (width > 0) {
 
		if (top < 0) {
 
			height += top;
 
			top = 0;
 
		}
 

	
 
		i = top + height - _screen.height;
 
		if (i >= 0) height -= i;
 

	
 
		if (height > 0) DoSetViewportPosition(FindWindowZPosition(w) + 1, left, top, width, height);
 
	}
 
}
 

	
 

	
 
ViewPort *IsPtInWindowViewport(const Window *w, int x, int y)
 
{
 
	ViewPort *vp = w->viewport;
 

	
 
	if (vp != NULL &&
 
	    IS_INT_INSIDE(x, vp->left, vp->left + vp->width) &&
 
			IS_INT_INSIDE(y, vp->top, vp->top + vp->height))
 
		return vp;
 

	
 
	return NULL;
 
}
 

	
 
static Point TranslateXYToTileCoord(const ViewPort *vp, int x, int y)
 
{
 
	Point pt;
 
	int a,b;
 
	uint z;
 

	
 
	if ( (uint)(x -= vp->left) >= (uint)vp->width ||
 
				(uint)(y -= vp->top) >= (uint)vp->height) {
 
				Point pt = {-1, -1};
 
				return pt;
 
	}
 

	
 
	x = ((x << vp->zoom) + vp->virtual_left) >> 2;
 
	y = ((y << vp->zoom) + vp->virtual_top) >> 1;
 

	
 
	a = y-x;
 
	b = y+x;
 

	
 
	/* we need to move variables in to the valid range, as the
 
	 * GetTileZoomCenterWindow() function can call here with invalid x and/or y,
 
	 * when the user tries to zoom out along the sides of the map */
 
	a = clamp(a, 0, (int)(MapMaxX() * TILE_SIZE) - 1);
 
	b = clamp(b, 0, (int)(MapMaxY() * TILE_SIZE) - 1);
 

	
 
	z = GetSlopeZ(a,     b    ) / 2;
 
	z = GetSlopeZ(a + z, b + z) / 2;
 
	z = GetSlopeZ(a + z, b + z) / 2;
 
	z = GetSlopeZ(a + z, b + z) / 2;
 
	z = GetSlopeZ(a + z, b + z) / 2;
 

	
 
	pt.x = a + z;
 
	pt.y = b + z;
 

	
 
	return pt;
 
}
 

	
 
/* When used for zooming, check area below current coordinates (x,y)
 
 * and return the tile of the zoomed out/in position (zoom_x, zoom_y)
 
 * when you just want the tile, make x = zoom_x and y = zoom_y */
 
static Point GetTileFromScreenXY(int x, int y, int zoom_x, int zoom_y)
 
{
 
	Window *w;
 
	ViewPort *vp;
 
	Point pt;
 

	
 
	if ( (w = FindWindowFromPt(x, y)) != NULL &&
 
			 (vp = IsPtInWindowViewport(w, x, y)) != NULL)
 
				return TranslateXYToTileCoord(vp, zoom_x, zoom_y);
 

	
 
	pt.y = pt.x = -1;
 
	return pt;
 
}
 

	
 
Point GetTileBelowCursor(void)
 
{
 
	return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, _cursor.pos.x, _cursor.pos.y);
 
}
 

	
 

	
 
Point GetTileZoomCenterWindow(bool in, Window * w)
 
{
 
	int x, y;
 
	ViewPort * vp;
 

	
 
	vp = w->viewport;
 

	
 
	if (in) {
 
		x = ((_cursor.pos.x - vp->left) >> 1) + (vp->width >> 2);
 
		y = ((_cursor.pos.y - vp->top) >> 1) + (vp->height >> 2);
 
	} else {
 
		x = vp->width - (_cursor.pos.x - vp->left);
 
		y = vp->height - (_cursor.pos.y - vp->top);
 
	}
 
	/* Get the tile below the cursor and center on the zoomed-out center */
 
	return GetTileFromScreenXY(_cursor.pos.x, _cursor.pos.y, x + vp->left, y + vp->top);
 
}
 

	
 
/** Update the status of the zoom-buttons according to the zoom-level
 
 * of the viewport. This will update their status and invalidate accordingly
 
 * @param window pointer to the window that has the zoom buttons
 
 * @param vp pointer to the viewport whose zoom-level the buttons represent
 
 * @param widget_zoom_in widget index for window with zoom-in button
 
 * @param widget_zoom_out widget index for window with zoom-out button */
 
void HandleZoomMessage(Window *w, const ViewPort *vp, byte widget_zoom_in, byte widget_zoom_out)
 
{
 
	SetWindowWidgetDisabledState(w, widget_zoom_in, vp->zoom == 0);
 
	InvalidateWidget(w, widget_zoom_in);
 

	
 
	SetWindowWidgetDisabledState(w, widget_zoom_out, vp->zoom == 2);
 
	InvalidateWidget(w, widget_zoom_out);
 
}
 

	
 
void DrawGroundSpriteAt(uint32 image, int32 x, int32 y, byte z)
 
{
 
	ViewportDrawer *vd = _cur_vd;
 
	TileSpriteToDraw *ts;
 

	
 
	assert((image & SPRITE_MASK) < MAX_SPRITES);
 

	
 
	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
 
		DEBUG(sprite, 0, "Out of sprite memory");
 
		return;
 
	}
 
	ts = (TileSpriteToDraw*)vd->spritelist_mem;
 

	
 
	vd->spritelist_mem += sizeof(TileSpriteToDraw);
 

	
 
	ts->image = image;
 
	ts->next = NULL;
 
	ts->x = x;
 
	ts->y = y;
 
	ts->z = z;
 
	*vd->last_tile = ts;
 
	vd->last_tile = &ts->next;
 
}
 

	
 
void DrawGroundSprite(uint32 image)
 
{
 
	if (_offset_ground_sprites) {
 
		// offset ground sprite because of foundation?
 
		AddChildSpriteScreen(image, _cur_vd->offs_x, _cur_vd->offs_y);
 
	} else {
 
		_added_tile_sprite = true;
 
		DrawGroundSpriteAt(image, _cur_ti->x, _cur_ti->y, _cur_ti->z);
 
	}
 
}
 

	
 

	
 
void OffsetGroundSprite(int x, int y)
 
{
 
	_cur_vd->offs_x = x;
 
	_cur_vd->offs_y = y;
 
	_offset_ground_sprites = true;
 
}
 

	
 
static void AddCombinedSprite(uint32 image, int x, int y, byte z)
 
{
 
	const ViewportDrawer *vd = _cur_vd;
 
	Point pt = RemapCoords(x, y, z);
 
	const Sprite* spr = GetSprite(image & SPRITE_MASK);
 

	
 
	if (pt.x + spr->x_offs >= vd->dpi.left + vd->dpi.width ||
 
			pt.x + spr->x_offs + spr->width <= vd->dpi.left ||
 
			pt.y + spr->y_offs >= vd->dpi.top + vd->dpi.height ||
 
			pt.y + spr->y_offs + spr->height <= vd->dpi.top)
 
		return;
 

	
 
	AddChildSpriteScreen(image, pt.x - vd->parent_list[-1]->left, pt.y - vd->parent_list[-1]->top);
 
}
 

	
 

	
 
void AddSortableSpriteToDraw(uint32 image, int x, int y, int w, int h, byte dz, byte z)
 
{
 
	ViewportDrawer *vd = _cur_vd;
 
	ParentSpriteToDraw *ps;
 
	const Sprite *spr;
 
	Point pt;
 

	
 
	assert((image & SPRITE_MASK) < MAX_SPRITES);
 

	
 
	if (vd->combine_sprites == 2) {
 
		AddCombinedSprite(image, x, y, z);
 
		return;
 
	}
 

	
 
	vd->last_child = NULL;
 

	
 
	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
 
		DEBUG(sprite, 0, "Out of sprite memory");
 
		return;
 
	}
 
	ps = (ParentSpriteToDraw*)vd->spritelist_mem;
 

	
 
	if (vd->parent_list >= vd->eof_parent_list) {
 
		// This can happen rarely, mostly when you zoom out completely
 
		//  and have a lot of stuff that moves (and is added to the
 
		//  sort-list, this function). To solve it, increase
 
		//  parent_list somewhere below to a higher number.
 
		// This can not really hurt you, it just gives some black
 
		//  spots on the screen ;)
 
		DEBUG(sprite, 0, "Out of sprite memory (parent_list)");
 
		return;
 
	}
 

	
 
	pt = RemapCoords(x, y, z);
 
	spr = GetSprite(image & SPRITE_MASK);
 
	if ((ps->left   = (pt.x += spr->x_offs)) >= vd->dpi.left + vd->dpi.width ||
 
			(ps->right  = (pt.x +  spr->width )) <= vd->dpi.left ||
 
			(ps->top    = (pt.y += spr->y_offs)) >= vd->dpi.top + vd->dpi.height ||
 
			(ps->bottom = (pt.y +  spr->height)) <= vd->dpi.top) {
 
		return;
 
	}
 

	
 
	vd->spritelist_mem += sizeof(ParentSpriteToDraw);
 

	
 
	ps->image = image;
 
	ps->xmin = x;
 
	ps->xmax = x + w - 1;
 

	
 
	ps->ymin = y;
 
	ps->ymax = y + h - 1;
 

	
 
	ps->zmin = z;
 
	ps->zmax = z + dz - 1;
 

	
 
	ps->unk16 = 0;
 
	ps->child = NULL;
 
	vd->last_child = &ps->child;
 

	
 
	*vd->parent_list++ = ps;
 

	
 
	if (vd->combine_sprites == 1) vd->combine_sprites = 2;
 
}
 

	
 
void StartSpriteCombine(void)
 
{
 
	_cur_vd->combine_sprites = 1;
 
}
 

	
 
void EndSpriteCombine(void)
 
{
 
	_cur_vd->combine_sprites = 0;
 
}
 

	
 
void AddChildSpriteScreen(uint32 image, int x, int y)
 
{
 
	ViewportDrawer *vd = _cur_vd;
 
	ChildScreenSpriteToDraw *cs;
 

	
 
	assert((image & SPRITE_MASK) < MAX_SPRITES);
 

	
 
	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
 
		DEBUG(sprite, 0, "Out of sprite memory");
 
		return;
 
	}
 
	cs = (ChildScreenSpriteToDraw*)vd->spritelist_mem;
 

	
 
	if (vd->last_child == NULL) return;
 

	
 
	vd->spritelist_mem += sizeof(ChildScreenSpriteToDraw);
 

	
 
	*vd->last_child = cs;
 
	vd->last_child = &cs->next;
 

	
 
	cs->image = image;
 
	cs->x = x;
 
	cs->y = y;
 
	cs->next = NULL;
 
}
 

	
 
/* Returns a StringSpriteToDraw */
 
void *AddStringToDraw(int x, int y, StringID string, uint32 params_1, uint32 params_2)
 
{
 
	ViewportDrawer *vd = _cur_vd;
 
	StringSpriteToDraw *ss;
 

	
 
	if (vd->spritelist_mem >= vd->eof_spritelist_mem) {
 
		DEBUG(sprite, 0, "Out of sprite memory");
 
		return NULL;
 
	}
 
	ss = (StringSpriteToDraw*)vd->spritelist_mem;
 

	
 
	vd->spritelist_mem += sizeof(StringSpriteToDraw);
 

	
 
	ss->string = string;
 
	ss->next = NULL;
 
	ss->x = x;
 
	ss->y = y;
 
	ss->params[0] = params_1;
 
	ss->params[1] = params_2;
 
	ss->width = 0;
 

	
 
	*vd->last_string = ss;
 
	vd->last_string = &ss->next;
 

	
 
	return ss;
 
}
 

	
 

	
 
static void DrawSelectionSprite(uint32 image, const TileInfo *ti)
 
{
 
	if (_added_tile_sprite && !(_thd.drawstyle & HT_LINE)) { // draw on real ground
 
		DrawGroundSpriteAt(image, ti->x, ti->y, ti->z + 7);
 
	} else { // draw on top of foundation
 
		AddSortableSpriteToDraw(image, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7);
 
	}
 
}
 

	
 
static bool IsPartOfAutoLine(int px, int py)
 
{
 
	px -= _thd.selstart.x;
 
	py -= _thd.selstart.y;
 

	
 
	switch (_thd.drawstyle) {
 
	case HT_LINE | HT_DIR_X:  return py == 0; // x direction
 
	case HT_LINE | HT_DIR_Y:  return px == 0; // y direction
 
	case HT_LINE | HT_DIR_HU: return px == -py || px == -py - 16; // horizontal upper
 
	case HT_LINE | HT_DIR_HL: return px == -py || px == -py + 16; // horizontal lower
 
	case HT_LINE | HT_DIR_VL: return px == py || px == py + 16; // vertival left
 
	case HT_LINE | HT_DIR_VR: return px == py || px == py - 16; // vertical right
 
	default:
 
		NOT_REACHED();
 
	}
 

	
 
	/* useless, but avoids compiler warning this way */
 
	return 0;
 
}
 

	
 
// [direction][side]
 
static const int _AutorailType[6][2] = {
 
	{ HT_DIR_X,  HT_DIR_X },
 
	{ HT_DIR_Y,  HT_DIR_Y },
 
	{ HT_DIR_HU, HT_DIR_HL },
 
	{ HT_DIR_HL, HT_DIR_HU },
 
	{ HT_DIR_VL, HT_DIR_VR },
 
	{ HT_DIR_VR, HT_DIR_VL }
 
};
 

	
 
#include "table/autorail.h"
 

	
 
static void DrawTileSelection(const TileInfo *ti)
 
{
 
	uint32 image;
 

	
 
	// Draw a red error square?
 
	if (_thd.redsq != 0 && _thd.redsq == ti->tile) {
 
		DrawSelectionSprite(PALETTE_TILE_RED_PULSATING | (SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh]), ti);
 
		return;
 
	}
 

	
 
	// no selection active?
 
	if (_thd.drawstyle == 0) return;
 

	
 
	// Inside the inner area?
 
	if (IS_INSIDE_1D(ti->x, _thd.pos.x, _thd.size.x) &&
 
			IS_INSIDE_1D(ti->y, _thd.pos.y, _thd.size.y)) {
 
		if (_thd.drawstyle & HT_RECT) {
 
			image = SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh];
 
			if (_thd.make_square_red) image |= PALETTE_SEL_TILE_RED;
 
			DrawSelectionSprite(image, ti);
 
		} else if (_thd.drawstyle & HT_POINT) {
 
			// Figure out the Z coordinate for the single dot.
 
			byte z = ti->z;
 
			if (ti->tileh & SLOPE_N) {
 
				z += TILE_HEIGHT;
 
				if (ti->tileh == SLOPE_STEEP_N) z += TILE_HEIGHT;
 
			}
 
			DrawGroundSpriteAt(_cur_dpi->zoom != 2 ? SPR_DOT : SPR_DOT_SMALL, ti->x, ti->y, z);
 
		} else if (_thd.drawstyle & HT_RAIL /*&& _thd.place_mode == VHM_RAIL*/) {
 
			// autorail highlight piece under cursor
 
			uint type = _thd.drawstyle & 0xF;
 
			assert(type <= 5);
 
			image = SPR_AUTORAIL_BASE + _AutorailTilehSprite[ti->tileh][_AutorailType[type][0]];
 

	
 
			if (_thd.make_square_red) image |= PALETTE_SEL_TILE_RED;
 
			DrawSelectionSprite(image, ti);
 

	
 
		} else if (IsPartOfAutoLine(ti->x, ti->y)) {
 
			// autorail highlighting long line
 
			int dir = _thd.drawstyle & ~0xF0;
 
			uint side;
 

	
 
			if (dir < 2) {
 
				side = 0;
 
			} else {
 
				TileIndex start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
 
				int diffx = myabs(TileX(start) - TileX(ti->tile));
 
				int diffy = myabs(TileY(start) - TileY(ti->tile));
 
				side = myabs(diffx - diffy);
 
			}
 

	
 
			image = SPR_AUTORAIL_BASE + _AutorailTilehSprite[ti->tileh][_AutorailType[dir][side]];
 

	
 
			if (_thd.make_square_red) image |= PALETTE_SEL_TILE_RED;
 
			DrawSelectionSprite(image, ti);
 
		}
 
		return;
 
	}
 

	
 
	// Check if it's inside the outer area?
 
	if (_thd.outersize.x &&
 
			_thd.size.x < _thd.size.x + _thd.outersize.x &&
 
			IS_INSIDE_1D(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
 
			IS_INSIDE_1D(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
 
		// Draw a blue rect.
 
		DrawSelectionSprite(PALETTE_SEL_TILE_BLUE | (SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh]), ti);
 
		return;
 
	}
 
}
 

	
 
static void ViewportAddLandscape(void)
 
{
 
	ViewportDrawer *vd = _cur_vd;
 
	int x, y, width, height;
 
	TileInfo ti;
 
	bool direction;
 

	
 
	_cur_ti = &ti;
 

	
 
	// Transform into tile coordinates and round to closest full tile
 
	x = ((vd->dpi.top >> 1) - (vd->dpi.left >> 2)) & ~0xF;
 
	y = ((vd->dpi.top >> 1) + (vd->dpi.left >> 2) - 0x10) & ~0xF;
 

	
 
	// determine size of area
 
	{
 
		Point pt = RemapCoords(x, y, 241);
 
		width = (vd->dpi.left + vd->dpi.width - pt.x + 95) >> 6;
 
		height = (vd->dpi.top + vd->dpi.height - pt.y) >> 5 << 1;
 
	}
 

	
 
	assert(width > 0);
 
	assert(height > 0);
 

	
 
	direction = false;
 

	
 
	do {
 
		int width_cur = width;
 
		int x_cur = x;
 
		int y_cur = y;
 

	
 
		do {
 
			TileType tt;
 

	
 
			ti.x = x_cur;
 
			ti.y = y_cur;
 
			if (0 <= x_cur && x_cur < (int)MapMaxX() * TILE_SIZE &&
 
					0 <= y_cur && y_cur < (int)MapMaxY() * TILE_SIZE) {
 
				TileIndex tile = TileVirtXY(x_cur, y_cur);
 

	
 
				ti.tile = tile;
 
				ti.tileh = GetTileSlope(tile, &ti.z);
 
				tt = GetTileType(tile);
 
			} else {
 
				ti.tileh = SLOPE_FLAT;
 
				ti.tile = 0;
 
				ti.z = 0;
 
				tt = MP_VOID;
 
			}
 

	
 
			y_cur += 0x10;
 
			x_cur -= 0x10;
 

	
 
			_added_tile_sprite = false;
 
			_offset_ground_sprites = false;
 

	
 
			_tile_type_procs[tt]->draw_tile_proc(&ti);
 
			DrawTileSelection(&ti);
 
		} while (--width_cur);
 

	
 
		if ((direction ^= 1) != 0) {
 
			y += 0x10;
 
		} else {
 
			x += 0x10;
 
		}
 
	} while (--height);
 
}
 

	
 

	
 
static void ViewportAddTownNames(DrawPixelInfo *dpi)
 
{
 
	Town *t;
 
	int left, top, right, bottom;
 

	
 
	if (!(_display_opt & DO_SHOW_TOWN_NAMES) || _game_mode == GM_MENU)
 
		return;
 

	
 
	left = dpi->left;
 
	top = dpi->top;
 
	right = left + dpi->width;
 
	bottom = top + dpi->height;
 

	
 
	switch (dpi->zoom) {
 
		case 0:
 
			FOR_ALL_TOWNS(t) {
 
				if (bottom > t->sign.top &&
 
						top    < t->sign.top + 12 &&
 
						right  > t->sign.left &&
 
						left   < t->sign.left + t->sign.width_1) {
 
					AddStringToDraw(t->sign.left + 1, t->sign.top + 1,
 
						_patches.population_in_label ? STR_TOWN_LABEL_POP : STR_TOWN_LABEL,
 
						t->index, t->population);
 
				}
 
			}
 
			break;
 

	
 
		case 1:
 
			right += 2;
 
			bottom += 2;
 

	
 
			FOR_ALL_TOWNS(t) {
 
				if (bottom > t->sign.top &&
 
						top    < t->sign.top + 24 &&
 
						right  > t->sign.left &&
 
						left   < t->sign.left + t->sign.width_1*2) {
 
					AddStringToDraw(t->sign.left + 1, t->sign.top + 1,
 
						_patches.population_in_label ? STR_TOWN_LABEL_POP : STR_TOWN_LABEL,
 
						t->index, t->population);
 
				}
 
			}
 
			break;
 

	
 
		default: NOT_REACHED();
 
		case 2:
 
			right += 4;
 
			bottom += 5;
 

	
 
			FOR_ALL_TOWNS(t) {
 
				if (bottom > t->sign.top &&
 
						top    < t->sign.top + 24 &&
 
						right  > t->sign.left &&
 
						left   < t->sign.left + t->sign.width_2*4) {
 
					AddStringToDraw(t->sign.left + 5, t->sign.top + 1, STR_TOWN_LABEL_TINY_BLACK, t->index, 0);
 
					AddStringToDraw(t->sign.left + 1, t->sign.top - 3, STR_TOWN_LABEL_TINY_WHITE, t->index, 0);
 
				}
 
			}
 
			break;
 
	}
 
}
 

	
 

	
 
static void AddStation(const Station *st, StringID str, uint16 width)
 
{
 
	StringSpriteToDraw *sstd;
 

	
 
	sstd = AddStringToDraw(st->sign.left + 1, st->sign.top + 1, str, st->index, st->facilities);
 
	if (sstd != NULL) {
 
		sstd->color = (st->owner == OWNER_NONE || st->facilities == 0) ? 0xE : _player_colors[st->owner];
 
		sstd->width = width;
 
	}
 
}
 

	
 

	
 
static void ViewportAddStationNames(DrawPixelInfo *dpi)
 
{
 
	int left, top, right, bottom;
 
	const Station *st;
 

	
 
	if (!(_display_opt & DO_SHOW_STATION_NAMES) || _game_mode == GM_MENU)
 
		return;
 

	
 
	left = dpi->left;
 
	top = dpi->top;
 
	right = left + dpi->width;
 
	bottom = top + dpi->height;
 

	
 
	switch (dpi->zoom) {
 
		case 0:
 
			FOR_ALL_STATIONS(st) {
 
				if (bottom > st->sign.top &&
 
						top    < st->sign.top + 12 &&
 
						right  > st->sign.left &&
 
						left   < st->sign.left + st->sign.width_1) {
 
					AddStation(st, STR_305C_0, st->sign.width_1);
 
				}
 
			}
 
			break;
 

	
 
		case 1:
 
			right += 2;
 
			bottom += 2;
 
			FOR_ALL_STATIONS(st) {
 
				if (bottom > st->sign.top &&
 
						top    < st->sign.top + 24 &&
 
						right  > st->sign.left &&
 
						left   < st->sign.left + st->sign.width_1*2) {
 
					AddStation(st, STR_305C_0, st->sign.width_1);
 
				}
 
			}
 
			break;
 

	
 
		default: NOT_REACHED();
 
		case 2:
 
			right += 4;
 
			bottom += 5;
 
			FOR_ALL_STATIONS(st) {
 
				if (bottom > st->sign.top &&
 
						top    < st->sign.top + 24 &&
 
						right  > st->sign.left &&
 
						left   < st->sign.left + st->sign.width_2*4) {
 
					AddStation(st, STR_STATION_SIGN_TINY, st->sign.width_2 | 0x8000);
 
				}
 
			}
 
			break;
 
	}
 
}
 

	
 

	
 
static void AddSign(const Sign *si, StringID str, uint16 width)
 
{
 
	StringSpriteToDraw *sstd;
 

	
 
	sstd = AddStringToDraw(si->sign.left + 1, si->sign.top + 1, str, si->str, 0);
 
	if (sstd != NULL) {
 
		sstd->color = (si->owner == OWNER_NONE) ? 14 : _player_colors[si->owner];
 
		sstd->width = width;
 
	}
 
}
 

	
 

	
 
static void ViewportAddSigns(DrawPixelInfo *dpi)
 
{
 
	const Sign *si;
 
	int left, top, right, bottom;
 

	
 
	if (!(_display_opt & DO_SHOW_SIGNS))
 
		return;
 

	
 
	left = dpi->left;
 
	top = dpi->top;
 
	right = left + dpi->width;
 
	bottom = top + dpi->height;
 

	
 
	switch (dpi->zoom) {
 
		case 0:
 
			FOR_ALL_SIGNS(si) {
 
				if (bottom > si->sign.top &&
 
						top    < si->sign.top + 12 &&
 
						right  > si->sign.left &&
 
						left   < si->sign.left + si->sign.width_1) {
 
					AddSign(si, STR_2806, si->sign.width_1);
 
				}
 
			}
 
			break;
 

	
 
		case 1:
 
			right += 2;
 
			bottom += 2;
 
			FOR_ALL_SIGNS(si) {
 
				if (bottom > si->sign.top &&
 
						top    < si->sign.top + 24 &&
 
						right  > si->sign.left &&
 
						left   < si->sign.left + si->sign.width_1 * 2) {
 
					AddSign(si, STR_2806, si->sign.width_1);
 
				}
 
			}
 
			break;
 

	
 
		default: NOT_REACHED();
 
		case 2:
 
			right += 4;
 
			bottom += 5;
 
			FOR_ALL_SIGNS(si) {
 
				if (bottom > si->sign.top &&
 
						top    < si->sign.top + 24 &&
 
						right  > si->sign.left &&
 
						left   < si->sign.left + si->sign.width_2 * 4) {
 
					AddSign(si, STR_2002, si->sign.width_2 | 0x8000);
 
				}
 
			}
 
			break;
 
	}
 
}
 

	
 

	
 
static void AddWaypoint(const Waypoint *wp, StringID str, uint16 width)
 
{
 
	StringSpriteToDraw *sstd;
 

	
 
	sstd = AddStringToDraw(wp->sign.left + 1, wp->sign.top + 1, str, wp->index, 0);
 
	if (sstd != NULL) {
 
		sstd->color = (wp->deleted ? 0xE : 11);
 
		sstd->width = width;
 
	}
 
}
 

	
 

	
 
static void ViewportAddWaypoints(DrawPixelInfo *dpi)
 
{
 
	const Waypoint *wp;
 
	int left, top, right, bottom;
 

	
 
	if (!(_display_opt & DO_WAYPOINTS))
 
		return;
 

	
 
	left = dpi->left;
 
	top = dpi->top;
 
	right = left + dpi->width;
 
	bottom = top + dpi->height;
 

	
 
	switch (dpi->zoom) {
 
		case 0:
 
			FOR_ALL_WAYPOINTS(wp) {
 
				if (bottom > wp->sign.top &&
 
						top    < wp->sign.top + 12 &&
 
						right  > wp->sign.left &&
 
						left   < wp->sign.left + wp->sign.width_1) {
 
					AddWaypoint(wp, STR_WAYPOINT_VIEWPORT, wp->sign.width_1);
 
				}
 
			}
 
			break;
 

	
 
		case 1:
 
			right += 2;
 
			bottom += 2;
 
			FOR_ALL_WAYPOINTS(wp) {
 
				if (bottom > wp->sign.top &&
 
						top    < wp->sign.top + 24 &&
 
						right  > wp->sign.left &&
 
						left   < wp->sign.left + wp->sign.width_1*2) {
 
					AddWaypoint(wp, STR_WAYPOINT_VIEWPORT, wp->sign.width_1);
 
				}
 
			}
 
			break;
 

	
 
		default: NOT_REACHED();
 
		case 2:
 
			right += 4;
 
			bottom += 5;
 
			FOR_ALL_WAYPOINTS(wp) {
 
				if (bottom > wp->sign.top &&
 
						top    < wp->sign.top + 24 &&
 
						right  > wp->sign.left &&
 
						left   < wp->sign.left + wp->sign.width_2*4) {
 
					AddWaypoint(wp, STR_WAYPOINT_VIEWPORT_TINY, wp->sign.width_2 | 0x8000);
 
				}
 
			}
 
			break;
 
	}
 
}
 

	
 
void UpdateViewportSignPos(ViewportSign *sign, int left, int top, StringID str)
 
{
 
	char buffer[128];
 
	uint w;
 

	
 
	sign->top = top;
 

	
 
	GetString(buffer, str, lastof(buffer));
 
	w = GetStringBoundingBox(buffer).width + 3;
 
	sign->width_1 = w;
 
	sign->left = left - w / 2;
 

	
 
	/* zoomed out version */
 
	_cur_fontsize = FS_SMALL;
 
	w = GetStringBoundingBox(buffer).width + 3;
 
	_cur_fontsize = FS_NORMAL;
 
	sign->width_2 = w;
 
}
 

	
 

	
 
static void ViewportDrawTileSprites(TileSpriteToDraw *ts)
 
{
 
	do {
 
		Point pt = RemapCoords(ts->x, ts->y, ts->z);
 
		DrawSprite(ts->image, pt.x, pt.y);
 
		ts = ts->next;
 
	} while (ts != NULL);
 
}
 

	
 
static void ViewportSortParentSprites(ParentSpriteToDraw *psd[])
 
{
 
	while (*psd != NULL) {
 
		ParentSpriteToDraw* ps = *psd;
 

	
 
		if (!(ps->unk16 & 1)) {
 
			ParentSpriteToDraw** psd2 = psd;
 

	
 
			ps->unk16 |= 1;
 

	
 
			while (*++psd2 != NULL) {
 
				ParentSpriteToDraw* ps2 = *psd2;
 
				ParentSpriteToDraw** psd3;
 

	
 
				if (ps2->unk16 & 1) continue;
 

	
 
				/* Decide which comparator to use, based on whether the bounding
 
				 * boxes overlap
 
				 */
 
				if (ps->xmax > ps2->xmin && ps->xmin < ps2->xmax && // overlap in X?
 
						ps->ymax > ps2->ymin && ps->ymin < ps2->ymax && // overlap in Y?
 
						ps->zmax > ps2->zmin && ps->zmin < ps2->zmax) { // overlap in Z?
 
					/* Use X+Y+Z as the sorting order, so sprites closer to the bottom of
 
					 * the screen and with higher Z elevation, are drawn in front.
 
					 * Here X,Y,Z are the coordinates of the "center of mass" of the sprite,
 
					 * i.e. X=(left+right)/2, etc.
 
					 * However, since we only care about order, don't actually divide / 2
 
					 */
 
					if (ps->xmin + ps->xmax + ps->ymin + ps->ymax + ps->zmin + ps->zmax <=
 
							ps2->xmin + ps2->xmax + ps2->ymin + ps2->ymax + ps2->zmin + ps2->zmax) {
 
						continue;
 
					}
 
				} else {
 
					if (ps->xmax < ps2->xmin ||
 
							ps->ymax < ps2->ymin ||
 
							ps->zmax < ps2->zmin || (
 
								ps->xmin < ps2->xmax &&
 
								ps->ymin < ps2->ymax &&
 
								ps->zmin < ps2->zmax
 
							)) {
 
						continue;
 
					}
 
				}
 

	
 
				// Swap the two sprites ps and ps2 using bubble-sort algorithm.
 
				psd3 = psd;
 
				do {
 
					ParentSpriteToDraw* temp = *psd3;
 
					*psd3 = ps2;
 
					ps2 = temp;
 

	
 
					psd3++;
 
				} while (psd3 <= psd2);
 
			}
 
		} else {
 
			psd++;
 
		}
 
	}
 
}
 

	
 
static void ViewportDrawParentSprites(ParentSpriteToDraw *psd[])
 
{
 
	for (; *psd != NULL; psd++) {
 
		const ParentSpriteToDraw* ps = *psd;
 
		Point pt = RemapCoords(ps->xmin, ps->ymin, ps->zmin);
 
		const ChildScreenSpriteToDraw* cs;
 

	
 
		DrawSprite(ps->image, pt.x, pt.y);
 

	
 
		for (cs = ps->child; cs != NULL; cs = cs->next) {
 
			DrawSprite(cs->image, ps->left + cs->x, ps->top + cs->y);
 
		}
 
	}
 
}
 

	
 
static void ViewportDrawStrings(DrawPixelInfo *dpi, const StringSpriteToDraw *ss)
 
{
 
	DrawPixelInfo dp;
 
	byte zoom;
 

	
 
	_cur_dpi = &dp;
 
	dp = *dpi;
 

	
 
	zoom = dp.zoom;
 
	dp.zoom = 0;
 

	
 
	dp.left >>= zoom;
 
	dp.top >>= zoom;
 
	dp.width >>= zoom;
 
	dp.height >>= zoom;
 

	
 
	do {
 
		uint16 colour;
 

	
 
		if (ss->width != 0) {
 
			int x = (ss->x >> zoom) - 1;
 
			int y = (ss->y >> zoom) - 1;
 
			int bottom = y + 11;
 
			int w = ss->width;
 

	
 
			if (w & 0x8000) {
 
				w &= ~0x8000;
 
				y--;
 
				bottom -= 6;
 
				w -= 3;
 
			}
 

	
 
		/* Draw the rectangle if 'tranparent station signs' is off,
 
		 * or if we are drawing a general text sign (STR_2806) */
 
			if (!(_display_opt & DO_TRANS_SIGNS) || ss->string == STR_2806)
 
				DrawFrameRect(
 
					x, y, x + w, bottom, ss->color,
 
					(_display_opt & DO_TRANS_BUILDINGS) ? FR_TRANSPARENT : 0
 
				);
 
		}
 

	
 
		SetDParam(0, ss->params[0]);
 
		SetDParam(1, ss->params[1]);
 
		/* if we didn't draw a rectangle, or if transparant building is on,
 
		 * draw the text in the color the rectangle would have */
 
		if ((
 
					(_display_opt & DO_TRANS_BUILDINGS) ||
 
					(_display_opt & DO_TRANS_SIGNS && ss->string != STR_2806)
 
				) && ss->width != 0) {
 
			/* Real colors need the IS_PALETTE_COLOR flag
 
			 * otherwise colors from _string_colormap are assumed. */
 
			colour = _colour_gradient[ss->color][6] | IS_PALETTE_COLOR;
 
		} else {
 
			colour = 16;
 
		}
 
		DrawString(
 
			ss->x >> zoom, (ss->y >> zoom) - (ss->width & 0x8000 ? 2 : 0),
 
			ss->string, colour
 
		);
 

	
 
		ss = ss->next;
 
	} while (ss != NULL);
 
}
 

	
 
void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom)
 
{
 
	ViewportDrawer vd;
 
	int mask;
 
	int x;
 
	int y;
 
	DrawPixelInfo *old_dpi;
 

	
 
	byte mem[VIEWPORT_DRAW_MEM];
 
	ParentSpriteToDraw *parent_list[6144];
 

	
 
	_cur_vd = &vd;
 

	
 
	old_dpi = _cur_dpi;
 
	_cur_dpi = &vd.dpi;
 

	
 
	vd.dpi.zoom = vp->zoom;
 
	mask = (-1) << vp->zoom;
 

	
 
	vd.combine_sprites = 0;
 

	
 
	vd.dpi.width = (right - left) & mask;
 
	vd.dpi.height = (bottom - top) & mask;
 
	vd.dpi.left = left & mask;
 
	vd.dpi.top = top & mask;
 
	vd.dpi.pitch = old_dpi->pitch;
 

	
 
	x = ((vd.dpi.left - (vp->virtual_left&mask)) >> vp->zoom) + vp->left;
 
	y = ((vd.dpi.top - (vp->virtual_top&mask)) >> vp->zoom) + vp->top;
 

	
 
	vd.dpi.dst_ptr = old_dpi->dst_ptr + x - old_dpi->left + (y - old_dpi->top) * old_dpi->pitch;
 

	
 
	vd.parent_list = parent_list;
 
	vd.eof_parent_list = endof(parent_list);
 
	vd.spritelist_mem = mem;
 
	vd.eof_spritelist_mem = endof(mem) - sizeof(LARGEST_SPRITELIST_STRUCT);
 
	vd.last_string = &vd.first_string;
 
	vd.first_string = NULL;
 
	vd.last_tile = &vd.first_tile;
 
	vd.first_tile = NULL;
 

	
 
	ViewportAddLandscape();
 
	ViewportAddVehicles(&vd.dpi);
 
	DrawTextEffects(&vd.dpi);
 

	
 
	ViewportAddTownNames(&vd.dpi);
 
	ViewportAddStationNames(&vd.dpi);
 
	ViewportAddSigns(&vd.dpi);
 
	ViewportAddWaypoints(&vd.dpi);
 

	
 
	// This assert should never happen (because the length of the parent_list
 
	//  is checked)
 
	assert(vd.parent_list <= endof(parent_list));
 

	
 
	if (vd.first_tile != NULL) ViewportDrawTileSprites(vd.first_tile);
 

	
 
	/* null terminate parent sprite list */
 
	*vd.parent_list = NULL;
 

	
 
	ViewportSortParentSprites(parent_list);
 
	ViewportDrawParentSprites(parent_list);
 

	
 
	if (vd.first_string != NULL) ViewportDrawStrings(&vd.dpi, vd.first_string);
 

	
 
	_cur_dpi = old_dpi;
 
}
 

	
 
// Make sure we don't draw a too big area at a time.
 
// If we do, the sprite memory will overflow.
 
static void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom)
 
{
 
	if (((bottom - top) * (right - left) << vp->zoom) > 180000) {
 
		if ((bottom - top) > (right - left)) {
 
			int t = (top + bottom) >> 1;
 
			ViewportDrawChk(vp, left, top, right, t);
 
			ViewportDrawChk(vp, left, t, right, bottom);
 
		} else {
 
			int t = (left + right) >> 1;
 
			ViewportDrawChk(vp, left, top, t, bottom);
 
			ViewportDrawChk(vp, t, top, right, bottom);
 
		}
 
	} else {
 
		ViewportDoDraw(vp,
 
			((left - vp->left) << vp->zoom) + vp->virtual_left,
 
			((top - vp->top) << vp->zoom) + vp->virtual_top,
 
			((right - vp->left) << vp->zoom) + vp->virtual_left,
 
			((bottom - vp->top) << vp->zoom) + vp->virtual_top
 
		);
 
	}
 
}
 

	
 
static inline void ViewportDraw(const ViewPort *vp, int left, int top, int right, int bottom)
 
{
 
	if (right <= vp->left || bottom <= vp->top) return;
 

	
 
	if (left >= vp->left + vp->width) return;
 

	
 
	if (left < vp->left) left = vp->left;
 
	if (right > vp->left + vp->width) right = vp->left + vp->width;
 

	
 
	if (top >= vp->top + vp->height) return;
 

	
 
	if (top < vp->top) top = vp->top;
 
	if (bottom > vp->top + vp->height) bottom = vp->top + vp->height;
 

	
 
	ViewportDrawChk(vp, left, top, right, bottom);
 
}
 

	
 
void DrawWindowViewport(const Window *w)
 
{
 
	DrawPixelInfo *dpi = _cur_dpi;
 

	
 
	dpi->left += w->left;
 
	dpi->top += w->top;
 

	
 
	ViewportDraw(w->viewport, dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height);
 

	
 
	dpi->left -= w->left;
 
	dpi->top -= w->top;
 
}
 

	
 
void UpdateViewportPosition(Window *w)
 
{
 
	const ViewPort *vp = w->viewport;
 

	
 
	if (WP(w, vp_d).follow_vehicle != INVALID_VEHICLE) {
 
		const Vehicle* veh = GetVehicle(WP(w,vp_d).follow_vehicle);
 
		Point pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
 

	
 
		SetViewportPosition(w, pt.x, pt.y);
 
	} else {
 
		int x;
 
		int y;
 
		int vx;
 
		int vy;
 

	
 
		// Center of the viewport is hot spot
 
		x = WP(w,vp_d).scrollpos_x + vp->virtual_width / 2;
 
		y = WP(w,vp_d).scrollpos_y + vp->virtual_height / 2;
 
		// Convert viewport coordinates to map coordinates
 
		// Calculation is scaled by 4 to avoid rounding errors
 
		vx = -x + y * 2;
 
		vy =  x + y * 2;
 
		// clamp to size of map
 
		vx = clamp(vx, 0 * 4, MapMaxX() * TILE_SIZE * 4);
 
		vy = clamp(vy, 0 * 4, MapMaxY() * TILE_SIZE * 4);
 
		// Convert map coordinates to viewport coordinates
 
		x = (-vx + vy) / 2;
 
		y = ( vx + vy) / 4;
 
		// Set position
 
		WP(w, vp_d).scrollpos_x = x - vp->virtual_width / 2;
 
		WP(w, vp_d).scrollpos_y = y - vp->virtual_height / 2;
 

	
 
		SetViewportPosition(w, WP(w, vp_d).scrollpos_x, WP(w, vp_d).scrollpos_y);
 
	}
 
}
 

	
 
static void MarkViewportDirty(const ViewPort *vp, int left, int top, int right, int bottom)
 
{
 
	right -= vp->virtual_left;
 
	if (right <= 0) return;
 

	
 
	bottom -= vp->virtual_top;
 
	if (bottom <= 0) return;
 

	
 
	left = max(0, left - vp->virtual_left);
 

	
 
	if (left >= vp->virtual_width) return;
 

	
 
	top = max(0, top - vp->virtual_top);
 

	
 
	if (top >= vp->virtual_height) return;
 

	
 
	SetDirtyBlocks(
 
		(left >> vp->zoom) + vp->left,
 
		(top >> vp->zoom) + vp->top,
 
		(right >> vp->zoom) + vp->left,
 
		(bottom >> vp->zoom) + vp->top
 
	);
 
}
 

	
 
void MarkAllViewportsDirty(int left, int top, int right, int bottom)
 
{
 
	const ViewPort *vp = _viewports;
 
	uint32 act = _active_viewports;
 
	do {
 
		if (act & 1) {
 
			assert(vp->width != 0);
 
			MarkViewportDirty(vp, left, top, right, bottom);
 
		}
 
	} while (vp++,act>>=1);
 
}
 

	
 
void MarkTileDirtyByTile(TileIndex tile)
 
{
 
	Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, GetTileZ(tile));
 
	MarkAllViewportsDirty(
 
		pt.x - 31,
 
		pt.y - 122,
 
		pt.x - 31 + 67,
 
		pt.y - 122 + 154
 
	);
 
}
 

	
 
void MarkTileDirty(int x, int y)
 
{
 
	uint z = 0;
 
	Point pt;
 

	
 
	if (IS_INT_INSIDE(x, 0, MapSizeX() * TILE_SIZE) &&
 
			IS_INT_INSIDE(y, 0, MapSizeY() * TILE_SIZE))
 
		z = GetTileZ(TileVirtXY(x, y));
 
	pt = RemapCoords(x, y, z);
 

	
 
	MarkAllViewportsDirty(
 
		pt.x - 31,
 
		pt.y - 122,
 
		pt.x - 31 + 67,
 
		pt.y - 122 + 154
 
	);
 
}
 

	
 
static void SetSelectionTilesDirty(void)
 
{
 
	int y_size, x_size;
 
	int x = _thd.pos.x;
 
	int y = _thd.pos.y;
 

	
 
	x_size = _thd.size.x;
 
	y_size = _thd.size.y;
 

	
 
	if (_thd.outersize.x) {
 
		x_size += _thd.outersize.x;
 
		x += _thd.offs.x;
 
		y_size += _thd.outersize.y;
 
		y += _thd.offs.y;
 
	}
 

	
 
	assert(x_size > 0);
 
	assert(y_size > 0);
 

	
 
	x_size += x;
 
	y_size += y;
 

	
 
	do {
 
		int y_bk = y;
 
		do {
 
			MarkTileDirty(x, y);
 
		} while ( (y += TILE_SIZE) != y_size);
 
		y = y_bk;
 
	} while ( (x += TILE_SIZE) != x_size);
 
}
 

	
 

	
 
void SetSelectionRed(bool b)
 
{
 
	_thd.make_square_red = b;
 
	SetSelectionTilesDirty();
 
}
 

	
 

	
 
static bool CheckClickOnTown(const ViewPort *vp, int x, int y)
 
{
 
	const Town *t;
 

	
 
	if (!(_display_opt & DO_SHOW_TOWN_NAMES)) return false;
 

	
 
	switch (vp->zoom) {
 
		case 0:
 
			x = x - vp->left + vp->virtual_left;
 
			y = y - vp->top  + vp->virtual_top;
 
			FOR_ALL_TOWNS(t) {
 
				if (y >= t->sign.top &&
 
						y < t->sign.top + 12 &&
 
						x >= t->sign.left &&
 
						x < t->sign.left + t->sign.width_1) {
 
					ShowTownViewWindow(t->index);
 
					return true;
 
				}
 
			}
 
			break;
 

	
 
		case 1:
 
			x = (x - vp->left + 1) * 2 + vp->virtual_left;
 
			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
 
			FOR_ALL_TOWNS(t) {
 
				if (y >= t->sign.top &&
 
						y < t->sign.top + 24 &&
 
						x >= t->sign.left &&
 
						x < t->sign.left + t->sign.width_1 * 2) {
 
					ShowTownViewWindow(t->index);
 
					return true;
 
				}
 
			}
 
			break;
 

	
 
		default:
 
			x = (x - vp->left + 3) * 4 + vp->virtual_left;
 
			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
 
			FOR_ALL_TOWNS(t) {
 
				if (y >= t->sign.top &&
 
						y < t->sign.top + 24 &&
 
						x >= t->sign.left &&
 
						x < t->sign.left + t->sign.width_2 * 4) {
 
					ShowTownViewWindow(t->index);
 
					return true;
 
				}
 
			}
 
			break;
 
	}
 

	
 
	return false;
 
}
 

	
 

	
 
static bool CheckClickOnStation(const ViewPort *vp, int x, int y)
 
{
 
	const Station *st;
 

	
 
	if (!(_display_opt & DO_SHOW_STATION_NAMES)) return false;
 

	
 
	switch (vp->zoom) {
 
		case 0:
 
			x = x - vp->left + vp->virtual_left;
 
			y = y - vp->top  + vp->virtual_top;
 
			FOR_ALL_STATIONS(st) {
 
				if (y >= st->sign.top &&
 
						y < st->sign.top + 12 &&
 
						x >= st->sign.left &&
 
						x < st->sign.left + st->sign.width_1) {
 
					ShowStationViewWindow(st->index);
 
					return true;
 
				}
 
			}
 
			break;
 

	
 
		case 1:
 
			x = (x - vp->left + 1) * 2 + vp->virtual_left;
 
			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
 
			FOR_ALL_STATIONS(st) {
 
				if (y >= st->sign.top &&
 
						y < st->sign.top + 24 &&
 
						x >= st->sign.left &&
 
						x < st->sign.left + st->sign.width_1 * 2) {
 
					ShowStationViewWindow(st->index);
 
					return true;
 
				}
 
			}
 
			break;
 

	
 
		default:
 
			x = (x - vp->left + 3) * 4 + vp->virtual_left;
 
			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
 
			FOR_ALL_STATIONS(st) {
 
				if (y >= st->sign.top &&
 
						y < st->sign.top + 24 &&
 
						x >= st->sign.left &&
 
						x < st->sign.left + st->sign.width_2 * 4) {
 
					ShowStationViewWindow(st->index);
 
					return true;
 
				}
 
			}
 
			break;
 
	}
 

	
 
	return false;
 
}
 

	
 

	
 
static bool CheckClickOnSign(const ViewPort *vp, int x, int y)
 
{
 
	const Sign *si;
 

	
 
	if (!(_display_opt & DO_SHOW_SIGNS)) return false;
 

	
 
	switch (vp->zoom) {
 
		case 0:
 
			x = x - vp->left + vp->virtual_left;
 
			y = y - vp->top  + vp->virtual_top;
 
			FOR_ALL_SIGNS(si) {
 
				if (y >= si->sign.top &&
 
						y <  si->sign.top + 12 &&
 
						x >= si->sign.left &&
 
						x <  si->sign.left + si->sign.width_1) {
 
					ShowRenameSignWindow(si);
 
					return true;
 
				}
 
			}
 
			break;
 

	
 
		case 1:
 
			x = (x - vp->left + 1) * 2 + vp->virtual_left;
 
			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
 
			FOR_ALL_SIGNS(si) {
 
				if (y >= si->sign.top &&
 
						y <  si->sign.top + 24 &&
 
						x >= si->sign.left &&
 
						x <  si->sign.left + si->sign.width_1 * 2) {
 
					ShowRenameSignWindow(si);
 
					return true;
 
				}
 
			}
 
			break;
 

	
 
		default:
 
			x = (x - vp->left + 3) * 4 + vp->virtual_left;
 
			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
 
			FOR_ALL_SIGNS(si) {
 
				if (y >= si->sign.top &&
 
						y <  si->sign.top + 24 &&
 
						x >= si->sign.left &&
 
						x <  si->sign.left + si->sign.width_2 * 4) {
 
					ShowRenameSignWindow(si);
 
					return true;
 
				}
 
			}
 
			break;
 
	}
 

	
 
	return false;
 
}
 

	
 

	
 
static bool CheckClickOnWaypoint(const ViewPort *vp, int x, int y)
 
{
 
	const Waypoint *wp;
 

	
 
	if (!(_display_opt & DO_WAYPOINTS)) return false;
 

	
 
	switch (vp->zoom) {
 
		case 0:
 
			x = x - vp->left + vp->virtual_left;
 
			y = y - vp->top  + vp->virtual_top;
 
			FOR_ALL_WAYPOINTS(wp) {
 
				if (y >= wp->sign.top &&
 
						y < wp->sign.top + 12 &&
 
						x >= wp->sign.left &&
 
						x < wp->sign.left + wp->sign.width_1) {
 
					ShowRenameWaypointWindow(wp);
 
					return true;
 
				}
 
			}
 
			break;
 

	
 
		case 1:
 
			x = (x - vp->left + 1) * 2 + vp->virtual_left;
 
			y = (y - vp->top  + 1) * 2 + vp->virtual_top;
 
			FOR_ALL_WAYPOINTS(wp) {
 
				if (y >= wp->sign.top &&
 
						y < wp->sign.top + 24 &&
 
						x >= wp->sign.left &&
 
						x < wp->sign.left + wp->sign.width_1 * 2) {
 
					ShowRenameWaypointWindow(wp);
 
					return true;
 
				}
 
			}
 
			break;
 

	
 
		default:
 
			x = (x - vp->left + 3) * 4 + vp->virtual_left;
 
			y = (y - vp->top  + 3) * 4 + vp->virtual_top;
 
			FOR_ALL_WAYPOINTS(wp) {
 
				if (y >= wp->sign.top &&
 
						y < wp->sign.top + 24 &&
 
						x >= wp->sign.left &&
 
						x < wp->sign.left + wp->sign.width_2 * 4) {
 
					ShowRenameWaypointWindow(wp);
 
					return true;
 
				}
 
			}
 
			break;
 
	}
 

	
 
	return false;
 
}
 

	
 

	
 
static void CheckClickOnLandscape(const ViewPort *vp, int x, int y)
 
{
 
	Point pt = TranslateXYToTileCoord(vp, x, y);
 

	
 
	if (pt.x != -1) ClickTile(TileVirtXY(pt.x, pt.y));
 
}
 

	
 

	
 
static void SafeShowTrainViewWindow(const Vehicle* v)
 
{
 
	if (!IsFrontEngine(v)) v = GetFirstVehicleInChain(v);
 
	ShowTrainViewWindow(v);
 
}
 

	
 
static void Nop(const Vehicle *v) {}
 

	
 
typedef void OnVehicleClickProc(const Vehicle *v);
 
static OnVehicleClickProc* const _on_vehicle_click_proc[] = {
 
	SafeShowTrainViewWindow,
 
	ShowRoadVehViewWindow,
 
	ShowShipViewWindow,
 
	ShowAircraftViewWindow,
 
	Nop, // Special vehicles
 
	Nop  // Disaster vehicles
 
};
 

	
 
void HandleViewportClicked(const ViewPort *vp, int x, int y)
 
{
 
	const Vehicle *v;
 

	
 
	if (CheckClickOnTown(vp, x, y)) return;
 
	if (CheckClickOnStation(vp, x, y)) return;
 
	if (CheckClickOnSign(vp, x, y)) return;
 
	if (CheckClickOnWaypoint(vp, x, y)) return;
 
	CheckClickOnLandscape(vp, x, y);
 

	
 
	v = CheckClickOnVehicle(vp, x, y);
 
	if (v != NULL) {
 
		DEBUG(misc, 2, "Vehicle %d (index %d) at %p", v->unitnumber, v->index, v);
 
		_on_vehicle_click_proc[v->type - 0x10](v);
 
	}
 
}
 

	
 
Vehicle *CheckMouseOverVehicle(void)
 
{
 
	const Window *w;
 
	const ViewPort *vp;
 

	
 
	int x = _cursor.pos.x;
 
	int y = _cursor.pos.y;
 

	
 
	w = FindWindowFromPt(x, y);
 
	if (w == NULL) return NULL;
 

	
 
	vp = IsPtInWindowViewport(w, x, y);
 
	return (vp != NULL) ? CheckClickOnVehicle(vp, x, y) : NULL;
 
}
 

	
 

	
 

	
 
void PlaceObject(void)
 
{
 
	Point pt;
 
	Window *w;
 

	
 
	pt = GetTileBelowCursor();
 
	if (pt.x == -1) return;
 

	
 
	if (_thd.place_mode == VHM_POINT) {
 
		pt.x += 8;
 
		pt.y += 8;
 
	}
 

	
 
	_tile_fract_coords.x = pt.x & 0xF;
 
	_tile_fract_coords.y = pt.y & 0xF;
 

	
 
	w = GetCallbackWnd();
 
	if (w != NULL) {
 
		WindowEvent e;
 

	
 
		e.event = WE_PLACE_OBJ;
 
		e.we.place.pt = pt;
 
		e.we.place.tile = TileVirtXY(pt.x, pt.y);
 
		w->wndproc(w, &e);
 
	}
 
}
 

	
 

	
 
/* scrolls the viewport in a window to a given location */
 
bool ScrollWindowTo(int x , int y, Window *w)
 
{
 
	Point pt;
 

	
 
	pt = MapXYZToViewport(w->viewport, x, y, GetSlopeZ(x, y));
 
	WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
 

	
 
	if (WP(w, vp_d).scrollpos_x == pt.x && WP(w, vp_d).scrollpos_y == pt.y)
 
		return false;
 

	
 
	WP(w, vp_d).scrollpos_x = pt.x;
 
	WP(w, vp_d).scrollpos_y = pt.y;
 
	return true;
 
}
 

	
 

	
 
bool ScrollMainWindowTo(int x, int y)
 
{
 
	Window *w;
 
	bool res = ScrollWindowTo(x, y, FindWindowById(WC_MAIN_WINDOW, 0));
 

	
 
	/* If a user scrolls to a tile (via what way what so ever) and already is on
 
	 *  that tile (e.g.: pressed twice), move the smallmap to that location,
 
	 *  so you directly see where you are on the smallmap. */
 

	
 
	if (res) return res;
 

	
 
	w = FindWindowById(WC_SMALLMAP, 0);
 
	if (w == NULL) return res;
 

	
 
	SmallMapCenterOnCurrentPos(w);
 

	
 
	return res;
 
}
 

	
 

	
 
bool ScrollMainWindowToTile(TileIndex tile)
 
{
 
	return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2);
 
}
 

	
 
void SetRedErrorSquare(TileIndex tile)
 
{
 
	TileIndex old;
 

	
 
	old = _thd.redsq;
 
	_thd.redsq = tile;
 

	
 
	if (tile != old) {
 
		if (tile != 0) MarkTileDirtyByTile(tile);
 
		if (old  != 0) MarkTileDirtyByTile(old);
 
	}
 
}
 

	
 
void SetTileSelectSize(int w, int h)
 
{
 
	_thd.new_size.x = w * TILE_SIZE;
 
	_thd.new_size.y = h * TILE_SIZE;
 
	_thd.new_outersize.x = 0;
 
	_thd.new_outersize.y = 0;
 
}
 

	
 
void SetTileSelectBigSize(int ox, int oy, int sx, int sy)
 
{
 
	_thd.offs.x = ox * TILE_SIZE;
 
	_thd.offs.y = oy * TILE_SIZE;
 
	_thd.new_outersize.x = sx * TILE_SIZE;
 
	_thd.new_outersize.y = sy * TILE_SIZE;
 
}
 

	
 
/* returns the best autorail highlight type from map coordinates */
 
static byte GetAutorailHT(int x, int y)
 
{
 
	return HT_RAIL | _AutorailPiece[x & 0xF][y & 0xF];
 
}
 

	
 
// called regular to update tile highlighting in all cases
 
void UpdateTileSelection(void)
 
{
 
	int x1;
 
	int y1;
 

	
 
	_thd.new_drawstyle = 0;
 

	
 
	if (_thd.place_mode == VHM_SPECIAL) {
 
		x1 = _thd.selend.x;
 
		y1 = _thd.selend.y;
 
		if (x1 != -1) {
 
			int x2 = _thd.selstart.x;
 
			int y2 = _thd.selstart.y;
 
			x1 &= ~0xF;
 
			y1 &= ~0xF;
 

	
 
			if (x1 >= x2) intswap(x1,x2);
 
			if (y1 >= y2) intswap(y1,y2);
 
			_thd.new_pos.x = x1;
 
			_thd.new_pos.y = y1;
 
			_thd.new_size.x = x2 - x1 + TILE_SIZE;
 
			_thd.new_size.y = y2 - y1 + TILE_SIZE;
 
			_thd.new_drawstyle = _thd.next_drawstyle;
 
		}
 
	} else if (_thd.place_mode != VHM_NONE) {
 
		Point pt = GetTileBelowCursor();
 
		x1 = pt.x;
 
		y1 = pt.y;
 
		if (x1 != -1) {
 
			switch (_thd.place_mode) {
 
				case VHM_RECT:
 
					_thd.new_drawstyle = HT_RECT;
 
					break;
 
				case VHM_POINT:
 
					_thd.new_drawstyle = HT_POINT;
 
					x1 += 8;
 
					y1 += 8;
 
					break;
 
				case VHM_RAIL:
 
					_thd.new_drawstyle = GetAutorailHT(pt.x, pt.y); // draw one highlighted tile
 
			}
 
			_thd.new_pos.x = x1 & ~0xF;
 
			_thd.new_pos.y = y1 & ~0xF;
 
		}
 
	}
 

	
 
	// redraw selection
 
	if (_thd.drawstyle != _thd.new_drawstyle ||
 
			_thd.pos.x != _thd.new_pos.x || _thd.pos.y != _thd.new_pos.y ||
 
			_thd.size.x != _thd.new_size.x || _thd.size.y != _thd.new_size.y ||
 
	    _thd.outersize.x != _thd.new_outersize.x ||
 
	    _thd.outersize.y != _thd.new_outersize.y) {
 
		// clear the old selection?
 
		if (_thd.drawstyle) SetSelectionTilesDirty();
 

	
 
		_thd.drawstyle = _thd.new_drawstyle;
 
		_thd.pos = _thd.new_pos;
 
		_thd.size = _thd.new_size;
 
		_thd.outersize = _thd.new_outersize;
 
		_thd.dirty = 0xff;
 

	
 
		// draw the new selection?
 
		if (_thd.new_drawstyle) SetSelectionTilesDirty();
 
	}
 
}
 

	
 
// highlighting tiles while only going over them with the mouse
 
void VpStartPlaceSizing(TileIndex tile, int user)
 
{
 
	_thd.userdata = user;
 
	_thd.selend.x = TileX(tile) * TILE_SIZE;
 
	_thd.selstart.x = TileX(tile) * TILE_SIZE;
 
	_thd.selend.y = TileY(tile) * TILE_SIZE;
 
	_thd.selstart.y = TileY(tile) * TILE_SIZE;
 
	if (_thd.place_mode == VHM_RECT) {
 
		_thd.place_mode = VHM_SPECIAL;
 
		_thd.next_drawstyle = HT_RECT;
 
	} else if (_thd.place_mode == VHM_RAIL) { // autorail one piece
 
		_thd.place_mode = VHM_SPECIAL;
 
		_thd.next_drawstyle = _thd.drawstyle;
 
	} else {
 
		_thd.place_mode = VHM_SPECIAL;
 
		_thd.next_drawstyle = HT_POINT;
 
	}
 
	_special_mouse_mode = WSM_SIZING;
 
}
 

	
 
void VpSetPlaceSizingLimit(int limit)
 
{
 
	_thd.sizelimit = limit;
 
}
 

	
 
/**
 
* Highlights all tiles between a set of two tiles. Used in dock and tunnel placement
 
* @param from TileIndex of the first tile to highlight
 
* @param to TileIndex of the last tile to highlight */
 
void VpSetPresizeRange(TileIndex from, TileIndex to)
 
{
 
	uint distance = DistanceManhattan(from, to) + 1;
 

	
 
	_thd.selend.x = TileX(to) * TILE_SIZE;
 
	_thd.selend.y = TileY(to) * TILE_SIZE;
 
	_thd.selstart.x = TileX(from) * TILE_SIZE;
 
	_thd.selstart.y = TileY(from) * TILE_SIZE;
 
	_thd.next_drawstyle = HT_RECT;
 

	
 
	/* show measurement only if there is any length to speak of */
 
	if (distance > 1) GuiShowTooltipsWithArgs(STR_MEASURE_LENGTH, 1, &distance);
 
}
 

	
 
static void VpStartPreSizing(void)
 
{
 
	_thd.selend.x = -1;
 
	_special_mouse_mode = WSM_PRESIZE;
 
}
 

	
 
/* returns information about the 2x1 piece to be build.
 
 * The lower bits (0-3) are the track type. */
 
static byte Check2x1AutoRail(int mode)
 
{
 
	int fxpy = _tile_fract_coords.x + _tile_fract_coords.y;
 
	int sxpy = (_thd.selend.x & 0xF) + (_thd.selend.y & 0xF);
 
	int fxmy = _tile_fract_coords.x - _tile_fract_coords.y;
 
	int sxmy = (_thd.selend.x & 0xF) - (_thd.selend.y & 0xF);
 

	
 
	switch (mode) {
 
	case 0: // end piece is lower right
 
		if (fxpy >= 20 && sxpy <= 12) { /*SwapSelection(); DoRailroadTrack(0); */return 3; }
 
		if (fxmy < -3 && sxmy > 3) {/* DoRailroadTrack(0); */return 5; }
 
		return 1;
 

	
 
	case 1:
 
		if (fxmy > 3 && sxmy < -3) { /*SwapSelection(); DoRailroadTrack(0); */return 4; }
 
		if (fxpy <= 12 && sxpy >= 20) { /*DoRailroadTrack(0); */return 2; }
 
		return 1;
 

	
 
	case 2:
 
		if (fxmy > 3 && sxmy < -3) { /*DoRailroadTrack(3);*/ return 4; }
 
		if (fxpy >= 20 && sxpy <= 12) { /*SwapSelection(); DoRailroadTrack(0); */return 3; }
 
		return 0;
 

	
 
	case 3:
 
		if (fxmy < -3 && sxmy > 3) { /*SwapSelection(); DoRailroadTrack(3);*/ return 5; }
 
		if (fxpy <= 12 && sxpy >= 20) { /*DoRailroadTrack(0); */return 2; }
 
		return 0;
 
	}
 

	
 
	return 0; // avoids compiler warnings
 
}
 

	
 
/** Check if the direction of start and end tile should be swapped based on
 
 * the dragging-style. Default directions are:
 
 * in the case of a line (HT_RAIL, HT_LINE):  DIR_NE, DIR_NW, DIR_N, DIR_E
 
 * in the case of a rect (HT_RECT, HT_POINT): DIR_S, DIR_E
 
 * For example dragging a rectangle area from south to north should be swapped to
 
 * north-south (DIR_S) to obtain the same results with less code. This is what
 
 * the return value signifies.
 
 * @param style HighLightStyle dragging style
 
 * @param start_tile, end_tile start and end tile of drag
 
 * @param boolean value which when true means start/end should be swapped */
 
static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile)
 
{
 
	uint start_x = TileX(start_tile);
 
	uint start_y = TileY(start_tile);
 
	uint end_x = TileX(end_tile);
 
	uint end_y = TileY(end_tile);
 

	
 
	switch (style & HT_DRAG_MASK) {
 
		case HT_RAIL:
 
		case HT_LINE: return (end_x > start_x || (end_x == start_x && end_y > start_y));
 

	
 
		case HT_RECT:
 
		case HT_POINT: return (end_x != start_x && end_y < start_y);
 
		default: NOT_REACHED();
 
	}
 

	
 
	return false;
 
}
 

	
 
/** Calculates height difference between one tile and another
 
* Multiplies the result to suit the standard given by minimap - 50 meters high
 
* To correctly get the height difference we need the direction we are dragging
 
* in, as well as with what kind of tool we are dragging. For example a horizontal
 
* autorail tool that starts in bottom and ends at the top of a tile will need the
 
* maximum of SW,S and SE,N corners respectively. This is handled by the lookup table below
 
* See _tileoffs_by_dir in map.c for the direction enums if you can't figure out
 
* the values yourself.
 
* @param style HightlightStyle of drag. This includes direction and style (autorail, rect, etc.)
 
* @param distance amount of tiles dragged, important for horizontal/vertical drags
 
*        ignored for others
 
* @param start_tile, end_tile start and end tile of drag operation
 
* @return height difference between two tiles. Tile measurement tool utilizes
 
* this value in its tooltips */
 
static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_tile, TileIndex end_tile)
 
{
 
	bool swap = SwapDirection(style, start_tile, end_tile);
 
	byte style_t;
 
	uint h0, h1, ht; // start heigth, end height, and temp variable
 

	
 
	if (start_tile == end_tile) return 0;
 
	if (swap) swap_tile(&start_tile, &end_tile);
 

	
 
	switch (style & HT_DRAG_MASK) {
 
		case HT_RECT: {
 
			static const TileIndexDiffC heightdiff_area_by_dir[] = {
 
				/* Start */ {1, 0}, /* Dragging east */ {0, 0}, /* Dragging south */
 
				/* End   */ {0, 1}, /* Dragging east */ {1, 1}  /* Dragging south */
 
			};
 

	
 
			/* In the case of an area we can determine whether we were dragging south or
 
			 * east by checking the X-coordinates of the tiles */
 
			style_t = (byte)(TileX(end_tile) > TileX(start_tile));
 
			start_tile = TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_area_by_dir[style_t]));
 
			end_tile   = TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_area_by_dir[2 + style_t]));
 
		}
 
		/* Fallthrough */
 
		case HT_POINT:
 
			h0 = TileHeight(start_tile);
 
			h1 = TileHeight(end_tile);
 
			break;
 
		default: { /* All other types, this is mostly only line/autorail */
 
			static const HighLightStyle flip_style_direction[] = {
 
				HT_DIR_X, HT_DIR_Y, HT_DIR_HL, HT_DIR_HU, HT_DIR_VR, HT_DIR_VL
 
			};
 
			static const TileIndexDiffC heightdiff_line_by_dir[] = {
 
				/* Start */ {1, 0}, {1, 1}, /* HT_DIR_X  */ {0, 1}, {1, 1}, /* HT_DIR_Y  */
 
				/* Start */ {1, 0}, {0, 0}, /* HT_DIR_HU */ {1, 0}, {1, 1}, /* HT_DIR_HL */
 
				/* Start */ {1, 0}, {1, 1}, /* HT_DIR_VL */ {0, 1}, {1, 1}, /* HT_DIR_VR */
 

	
 
				/* Start */ {0, 1}, {0, 0}, /* HT_DIR_X  */ {1, 0}, {0, 0}, /* HT_DIR_Y  */
 
				/* End   */ {0, 1}, {0, 0}, /* HT_DIR_HU */ {1, 1}, {0, 1}, /* HT_DIR_HL */
 
				/* End   */ {1, 0}, {0, 0}, /* HT_DIR_VL */ {0, 0}, {0, 1}, /* HT_DIR_VR */
 
			};
 

	
 
			distance %= 2; // we're only interested if the distance is even or uneven
 
			style &= HT_DIR_MASK;
 

	
 
			/* To handle autorail, we do some magic to be able to use a lookup table.
 
			 * Firstly if we drag the other way around, we switch start&end, and if needed
 
			 * also flip the drag-position. Eg if it was on the left, and the distance is even
 
			 * that means the end, which is now the start is on the right */
 
			if (swap && distance == 0) style = flip_style_direction[style];
 

	
 
			/* Use lookup table for start-tile based on HighLightStyle direction */
 
			style_t = style * 2;
 
			assert(style_t < lengthof(heightdiff_line_by_dir) - 13);
 
			h0 = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t])));
 
			ht = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t + 1])));
 
			h0 = maxu(h0, ht);
 

	
 
			/* Use lookup table for end-tile based on HighLightStyle direction
 
			 * flip around side (lower/upper, left/right) based on distance */
 
			if (distance == 0) style_t = flip_style_direction[style] * 2;
 
			assert(style_t < lengthof(heightdiff_line_by_dir) - 13);
 
			h1 = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t])));
 
			ht = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t + 1])));
 
			h1 = maxu(h1, ht);
 
		} break;
 
	}
 

	
 
	if (swap) swap_uint32(&h0, &h1);
 
	/* Minimap shows height in intervals of 50 meters, let's do the same */
 
	return (int)(h1 - h0) * 50;
 
}
 

	
 
static const StringID measure_strings_length[] = {STR_NULL, STR_MEASURE_LENGTH, STR_MEASURE_LENGTH_HEIGHTDIFF};
 

	
 
// while dragging
 
static void CalcRaildirsDrawstyle(TileHighlightData *thd, int x, int y, int method)
 
{
 
	HighLightStyle b;
 
	uint w, h;
 

	
 
	int dx = thd->selstart.x - (thd->selend.x & ~0xF);
 
	int dy = thd->selstart.y - (thd->selend.y & ~0xF);
 
	w = myabs(dx) + 16;
 
	h = myabs(dy) + 16;
 

	
 
	if (TileVirtXY(thd->selstart.x, thd->selstart.y) == TileVirtXY(x, y)) { // check if we're only within one tile
 
		if (method == VPM_RAILDIRS) {
 
			b = GetAutorailHT(x, y);
 
		} else { // rect for autosignals on one tile
 
			b = HT_RECT;
 
		}
 
	} else if (h == 16) { // Is this in X direction?
 
		if (dx == 16) { // 2x1 special handling
 
			b = (Check2x1AutoRail(3)) | HT_LINE;
 
		} else if (dx == -16) {
 
			b = (Check2x1AutoRail(2)) | HT_LINE;
 
		} else {
 
			b = HT_LINE | HT_DIR_X;
 
		}
 
		y = thd->selstart.y;
 
	} else if (w == 16) { // Or Y direction?
 
		if (dy == 16) { // 2x1 special handling
 
			b = (Check2x1AutoRail(1)) | HT_LINE;
 
		} else if (dy == -16) { // 2x1 other direction
 
			b = (Check2x1AutoRail(0)) | HT_LINE;
 
		} else {
 
			b = HT_LINE | HT_DIR_Y;
 
		}
 
		x = thd->selstart.x;
 
	} else if (w > h * 2) { // still count as x dir?
 
		b = HT_LINE | HT_DIR_X;
 
		y = thd->selstart.y;
 
	} else if (h > w * 2) { // still count as y dir?
 
		b = HT_LINE | HT_DIR_Y;
 
		x = thd->selstart.x;
 
	} else { // complicated direction
 
		int d = w - h;
 
		thd->selend.x = thd->selend.x & ~0xF;
 
		thd->selend.y = thd->selend.y & ~0xF;
 

	
 
		// four cases.
 
		if (x > thd->selstart.x) {
 
			if (y > thd->selstart.y) {
 
				// south
 
				if (d == 0) {
 
					b = (x & 0xF) > (y & 0xF) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
 
				} else if (d >= 0) {
 
					x = thd->selstart.x + h;
 
					b = HT_LINE | HT_DIR_VL;
 
					// return px == py || px == py + 16;
 
				} else {
 
					y = thd->selstart.y + w;
 
					b = HT_LINE | HT_DIR_VR;
 
				} // return px == py || px == py - 16;
 
			} else {
 
				// west
 
				if (d == 0) {
 
					b = (x & 0xF) + (y & 0xF) >= 0x10 ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
 
				} else if (d >= 0) {
 
					x = thd->selstart.x + h;
 
					b = HT_LINE | HT_DIR_HL;
 
				} else {
 
					y = thd->selstart.y - w;
 
					b = HT_LINE | HT_DIR_HU;
 
				}
 
			}
 
		} else {
 
			if (y > thd->selstart.y) {
 
				// east
 
				if (d == 0) {
 
					b = (x & 0xF) + (y & 0xF) >= 0x10 ? HT_LINE | HT_DIR_HL : HT_LINE | HT_DIR_HU;
 
				} else if (d >= 0) {
 
					x = thd->selstart.x - h;
 
					b = HT_LINE | HT_DIR_HU;
 
					// return px == -py || px == -py - 16;
 
				} else {
 
					y = thd->selstart.y + w;
 
					b = HT_LINE | HT_DIR_HL;
 
				} // return px == -py || px == -py + 16;
 
			} else {
 
				// north
 
				if (d == 0) {
 
					b = (x & 0xF) > (y & 0xF) ? HT_LINE | HT_DIR_VL : HT_LINE | HT_DIR_VR;
 
				} else if (d >= 0) {
 
					x = thd->selstart.x - h;
 
					b = HT_LINE | HT_DIR_VR;
 
					// return px == py || px == py - 16;
 
				} else {
 
					y = thd->selstart.y - w;
 
					b = HT_LINE | HT_DIR_VL;
 
				} //return px == py || px == py + 16;
 
			}
 
		}
 
	}
 

	
 
	if (_patches.measure_tooltip) {
 
		TileIndex t0 = TileVirtXY(thd->selstart.x, thd->selstart.y);
 
		TileIndex t1 = TileVirtXY(x, y);
 
		uint distance = DistanceManhattan(t0, t1) + 1;
 
		byte index = 0;
 
		uint params[2];
 

	
 
		if (distance != 1) {
 
			int heightdiff = CalcHeightdiff(b, distance, t0, t1);
 
			/* If we are showing a tooltip for horizontal or vertical drags,
 
			 * 2 tiles have a length of 1. To bias towards the ceiling we add
 
			 * one before division. It feels more natural to count 3 lengths as 2 */
 
			if ((b & HT_DIR_MASK) != HT_DIR_X && (b & HT_DIR_MASK) != HT_DIR_Y) {
 
				distance = (distance + 1) / 2;
 
			}
 

	
 
			params[index++] = distance;
 
			if (heightdiff != 0) params[index++] = heightdiff;
 
		}
 

	
 
		GuiShowTooltipsWithArgs(measure_strings_length[index], index, params);
 
	}
 

	
 
	thd->selend.x = x;
 
	thd->selend.y = y;
 
	thd->next_drawstyle = b;
 
}
 

	
 
/**
 
 * Selects tiles while dragging
 
 * @param x X coordinate of end of selection
 
 * @param y Y coordinate of end of selection
 
 * @param method modifies the way tiles are selected. Possible
 
 * methods are VPM_* in viewport.h */
 
void VpSelectTilesWithMethod(int x, int y, int method)
 
{
 
	int sx, sy;
 
	HighLightStyle style;
 

	
 
	if (x == -1) {
 
		_thd.selend.x = -1;
 
		return;
 
	}
 

	
 
	/* Special handling of drag in any (8-way) direction */
 
	if (method == VPM_RAILDIRS || method == VPM_SIGNALDIRS) {
 
		_thd.selend.x = x;
 
		_thd.selend.y = y;
 
		CalcRaildirsDrawstyle(&_thd, x, y, method);
 
		return;
 
	}
 

	
 
	if (_thd.next_drawstyle == HT_POINT) {
 
		x += TILE_SIZE / 2;
 
		y += TILE_SIZE / 2;
 
	}
 

	
 
	sx = _thd.selstart.x;
 
	sy = _thd.selstart.y;
 

	
 
	switch (method) {
 
		case VPM_X_OR_Y: /* drag in X or Y direction */
 
			if (myabs(sy - y) < myabs(sx - x)) {
 
				y = sy;
 
				style = HT_DIR_X;
 
			} else {
 
				x = sx;
 
				style = HT_DIR_Y;
 
			}
 
			goto calc_heightdiff_single_direction;
 
		case VPM_FIX_X: /* drag in Y direction */
 
			x = sx;
 
			style = HT_DIR_Y;
 
			goto calc_heightdiff_single_direction;
 
		case VPM_FIX_Y: /* drag in X direction */
 
			y = sy;
 
			style = HT_DIR_X;
 

	
 
calc_heightdiff_single_direction:;
 
			if (_patches.measure_tooltip) {
 
				TileIndex t0 = TileVirtXY(sx, sy);
 
				TileIndex t1 = TileVirtXY(x, y);
 
				uint distance = DistanceManhattan(t0, t1) + 1;
 
				byte index = 0;
 
				uint params[2];
 

	
 
				if (distance != 1) {
 
					/* With current code passing a HT_LINE style to calculate the height
 
					 * difference is enough. However if/when a point-tool is created
 
					 * with this method, function should be called with new_style (below)
 
					 * instead of HT_LINE | style case HT_POINT is handled specially
 
					 * new_style := (_thd.next_drawstyle & HT_RECT) ? HT_LINE | style : _thd.next_drawstyle; */
 
					int heightdiff = CalcHeightdiff(HT_LINE | style, 0, t0, t1);
 

	
 
					params[index++] = distance;
 
					if (heightdiff != 0) params[index++] = heightdiff;
 
				}
 

	
 
				GuiShowTooltipsWithArgs(measure_strings_length[index], index, params);
 
			} break;
 

	
 
		case VPM_X_AND_Y_LIMITED: { /* drag an X by Y constrained rect area */
 
			int limit = (_thd.sizelimit - 1) * TILE_SIZE;
 
			x = sx + clamp(x - sx, -limit, limit);
 
			y = sy + clamp(y - sy, -limit, limit);
 
			/* Fallthrough */
 
		case VPM_X_AND_Y: /* drag an X by Y area */
 
			if (_patches.measure_tooltip) {
 
				static const StringID measure_strings_area[] = {
 
					STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF
 
				};
 

	
 
				TileIndex t0 = TileVirtXY(sx, sy);
 
				TileIndex t1 = TileVirtXY(x, y);
 
				uint dx = abs(TileX(t0) - TileX(t1)) + 1;
 
				uint dy = abs(TileY(t0) - TileY(t1)) + 1;
 
				byte index = 0;
 
				uint params[3];
 

	
 
				/* If dragging an area (eg dynamite tool) and it is actually a single
 
				 * row/column, change the type to 'line' to get proper calculation for height */
 
				style = _thd.next_drawstyle;
 
				if (style & HT_RECT) {
 
					if (dx == 1) {
 
						style = HT_LINE | HT_DIR_Y;
 
					} else if (dy == 1) {
 
						style = HT_LINE | HT_DIR_X;
 
					}
 
				}
 

	
 
				if (dx != 1 || dy != 1) {
 
					int heightdiff = CalcHeightdiff(style, 0, t0, t1);
 

	
 
					params[index++] = dx;
 
					params[index++] = dy;
 
					if (heightdiff != 0) params[index++] = heightdiff;
 
				}
 

	
 
				GuiShowTooltipsWithArgs(measure_strings_area[index], index, params);
 
			}
 
		break;
 

	
 
		}
 
		default: NOT_REACHED();
 
	}
 

	
 
	_thd.selend.x = x;
 
	_thd.selend.y = y;
 
}
 

	
 
// while dragging
 
bool VpHandlePlaceSizingDrag(void)
 
{
 
	Window *w;
 
	WindowEvent e;
 

	
 
	if (_special_mouse_mode != WSM_SIZING) return true;
 

	
 
	e.we.place.userdata = _thd.userdata;
 

	
 
	// stop drag mode if the window has been closed
 
	w = FindWindowById(_thd.window_class,_thd.window_number);
 
	if (w == NULL) {
 
		ResetObjectToPlace();
 
		return false;
 
	}
 

	
 
	// while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() )
 
	if (_left_button_down) {
 
		e.event = WE_PLACE_DRAG;
 
		e.we.place.pt = GetTileBelowCursor();
 
		w->wndproc(w, &e);
 
		return false;
 
	}
 

	
 
	// mouse button released..
 
	// keep the selected tool, but reset it to the original mode.
 
	_special_mouse_mode = WSM_NONE;
 
	if (_thd.next_drawstyle == HT_RECT) {
 
		_thd.place_mode = VHM_RECT;
 
	} else if ((e.we.place.userdata & 0xF) == VPM_SIGNALDIRS) { // some might call this a hack... -- Dominik
 
		_thd.place_mode = VHM_RECT;
 
	} else if (_thd.next_drawstyle & HT_LINE) {
 
		_thd.place_mode = VHM_RAIL;
 
	} else if (_thd.next_drawstyle & HT_RAIL) {
 
		_thd.place_mode = VHM_RAIL;
 
	} else {
 
		_thd.place_mode = VHM_POINT;
 
	}
 
	SetTileSelectSize(1, 1);
 

	
 
	// and call the mouseup event.
 
	e.event = WE_PLACE_MOUSEUP;
 
	e.we.place.pt = _thd.selend;
 
	e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y);
 
	e.we.place.starttile = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
 
	w->wndproc(w, &e);
 

	
 
	return false;
 
}
 

	
 
void SetObjectToPlaceWnd(CursorID icon, byte mode, Window *w)
 
{
 
	SetObjectToPlace(icon, mode, w->window_class, w->window_number);
 
}
 

	
 
#include "table/animcursors.h"
 

	
 
void SetObjectToPlace(CursorID icon, byte mode, WindowClass window_class, WindowNumber window_num)
 
{
 
	Window *w;
 

	
 
	// undo clicking on button
 
	if (_thd.place_mode != 0) {
 
		_thd.place_mode = 0;
 
		w = FindWindowById(_thd.window_class, _thd.window_number);
 
		if (w != NULL) CallWindowEventNP(w, WE_ABORT_PLACE_OBJ);
 
	}
 

	
 
	SetTileSelectSize(1, 1);
 

	
 
	_thd.make_square_red = false;
 

	
 
	if (mode == VHM_DRAG) { // mode 4 is for dragdropping trains in the depot window
 
		mode = 0;
 
		_special_mouse_mode = WSM_DRAGDROP;
 
	} else {
 
		_special_mouse_mode = WSM_NONE;
 
	}
 

	
 
	_thd.place_mode = mode;
 
	_thd.window_class = window_class;
 
	_thd.window_number = window_num;
 

	
 
	if (mode == VHM_SPECIAL) // special tools, like tunnels or docks start with presizing mode
 
		VpStartPreSizing();
 

	
 
	if ( (int)icon < 0)
 
		SetAnimatedMouseCursor(_animcursors[~icon]);
 
	else
 
		SetMouseCursor(icon);
 
}
 

	
 
void ResetObjectToPlace(void)
 
{
 
	SetObjectToPlace(SPR_CURSOR_MOUSE, VHM_NONE, 0, 0);
 
}
src/water_cmd.c
Show inline comments
 
deleted file
src/water_cmd.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "bridge_map.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 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 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));
 
	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, p1);
 
		MakeShipDepot(tile2,_current_player, DEPOT_SOUTH, p1);
 
		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));
 
	MarkTileDirtyByTile(TILE_ADDXY(tile, 1, 0));
 
	MarkTileDirtyByTile(TILE_ADDXY(tile, -1, 0));
 
}
 

	
 
/** Builds a lock (ship-lift)
 
 * @param tile tile where to place the lock
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdBuildLock(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	DiagDirection dir;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	switch (GetTileSlope(tile, NULL)) {
 
		case SLOPE_SW: dir = DIAGDIR_SW; break;
 
		case SLOPE_SE: dir = DIAGDIR_SE; break;
 
		case SLOPE_NW: dir = DIAGDIR_NW; break;
 
		case SLOPE_NE: dir = DIAGDIR_NE; break;
 
		default: return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
 
	}
 
	return DoBuildShiplift(tile, dir, flags);
 
}
 

	
 
/** Build a piece of canal.
 
 * @param tile end tile of stretch-dragging
 
 * @param p1 start tile of stretch-dragging
 
 * @param p2 ctrl pressed - toggles ocean / canals at sealevel
 
 */
 
int32 CmdBuildCanal(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 cost;
 
	int size_x, size_y;
 
	int x;
 
	int y;
 
	int sx, sy;
 

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

	
 
	x = TileX(tile);
 
	y = TileY(tile);
 
	sx = TileX(p1);
 
	sy = TileY(p1);
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (x < sx) intswap(x, sx);
 
	if (y < sy) intswap(y, sy);
 
	size_x = (x - sx) + 1;
 
	size_y = (y - sy) + 1;
 

	
 
	/* Outside the editor you can only drag canals, and not areas */
 
	if (_game_mode != GM_EDITOR && (sx != x && sy != y)) return CMD_ERROR;
 

	
 
	cost = 0;
 
	BEGIN_TILE_LOOP(tile, size_x, size_y, TileXY(sx, sy)) {
 
		int32 ret;
 

	
 
		if (GetTileSlope(tile, NULL) != SLOPE_FLAT) {
 
			return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
 
		}
 

	
 
		// can't make water of water!
 
		if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || HASBIT(p2, 0))) continue;
 

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

	
 
		if (flags & DC_EXEC) {
 
			if (TileHeight(tile) == 0 && HASBIT(p2, 0)) {
 
				MakeWater(tile);
 
			} else {
 
				MakeCanal(tile, _current_player);
 
			}
 
			MarkTileDirtyByTile(tile);
 
			MarkTilesAroundDirty(tile);
 
		}
 

	
 
		cost += _price.clear_water;
 
	} END_TILE_LOOP(tile, size_x, size_y, 0);
 

	
 
	if (cost == 0) {
 
		return_cmd_error(STR_1007_ALREADY_BUILT);
 
	} else {
 
		return cost;
 
	}
 
}
 

	
 
static int32 ClearTile_Water(TileIndex tile, byte flags)
 
{
 
	switch (GetWaterTileType(tile)) {
 
		case WATER_CLEAR:
 
			if (flags & DC_NO_WATER) return_cmd_error(STR_3807_CAN_T_BUILD_ON_WATER);
 

	
 
			// Make sure no vehicle is on the tile
 
			if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
			// Make sure it's not an edge tile.
 
			if (!IS_INT_INSIDE(TileX(tile), 1, MapMaxX() - 1) ||
 
					!IS_INT_INSIDE(TileY(tile), 1, MapMaxY() - 1)) {
 
				return_cmd_error(STR_0002_TOO_CLOSE_TO_EDGE_OF_MAP);
 
			}
 

	
 
			if (GetTileOwner(tile) != OWNER_WATER && !CheckTileOwnership(tile)) return CMD_ERROR;
 

	
 
			if (flags & DC_EXEC) DoClearSquare(tile);
 
			return _price.clear_water;
 

	
 
		case WATER_COAST: {
 
			Slope slope = GetTileSlope(tile, NULL);
 

	
 
			// Make sure no vehicle is on the tile
 
			if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
			// Make sure it's not an edge tile.
 
			if (!IS_INT_INSIDE(TileX(tile), 1, MapMaxX() - 1) ||
 
					!IS_INT_INSIDE(TileY(tile), 1, MapMaxY() - 1)) {
 
				return_cmd_error(STR_0002_TOO_CLOSE_TO_EDGE_OF_MAP);
 
			}
 

	
 
			if (flags & DC_EXEC) DoClearSquare(tile);
 
			if (slope == SLOPE_N || slope == SLOPE_E || slope == SLOPE_S || slope == SLOPE_W) {
 
				return _price.clear_water;
 
			} else {
 
				return _price.purchase_land;
 
			}
 
		}
 

	
 
		case WATER_LOCK: {
 
			static const TileIndexDiffC _shiplift_tomiddle_offs[] = {
 
				{ 0,  0}, {0,  0}, { 0, 0}, {0,  0}, // middle
 
				{-1,  0}, {0,  1}, { 1, 0}, {0, -1}, // lower
 
				{ 1,  0}, {0, -1}, {-1, 0}, {0,  1}, // upper
 
			};
 

	
 
			if (flags & DC_AUTO) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);
 
			if (_current_player == OWNER_WATER) return CMD_ERROR;
 
			// move to the middle tile..
 
			return RemoveShiplift(tile + ToTileIndexDiff(_shiplift_tomiddle_offs[GetSection(tile)]), flags);
 
		}
 

	
 
		case WATER_DEPOT:
 
			if (flags & DC_AUTO) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);
 
			return RemoveShipDepot(tile, flags);
 

	
 
		default:
 
			NOT_REACHED();
 
			return 0;
 
	}
 

	
 
	return 0; // useless but silences warning
 
}
 

	
 
// return true if a tile is a water tile.
 
static bool IsWateredTile(TileIndex tile)
 
{
 
	switch (GetTileType(tile)) {
 
		case MP_WATER:
 
			if (!IsCoast(tile)) return true;
 

	
 
			switch (GetTileSlope(tile, NULL)) {
 
				case SLOPE_W:
 
				case SLOPE_S:
 
				case SLOPE_E:
 
				case SLOPE_N:
 
					return true;
 

	
 
				default:
 
					return false;
 
			}
 

	
 
		case MP_STATION: return IsOilRig(tile) || IsDock(tile) || IsBuoy_(tile);
 
		default:         return false;
 
	}
 
}
 

	
 
// draw a canal styled water tile with dikes around
 
void DrawCanalWater(TileIndex tile)
 
{
 
	uint wa;
 

	
 
	// determine the edges around with water.
 
	wa = IsWateredTile(TILE_ADDXY(tile, -1, 0)) << 0;
 
	wa += IsWateredTile(TILE_ADDXY(tile, 0, 1)) << 1;
 
	wa += IsWateredTile(TILE_ADDXY(tile, 1, 0)) << 2;
 
	wa += IsWateredTile(TILE_ADDXY(tile, 0, -1)) << 3;
 

	
 
	if (!(wa & 1)) DrawGroundSprite(SPR_CANALS_BASE + 57);
 
	if (!(wa & 2)) DrawGroundSprite(SPR_CANALS_BASE + 58);
 
	if (!(wa & 4)) DrawGroundSprite(SPR_CANALS_BASE + 59);
 
	if (!(wa & 8)) DrawGroundSprite(SPR_CANALS_BASE + 60);
 

	
 
	// right corner
 
	switch (wa & 0x03) {
 
		case 0: DrawGroundSprite(SPR_CANALS_BASE + 57 + 4); break;
 
		case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1))) DrawGroundSprite(SPR_CANALS_BASE + 57 + 8); break;
 
	}
 

	
 
	// bottom corner
 
	switch (wa & 0x06) {
 
		case 0: DrawGroundSprite(SPR_CANALS_BASE + 57 + 5); break;
 
		case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1))) DrawGroundSprite(SPR_CANALS_BASE + 57 + 9); break;
 
	}
 

	
 
	// left corner
 
	switch (wa & 0x0C) {
 
		case  0: DrawGroundSprite(SPR_CANALS_BASE + 57 + 6); break;
 
		case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1))) DrawGroundSprite(SPR_CANALS_BASE + 57 + 10); break;
 
	}
 

	
 
	// upper corner
 
	switch (wa & 0x09) {
 
		case 0: DrawGroundSprite(SPR_CANALS_BASE + 57 + 7); break;
 
		case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1))) DrawGroundSprite(SPR_CANALS_BASE + 57 + 11); break;
 
	}
 
}
 

	
 
typedef struct LocksDrawTileStruct {
 
	int8 delta_x, delta_y, delta_z;
 
	byte width, height, depth;
 
	SpriteID image;
 
} LocksDrawTileStruct;
 

	
 
#include "table/water_land.h"
 

	
 
static void DrawWaterStuff(const TileInfo *ti, const WaterDrawTileStruct *wdts,
 
	uint32 palette, uint base
 
)
 
{
 
	DrawGroundSprite(wdts++->image);
 

	
 
	for (; wdts->delta_x != 0x80; wdts++) {
 
		uint32 image = wdts->image + base;
 
		if (_display_opt & DO_TRANS_BUILDINGS) {
 
			MAKE_TRANSPARENT(image);
 
		} else {
 
			image |= palette;
 
		}
 
		AddSortableSpriteToDraw(image, ti->x + wdts->delta_x, ti->y + wdts->delta_y, wdts->width, wdts->height, wdts->unk, ti->z + wdts->delta_z);
 
	}
 
}
 

	
 
static void DrawTile_Water(TileInfo *ti)
 
{
 
	switch (GetWaterTileType(ti->tile)) {
 
		case WATER_CLEAR:
 
			DrawGroundSprite(SPR_FLAT_WATER_TILE);
 
			if (ti->z != 0 || !IsTileOwner(ti->tile, OWNER_WATER)) DrawCanalWater(ti->tile);
 
			DrawBridgeMiddle(ti);
 
			break;
 

	
 
		case WATER_COAST:
 
			assert(!IsSteepSlope(ti->tileh));
 
			if (_coast_base != 0) {
 
				DrawGroundSprite(_coast_base + ti->tileh);
 
			} else {
 
				DrawGroundSprite(_water_shore_sprites[ti->tileh]);
 
			}
 
			DrawBridgeMiddle(ti);
 
			break;
 

	
 
		case WATER_LOCK: {
 
			const WaterDrawTileStruct *t = _shiplift_display_seq[GetSection(ti->tile)];
 
			DrawWaterStuff(ti, t, 0, ti->z > t[3].delta_y ? 24 : 0);
 
		} break;
 

	
 
		case WATER_DEPOT:
 
			DrawWaterStuff(ti, _shipdepot_display_seq[GetSection(ti->tile)], PLAYER_SPRITE_COLOR(GetTileOwner(ti->tile)), 0);
 
			break;
 
	}
 
}
 

	
 
void DrawShipDepotSprite(int x, int y, int image)
 
{
 
	const WaterDrawTileStruct *wdts = _shipdepot_display_seq[image];
 

	
 
	DrawSprite(wdts++->image, x, y);
 

	
 
	for (; wdts->delta_x != 0x80; wdts++) {
 
		Point pt = RemapCoords(wdts->delta_x, wdts->delta_y, wdts->delta_z);
 
		DrawSprite(wdts->image + PLAYER_SPRITE_COLOR(_local_player), x + pt.x, y + pt.y);
 
	}
 
}
 

	
 

	
 
static uint GetSlopeZ_Water(TileIndex tile, uint x, uint y)
 
{
 
	uint z;
 
	uint tileh = GetTileSlope(tile, &z);
 

	
 
	return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
 
}
 

	
 
static Slope GetSlopeTileh_Water(TileIndex tile, Slope tileh)
 
{
 
	return tileh;
 
}
 

	
 
static void GetAcceptedCargo_Water(TileIndex tile, AcceptedCargo ac)
 
{
 
	/* not used */
 
}
 

	
 
static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
 
{
 
	switch (GetWaterTileType(tile)) {
 
		case WATER_CLEAR:
 
			if (TilePixelHeight(tile) == 0 || IsTileOwner(tile, OWNER_WATER)) {
 
				td->str = STR_3804_WATER;
 
			} else {
 
				td->str = STR_LANDINFO_CANAL;
 
			}
 
			break;
 
		case WATER_COAST: td->str = STR_3805_COAST_OR_RIVERBANK; break;
 
		case WATER_LOCK : td->str = STR_LANDINFO_LOCK; break;
 
		case WATER_DEPOT: td->str = STR_3806_SHIP_DEPOT; break;
 
		default: assert(0); break;
 
	}
 

	
 
	td->owner = GetTileOwner(tile);
 
}
 

	
 
static void AnimateTile_Water(TileIndex tile)
 
{
 
	/* not used */
 
}
 

	
 
static void TileLoopWaterHelper(TileIndex tile, const TileIndexDiffC *offs)
 
{
 
	TileIndex target = TILE_ADD(tile, ToTileIndexDiff(offs[0]));
 

	
 
	// type of this tile mustn't be water already.
 
	if (IsTileType(target, MP_WATER)) return;
 

	
 
	if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[1]))) != 0 ||
 
			TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[2]))) != 0) {
 
		return;
 
	}
 

	
 
	if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[3]))) != 0 ||
 
			TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[4]))) != 0) {
 
		// make coast..
 
		switch (GetTileType(target)) {
 
			case MP_RAILWAY: {
 
				TrackBits tracks;
 
				Slope slope;
 

	
 
				if (!IsPlainRailTile(target)) break;
 

	
 
				tracks = GetTrackBits(target);
 
				slope = GetTileSlope(target, NULL);
 
				if (!(
 
							(slope == SLOPE_W && tracks == TRACK_BIT_RIGHT) ||
 
							(slope == SLOPE_S && tracks == TRACK_BIT_UPPER) ||
 
							(slope == SLOPE_E && tracks == TRACK_BIT_LEFT)  ||
 
							(slope == SLOPE_N && tracks == TRACK_BIT_LOWER)
 
						)) {
 
					break;
 
				}
 
			}
 
			/* FALLTHROUGH */
 

	
 
			case MP_CLEAR:
 
			case MP_TREES:
 
				_current_player = OWNER_WATER;
 
				if (!CmdFailed(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
 
					MakeShore(target);
 
					MarkTileDirtyByTile(target);
 
				}
 
				break;
 

	
 
			default:
 
				break;
 
		}
 
	} else {
 
		_current_player = OWNER_WATER;
 
		{
 
			Vehicle *v = FindVehicleOnTileZ(target, 0);
 
			if (v != NULL) FloodVehicle(v);
 
		}
 

	
 
		if (!CmdFailed(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
 
			MakeWater(target);
 
			MarkTileDirtyByTile(target);
 
		}
 
	}
 
}
 

	
 
static void FloodVehicle(Vehicle *v)
 
{
 
	if (!(v->vehstatus & VS_CRASHED)) {
 
		uint16 pass = 0;
 

	
 
		if (v->type == VEH_Road) { // flood bus/truck
 
			pass = 1; // driver
 
			if (v->cargo_type == CT_PASSENGERS)
 
				pass += v->cargo_count;
 

	
 
			v->vehstatus |= VS_CRASHED;
 
			v->u.road.crashed_ctr = 2000; // max 2220, disappear pretty fast
 
			RebuildVehicleLists();
 
		} else if (v->type == VEH_Train) {
 
			Vehicle *u;
 

	
 
			v = GetFirstVehicleInChain(v);
 
			u = v;
 
			if (IsFrontEngine(v)) pass = 4; // driver
 

	
 
			// crash all wagons, and count passangers
 
			BEGIN_ENUM_WAGONS(v)
 
				if (v->cargo_type == CT_PASSENGERS) pass += v->cargo_count;
 
				v->vehstatus |= VS_CRASHED;
 
			END_ENUM_WAGONS(v)
 

	
 
			v = u;
 
			v->u.rail.crash_anim_pos = 4000; // max 4440, disappear pretty fast
 
			RebuildVehicleLists();
 
		} else {
 
			return;
 
		}
 

	
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 

	
 
		SetDParam(0, pass);
 
		AddNewsItem(STR_B006_FLOOD_VEHICLE_DESTROYED,
 
			NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
 
			v->index,
 
			0);
 
		CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
 
		SndPlayVehicleFx(SND_12_EXPLOSION, v);
 
	}
 
}
 

	
 
// called from tunnelbridge_cmd, and by TileLoop_Industry()
 
void TileLoop_Water(TileIndex tile)
 
{
 
	static const TileIndexDiffC _tile_loop_offs_array[][5] = {
 
		// tile to mod              shore?    shore?
 
		{{-1,  0}, {0, 0}, {0, 1}, {-1,  0}, {-1,  1}},
 
		{{ 0,  1}, {0, 1}, {1, 1}, { 0,  2}, { 1,  2}},
 
		{{ 1,  0}, {1, 0}, {1, 1}, { 2,  0}, { 2,  1}},
 
		{{ 0, -1}, {0, 0}, {1, 0}, { 0, -1}, { 1, -1}}
 
	};
 

	
 
	/* Ensure sea-level canals do not flood */
 
	if ((IsTileType(tile, MP_WATER) || IsTileType(tile, MP_TUNNELBRIDGE)) &&
 
			!IsTileOwner(tile, OWNER_WATER)) return;
 

	
 
	if (IS_INT_INSIDE(TileX(tile), 1, MapSizeX() - 3 + 1) &&
 
			IS_INT_INSIDE(TileY(tile), 1, MapSizeY() - 3 + 1)) {
 
		uint i;
 

	
 
		for (i = 0; i != lengthof(_tile_loop_offs_array); i++) {
 
			TileLoopWaterHelper(tile, _tile_loop_offs_array[i]);
 
		}
 
	}
 
	// _current_player can be changed by TileLoopWaterHelper.. reset it back
 
	//   here
 
	_current_player = OWNER_NONE;
 

	
 
	// edges
 
	if (TileX(tile) == 0 && IS_INT_INSIDE(TileY(tile), 1, MapSizeY() - 3 + 1)) { //NE
 
		TileLoopWaterHelper(tile, _tile_loop_offs_array[2]);
 
	}
 

	
 
	if (TileX(tile) == MapSizeX() - 2 && IS_INT_INSIDE(TileY(tile), 1, MapSizeY() - 3 + 1)) { //SW
 
		TileLoopWaterHelper(tile, _tile_loop_offs_array[0]);
 
	}
 

	
 
	if (TileY(tile) == 0 && IS_INT_INSIDE(TileX(tile), 1, MapSizeX() - 3 + 1)) { //NW
 
		TileLoopWaterHelper(tile, _tile_loop_offs_array[1]);
 
	}
 

	
 
	if (TileY(tile) == MapSizeY() - 2 && IS_INT_INSIDE(TileX(tile), 1, MapSizeX() - 3 + 1)) { //SE
 
		TileLoopWaterHelper(tile, _tile_loop_offs_array[3]);
 
	}
 
}
 

	
 
static uint32 GetTileTrackStatus_Water(TileIndex tile, TransportType mode)
 
{
 
	static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
 

	
 
	TrackBits ts;
 

	
 
	if (mode != TRANSPORT_WATER) return 0;
 

	
 
	switch (GetWaterTileType(tile)) {
 
		case WATER_CLEAR: ts = TRACK_BIT_ALL; break;
 
		case WATER_COAST: ts = coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break;
 
		case WATER_LOCK:  ts = AxisToTrackBits(DiagDirToAxis(GetLockDirection(tile))); break;
 
		case WATER_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
 
		default: return 0;
 
	}
 
	if (TileX(tile) == 0) {
 
		// NE border: remove tracks that connects NE tile edge
 
		ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
 
	}
 
	if (TileY(tile) == 0) {
 
		// NW border: remove tracks that connects NW tile edge
 
		ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
 
	}
 
	return ts * 0x101;
 
}
 

	
 
static void ClickTile_Water(TileIndex tile)
 
{
 
	if (GetWaterTileType(tile) == WATER_DEPOT) {
 
		TileIndex tile2 = GetOtherShipDepotTile(tile);
 

	
 
		ShowDepotWindow(tile < tile2 ? tile : tile2, VEH_Ship);
 
	}
 
}
 

	
 
static void ChangeTileOwner_Water(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	if (!IsTileOwner(tile, old_player)) return;
 

	
 
	if (new_player != PLAYER_SPECTATOR) {
 
		SetTileOwner(tile, new_player);
 
	} else {
 
		DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
	}
 
}
 

	
 
static uint32 VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
 
{
 
	return 0;
 
}
 

	
 

	
 
const TileTypeProcs _tile_type_water_procs = {
 
	DrawTile_Water,           /* draw_tile_proc */
 
	GetSlopeZ_Water,          /* get_slope_z_proc */
 
	ClearTile_Water,          /* clear_tile_proc */
 
	GetAcceptedCargo_Water,   /* get_accepted_cargo_proc */
 
	GetTileDesc_Water,        /* get_tile_desc_proc */
 
	GetTileTrackStatus_Water, /* get_tile_track_status_proc */
 
	ClickTile_Water,          /* click_tile_proc */
 
	AnimateTile_Water,        /* animate_tile_proc */
 
	TileLoop_Water,           /* tile_loop_clear */
 
	ChangeTileOwner_Water,    /* change_tile_owner_clear */
 
	NULL,                     /* get_produced_cargo_proc */
 
	VehicleEnter_Water,       /* vehicle_enter_tile_proc */
 
	GetSlopeTileh_Water,      /* get_slope_tileh_proc */
 
};
src/waypoint.c
Show inline comments
 
deleted file
src/waypoint.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 

	
 
#include "command.h"
 
#include "functions.h"
 
#include "gfx.h"
 
#include "map.h"
 
#include "order.h"
 
#include "rail_map.h"
 
#include "bridge_map.h"
 
#include "saveload.h"
 
#include "station.h"
 
#include "tile.h"
 
#include "town.h"
 
#include "waypoint.h"
 
#include "variables.h"
 
#include "table/strings.h"
 
#include "vehicle.h"
 
#include "yapf/yapf.h"
 
#include "date.h"
 

	
 
enum {
 
	MAX_WAYPOINTS_PER_TOWN = 64,
 
};
 

	
 
/**
 
 * Called if a new block is added to the waypoint-pool
 
 */
 
static void WaypointPoolNewBlock(uint start_item)
 
{
 
	Waypoint *wp;
 

	
 
	/* 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 (wp = GetWaypoint(start_item); wp != NULL; wp = (wp->index + 1U < GetWaypointPoolSize()) ? GetWaypoint(wp->index + 1U) : NULL) wp->index = start_item++;
 
}
 

	
 
DEFINE_OLD_POOL(Waypoint, Waypoint, WaypointPoolNewBlock, NULL)
 

	
 
/* Create a new waypoint */
 
static Waypoint* AllocateWaypoint(void)
 
{
 
	Waypoint *wp;
 

	
 
	/* 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 (wp = GetWaypoint(0); wp != NULL; wp = (wp->index + 1U < GetWaypointPoolSize()) ? GetWaypoint(wp->index + 1U) : NULL) {
 
		if (!IsValidWaypoint(wp)) {
 
			uint index = wp->index;
 

	
 
			memset(wp, 0, sizeof(*wp));
 
			wp->index = index;
 

	
 
			return wp;
 
		}
 
	}
 

	
 
	/* Check if we can add a block to the pool */
 
	if (AddBlockToPool(&_Waypoint_pool)) return AllocateWaypoint();
 

	
 
	return NULL;
 
}
 

	
 
/* Update the sign for the waypoint */
 
static void UpdateWaypointSign(Waypoint* wp)
 
{
 
	Point pt = RemapCoords2(TileX(wp->xy) * TILE_SIZE, TileY(wp->xy) * TILE_SIZE);
 
	SetDParam(0, wp->index);
 
	UpdateViewportSignPos(&wp->sign, pt.x, pt.y - 0x20, STR_WAYPOINT_VIEWPORT);
 
}
 

	
 
/* Redraw the sign of a waypoint */
 
static void RedrawWaypointSign(const Waypoint* wp)
 
{
 
	MarkAllViewportsDirty(
 
		wp->sign.left - 6,
 
		wp->sign.top,
 
		wp->sign.left + (wp->sign.width_1 << 2) + 12,
 
		wp->sign.top + 48);
 
}
 

	
 
/* Update all signs */
 
void UpdateAllWaypointSigns(void)
 
{
 
	Waypoint *wp;
 

	
 
	FOR_ALL_WAYPOINTS(wp) {
 
		UpdateWaypointSign(wp);
 
	}
 
}
 

	
 
/* Internal handler to delete a waypoint */
 
void DestroyWaypoint(Waypoint *wp)
 
{
 
	RemoveOrderFromAllVehicles(OT_GOTO_WAYPOINT, wp->index);
 

	
 
	if (wp->string != STR_NULL) DeleteName(wp->string);
 

	
 
	RedrawWaypointSign(wp);
 
}
 

	
 
/* Set the default name for a waypoint */
 
static void MakeDefaultWaypointName(Waypoint* wp)
 
{
 
	Waypoint *local_wp;
 
	bool used_waypoint[MAX_WAYPOINTS_PER_TOWN];
 
	int i;
 

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

	
 
	memset(used_waypoint, 0, sizeof(used_waypoint));
 

	
 
	/* Find an unused waypoint number belonging to this town */
 
	FOR_ALL_WAYPOINTS(local_wp) {
 
		if (wp == local_wp) continue;
 

	
 
		if (local_wp->xy && local_wp->string == STR_NULL && local_wp->town_index == wp->town_index)
 
			used_waypoint[local_wp->town_cn] = true;
 
	}
 

	
 
	/* Find an empty spot */
 
	for (i = 0; used_waypoint[i] && i < MAX_WAYPOINTS_PER_TOWN; i++) {}
 

	
 
	wp->string = STR_NULL;
 
	wp->town_cn = i;
 
}
 

	
 
/* Find a deleted waypoint close to a tile. */
 
static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile)
 
{
 
	Waypoint *wp, *best = NULL;
 
	uint thres = 8;
 

	
 
	FOR_ALL_WAYPOINTS(wp) {
 
		if (wp->deleted) {
 
			uint cur_dist = DistanceManhattan(tile, wp->xy);
 

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

	
 
	return best;
 
}
 

	
 
/**
 
 * Update waypoint graphics id against saved GRFID/localidx.
 
 * This is to ensure the chosen graphics are correct if GRF files are changed.
 
 */
 
void AfterLoadWaypoints(void)
 
{
 
	Waypoint *wp;
 

	
 
	FOR_ALL_WAYPOINTS(wp) {
 
		uint i;
 

	
 
		if (wp->grfid == 0) continue;
 

	
 
		for (i = 0; i < GetNumCustomStations(STAT_CLASS_WAYP); i++) {
 
			const StationSpec *statspec = GetCustomStationSpec(STAT_CLASS_WAYP, i);
 
			if (statspec != NULL && statspec->grfid == wp->grfid && statspec->localidx == wp->localidx) {
 
				wp->stat_id = i;
 
				break;
 
			}
 
		}
 
	}
 
}
 

	
 
/** Convert existing rail to waypoint. Eg build a waypoint station over
 
 * piece of rail
 
 * @param tile tile where waypoint will be built
 
 * @param p1 graphics for waypoint type, 0 indicates standard graphics
 
 * @param p2 unused
 
 *
 
 * @todo When checking for the tile slope,
 
 * distingush between "Flat land required" and "land sloped in wrong direction"
 
 */
 
int32 CmdBuildTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Waypoint *wp;
 
	Slope tileh;
 
	Axis axis;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* if custom gfx are used, make sure it is within bounds */
 
	if (p1 >= GetNumCustomStations(STAT_CLASS_WAYP)) return CMD_ERROR;
 

	
 
	if (!IsTileType(tile, MP_RAILWAY) ||
 
			GetRailTileType(tile) != RAIL_TILE_NORMAL || (
 
				(axis = AXIS_X, GetTrackBits(tile) != TRACK_BIT_X) &&
 
				(axis = AXIS_Y, GetTrackBits(tile) != TRACK_BIT_Y)
 
			)) {
 
		return_cmd_error(STR_1005_NO_SUITABLE_RAILROAD_TRACK);
 
	}
 

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

	
 
	tileh = GetTileSlope(tile, NULL);
 
	if (tileh != SLOPE_FLAT &&
 
			(!_patches.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) {
 
		return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
 
	}
 

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

	
 
	/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
 
	wp = FindDeletedWaypointCloseTo(tile);
 
	if (wp == NULL) {
 
		wp = AllocateWaypoint();
 
		if (wp == NULL) return CMD_ERROR;
 

	
 
		wp->town_index = 0;
 
		wp->string = STR_NULL;
 
		wp->town_cn = 0;
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		const StationSpec* statspec;
 

	
 
		MakeRailWaypoint(tile, GetTileOwner(tile), axis, GetRailType(tile), wp->index);
 
		MarkTileDirtyByTile(tile);
 

	
 
		statspec = GetCustomStationSpec(STAT_CLASS_WAYP, p1);
 

	
 
		if (statspec != NULL) {
 
			wp->stat_id = p1;
 
			wp->grfid = statspec->grfid;
 
			wp->localidx = statspec->localidx;
 
		} else {
 
			// Specified custom graphics do not exist, so use default.
 
			wp->stat_id = 0;
 
			wp->grfid = 0;
 
			wp->localidx = 0;
 
		}
 

	
 
		wp->deleted = 0;
 
		wp->xy = tile;
 
		wp->build_date = _date;
 

	
 
		if (wp->town_index == 0) MakeDefaultWaypointName(wp);
 

	
 
		UpdateWaypointSign(wp);
 
		RedrawWaypointSign(wp);
 
		YapfNotifyTrackLayoutChange(tile, AxisToTrack(axis));
 
	}
 

	
 
	return _price.build_train_depot;
 
}
 

	
 
/* Daily loop for waypoints */
 
void WaypointsDailyLoop(void)
 
{
 
	Waypoint *wp;
 

	
 
	/* Check if we need to delete a waypoint */
 
	FOR_ALL_WAYPOINTS(wp) {
 
		if (wp->deleted != 0 && --wp->deleted == 0) DeleteWaypoint(wp);
 
	}
 
}
 

	
 
/* Remove a waypoint */
 
int32 RemoveTrainWaypoint(TileIndex tile, uint32 flags, bool justremove)
 
{
 
	Waypoint *wp;
 

	
 
	/* Make sure it's a waypoint */
 
	if (!IsTileType(tile, MP_RAILWAY) ||
 
			!IsRailWaypoint(tile) ||
 
			(!CheckTileOwnership(tile) && _current_player != OWNER_WATER) ||
 
			!EnsureNoVehicle(tile)) {
 
		return CMD_ERROR;
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		Track track = GetRailWaypointTrack(tile);
 
		wp = GetWaypointByTile(tile);
 

	
 
		wp->deleted = 30; // let it live for this many days before we do the actual deletion.
 
		RedrawWaypointSign(wp);
 

	
 
		if (justremove) {
 
			MakeRailNormal(tile, GetTileOwner(tile), GetRailWaypointBits(tile), GetRailType(tile));
 
			MarkTileDirtyByTile(tile);
 
		} else {
 
			DoClearSquare(tile);
 
			SetSignalsOnBothDir(tile, track);
 
		}
 
		YapfNotifyTrackLayoutChange(tile, track);
 
	}
 

	
 
	return _price.remove_train_depot;
 
}
 

	
 
/** Delete a waypoint
 
 * @param tile tile where waypoint is to be deleted
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdRemoveTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 
	return RemoveTrainWaypoint(tile, flags, true);
 
}
 

	
 
/** Rename a waypoint.
 
 * @param tile unused
 
 * @param p1 id of waypoint
 
 * @param p2 unused
 
 */
 
int32 CmdRenameWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Waypoint *wp;
 

	
 
	if (!IsValidWaypointID(p1)) return CMD_ERROR;
 

	
 
	if (_cmd_text[0] != '\0') {
 
		StringID str = AllocateNameUnique(_cmd_text, 0);
 

	
 
		if (str == 0) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
 
			wp = GetWaypoint(p1);
 
			if (wp->string != STR_NULL) DeleteName(wp->string);
 

	
 
			wp->string = str;
 
			wp->town_cn = 0;
 

	
 
			UpdateWaypointSign(wp);
 
			MarkWholeScreenDirty();
 
		} else {
 
			DeleteName(str);
 
		}
 
	} else {
 
		if (flags & DC_EXEC) {
 
			wp = GetWaypoint(p1);
 
			if (wp->string != STR_NULL) DeleteName(wp->string);
 

	
 
			MakeDefaultWaypointName(wp);
 
			UpdateWaypointSign(wp);
 
			MarkWholeScreenDirty();
 
		}
 
	}
 
	return 0;
 
}
 

	
 
/* This hacks together some dummy one-shot Station structure for a waypoint. */
 
Station *ComposeWaypointStation(TileIndex tile)
 
{
 
	Waypoint *wp = GetWaypointByTile(tile);
 
	static Station stat;
 

	
 
	stat.train_tile = stat.xy = wp->xy;
 
	stat.town = GetTown(wp->town_index);
 
	stat.string_id = wp->string == STR_NULL ? /* FIXME? */ 0 : wp->string;
 
	stat.build_date = wp->build_date;
 

	
 
	return &stat;
 
}
 

	
 
/* Draw a waypoint */
 
void DrawWaypointSprite(int x, int y, int stat_id, RailType railtype)
 
{
 
	x += 33;
 
	y += 17;
 

	
 
	if (!DrawStationTile(x, y, railtype, AXIS_X, STAT_CLASS_WAYP, stat_id)) {
 
		DrawDefaultWaypointSprite(x, y, railtype);
 
	}
 
}
 

	
 
/* Fix savegames which stored waypoints in their old format */
 
void FixOldWaypoints(void)
 
{
 
	Waypoint *wp;
 

	
 
	/* Convert the old 'town_or_string', to 'string' / 'town' / 'town_cn' */
 
	FOR_ALL_WAYPOINTS(wp) {
 
		wp->town_index = ClosestTownFromTile(wp->xy, (uint)-1)->index;
 
		wp->town_cn = 0;
 
		if (wp->string & 0xC000) {
 
			wp->town_cn = wp->string & 0x3F;
 
			wp->string = STR_NULL;
 
		}
 
	}
 
}
 

	
 
void InitializeWaypoints(void)
 
{
 
	CleanPool(&_Waypoint_pool);
 
	AddBlockToPool(&_Waypoint_pool);
 
}
 

	
 
static const SaveLoad _waypoint_desc[] = {
 
	SLE_CONDVAR(Waypoint, xy,         SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Waypoint, xy,         SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Waypoint, town_index, SLE_UINT16,                 12, SL_MAX_VERSION),
 
	SLE_CONDVAR(Waypoint, town_cn,    SLE_UINT8,                  12, SL_MAX_VERSION),
 
	    SLE_VAR(Waypoint, string,     SLE_UINT16),
 
	    SLE_VAR(Waypoint, deleted,    SLE_UINT8),
 

	
 
	SLE_CONDVAR(Waypoint, build_date, SLE_FILE_U16 | SLE_VAR_I32,  3, 30),
 
	SLE_CONDVAR(Waypoint, build_date, SLE_INT32,                  31, SL_MAX_VERSION),
 
	SLE_CONDVAR(Waypoint, localidx,   SLE_UINT8,                   3, SL_MAX_VERSION),
 
	SLE_CONDVAR(Waypoint, grfid,      SLE_UINT32,                 17, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static void Save_WAYP(void)
 
{
 
	Waypoint *wp;
 

	
 
	FOR_ALL_WAYPOINTS(wp) {
 
		SlSetArrayIndex(wp->index);
 
		SlObject(wp, _waypoint_desc);
 
	}
 
}
 

	
 
static void Load_WAYP(void)
 
{
 
	int index;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		Waypoint *wp;
 

	
 
		if (!AddBlockIfNeeded(&_Waypoint_pool, index))
 
			error("Waypoints: failed loading savegame: too many waypoints");
 

	
 
		wp = GetWaypoint(index);
 
		SlObject(wp, _waypoint_desc);
 
	}
 
}
 

	
 
const ChunkHandler _waypoint_chunk_handlers[] = {
 
	{ 'CHKP', Save_WAYP, Load_WAYP, CH_ARRAY | CH_LAST},
 
};
src/widget.c
Show inline comments
 
deleted file
src/widget.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "functions.h"
 
#include "player.h"
 
#include "table/sprites.h"
 
#include "table/strings.h"
 
#include "window.h"
 
#include "gfx.h"
 
#include "viewport.h"
 

	
 
static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom)
 
{
 
	Point pt;
 
	int height, count, pos, cap;
 

	
 
	top += 10;
 
	bottom -= 9;
 

	
 
	height = (bottom - top);
 

	
 
	pos = sb->pos;
 
	count = sb->count;
 
	cap = sb->cap;
 

	
 
	if (count != 0) top += height * pos / count;
 

	
 
	if (cap > count) cap = count;
 
	if (count != 0) bottom -= (count - pos - cap) * height / count;
 

	
 
	pt.x = top;
 
	pt.y = bottom - 1;
 
	return pt;
 
}
 

	
 
/*****************************************************
 
 * Special handling for the scrollbar widget type.
 
 * Handles the special scrolling buttons and other
 
 * scrolling.
 
 * Parameters:
 
 *   w   - Window.
 
 *   wi  - Pointer to the scrollbar widget.
 
 *   x   - The X coordinate of the mouse click.
 
 *   y   - The Y coordinate of the mouse click.
 
 */
 

	
 
void ScrollbarClickHandler(Window *w, const Widget *wi, int x, int y)
 
{
 
	int mi, ma, pos;
 
	Scrollbar *sb;
 

	
 
	switch (wi->type) {
 
		case WWT_SCROLLBAR: {
 
			// vertical scroller
 
			w->flags4 &= ~WF_HSCROLL;
 
			w->flags4 &= ~WF_SCROLL2;
 
			mi = wi->top;
 
			ma = wi->bottom;
 
			pos = y;
 
			sb = &w->vscroll;
 
			break;
 
		}
 
		case WWT_SCROLL2BAR: {
 
			// 2nd vertical scroller
 
			w->flags4 &= ~WF_HSCROLL;
 
			w->flags4 |= WF_SCROLL2;
 
			mi = wi->top;
 
			ma = wi->bottom;
 
			pos = y;
 
			sb = &w->vscroll2;
 
			break;
 
		}
 
		case  WWT_HSCROLLBAR: {
 
			// horizontal scroller
 
			w->flags4 &= ~WF_SCROLL2;
 
			w->flags4 |= WF_HSCROLL;
 
			mi = wi->left;
 
			ma = wi->right;
 
			pos = x;
 
			sb = &w->hscroll;
 
			break;
 
		}
 
		default: return; //this should never happen
 
	}
 
	if (pos <= mi+9) {
 
		// Pressing the upper button?
 
		w->flags4 |= WF_SCROLL_UP;
 
		if (_scroller_click_timeout == 0) {
 
			_scroller_click_timeout = 6;
 
			if (sb->pos != 0) sb->pos--;
 
		}
 
		_left_button_clicked = false;
 
	} else if (pos >= ma-10) {
 
		// Pressing the lower button?
 
		w->flags4 |= WF_SCROLL_DOWN;
 

	
 
		if (_scroller_click_timeout == 0) {
 
			_scroller_click_timeout = 6;
 
			if ((byte)(sb->pos + sb->cap) < sb->count)
 
				sb->pos++;
 
		}
 
		_left_button_clicked = false;
 
	} else {
 
		//
 
		Point pt = HandleScrollbarHittest(sb, mi, ma);
 

	
 
		if (pos < pt.x) {
 
			sb->pos = max(sb->pos - sb->cap, 0);
 
		} else if (pos > pt.y) {
 
			sb->pos = min(
 
				sb->pos + sb->cap,
 
				max(sb->count - sb->cap, 0)
 
			);
 
		} else {
 
			_scrollbar_start_pos = pt.x - mi - 9;
 
			_scrollbar_size = ma - mi - 23;
 
			w->flags4 |= WF_SCROLL_MIDDLE;
 
			_scrolling_scrollbar = true;
 
			_cursorpos_drag_start = _cursor.pos;
 
		}
 
	}
 

	
 
	SetWindowDirty(w);
 
}
 

	
 
/** Returns the index for the widget located at the given position
 
 * relative to the window. It includes all widget-corner pixels as well.
 
 * @param *w Window to look inside
 
 * @param  x,y Window client coordinates
 
 * @return A widget index, or -1 if no widget was found.
 
 */
 
int GetWidgetFromPos(const Window *w, int x, int y)
 
{
 
	uint index;
 
	int found_index = -1;
 

	
 
	// Go through the widgets and check if we find the widget that the coordinate is
 
	// inside.
 
	for (index = 0; index < w->widget_count; index++) {
 
		const Widget *wi = &w->widget[index];
 
		if (wi->type == WWT_EMPTY || wi->type == WWT_FRAME) continue;
 

	
 
		if (x >= wi->left && x <= wi->right && y >= wi->top &&  y <= wi->bottom &&
 
				!IsWindowWidgetHidden(w, index)) {
 
			found_index = index;
 
		}
 
	}
 

	
 
	return found_index;
 
}
 

	
 

	
 
void DrawFrameRect(int left, int top, int right, int bottom, int ctab, FrameFlags flags)
 
{
 
	uint dark         = _colour_gradient[ctab][3];
 
	uint medium_dark  = _colour_gradient[ctab][5];
 
	uint medium_light = _colour_gradient[ctab][6];
 
	uint light        = _colour_gradient[ctab][7];
 

	
 
	if (flags & FR_TRANSPARENT) {
 
		GfxFillRect(left, top, right, bottom, 0x322 | USE_COLORTABLE);
 
	} else {
 
		uint interior;
 

	
 
		if (flags & FR_LOWERED) {
 
			GfxFillRect(left,     top,     left,  bottom,     dark);
 
			GfxFillRect(left + 1, top,     right, top,        dark);
 
			GfxFillRect(right,    top + 1, right, bottom - 1, light);
 
			GfxFillRect(left + 1, bottom,  right, bottom,     light);
 
			interior = (flags & FR_DARKENED ? medium_dark : medium_light);
 
		} else {
 
			GfxFillRect(left,     top,    left,      bottom - 1, light);
 
			GfxFillRect(left + 1, top,    right - 1, top,        light);
 
			GfxFillRect(right,    top,    right,     bottom - 1, dark);
 
			GfxFillRect(left,     bottom, right,     bottom,     dark);
 
			interior = medium_dark;
 
		}
 
		if (!(flags & FR_BORDERONLY)) {
 
			GfxFillRect(left + 1, top + 1, right - 1, bottom - 1, interior);
 
		}
 
	}
 
}
 

	
 

	
 
void DrawWindowWidgets(const Window *w)
 
{
 
	const DrawPixelInfo* dpi = _cur_dpi;
 
	Rect r;
 
	uint i;
 

	
 
	for (i = 0; i < w->widget_count; i++) {
 
		const Widget *wi = &w->widget[i];
 
		bool clicked = IsWindowWidgetLowered(w, i);
 

	
 
		if (dpi->left > (r.right=/*w->left + */wi->right) ||
 
				dpi->left + dpi->width <= (r.left=wi->left/* + w->left*/) ||
 
				dpi->top > (r.bottom=/*w->top +*/ wi->bottom) ||
 
				dpi->top + dpi->height <= (r.top = /*w->top +*/ wi->top) ||
 
				IsWindowWidgetHidden(w, i)) {
 
			continue;
 
		}
 

	
 
		switch (wi->type & WWT_MASK) {
 
		case WWT_IMGBTN:
 
		case WWT_IMGBTN_2: {
 
			int img = wi->data;
 
			assert(img != 0);
 
			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
 

	
 
			/* show different image when clicked for WWT_IMGBTN_2 */
 
			if ((wi->type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++;
 
			DrawSprite(img, r.left + 1 + clicked, r.top + 1 + clicked);
 
			goto draw_default;
 
		}
 

	
 
		case WWT_PANEL: {
 
			assert(wi->data == 0);
 
			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
 
			goto draw_default;
 
		}
 

	
 
		case WWT_TEXTBTN:
 
		case WWT_TEXTBTN_2: {
 
			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
 
			}
 
		/* fall through */
 

	
 
		case WWT_LABEL: {
 
			StringID str = wi->data;
 

	
 
			if ((wi->type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++;
 

	
 
			DrawStringCentered(((r.left + r.right + 1) >> 1) + clicked, ((r.top + r.bottom + 1) >> 1) - 5 + clicked, str, 0);
 
			goto draw_default;
 
		}
 

	
 
		case WWT_INSET: {
 
			StringID str = wi->data;
 
			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_LOWERED | FR_DARKENED);
 

	
 
			if (str != STR_NULL) DrawStringTruncated(r.left + 2, r.top + 1, str, 0, r.right - r.left - 10);
 
			goto draw_default;
 
		}
 

	
 
		case WWT_MATRIX: {
 
			int c, d, ctr;
 
			int x, amt1, amt2;
 
			int color;
 

	
 
			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
 

	
 
			c = GB(wi->data, 0, 8);
 
			amt1 = (wi->right - wi->left + 1) / c;
 

	
 
			d = GB(wi->data, 8, 8);
 
			amt2 = (wi->bottom - wi->top + 1) / d;
 

	
 
			color = _colour_gradient[wi->color & 0xF][6];
 

	
 
			x = r.left;
 
			for (ctr = c; ctr > 1; ctr--) {
 
				x += amt1;
 
				GfxFillRect(x, r.top + 1, x, r.bottom - 1, color);
 
			}
 

	
 
			x = r.top;
 
			for (ctr = d; ctr > 1; ctr--) {
 
				x += amt2;
 
				GfxFillRect(r.left + 1, x, r.right - 1, x, color);
 
			}
 

	
 
			color = _colour_gradient[wi->color&0xF][4];
 

	
 
			x = r.left - 1;
 
			for (ctr = c; ctr > 1; ctr--) {
 
				x += amt1;
 
				GfxFillRect(x, r.top + 1, x, r.bottom - 1, color);
 
			}
 

	
 
			x = r.top - 1;
 
			for (ctr = d; ctr > 1; ctr--) {
 
				x += amt2;
 
				GfxFillRect(r.left+1, x, r.right-1, x, color);
 
			}
 

	
 
			goto draw_default;
 
		}
 

	
 
		// vertical scrollbar
 
		case WWT_SCROLLBAR: {
 
			Point pt;
 
			int c1,c2;
 

	
 
			assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere!
 

	
 
			// draw up/down buttons
 
			clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_UP);
 
			DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color, (clicked) ? FR_LOWERED : 0);
 
			DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, 0x10);
 

	
 
			clicked = (((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_DOWN));
 
			DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
 
			DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, 0x10);
 

	
 
			c1 = _colour_gradient[wi->color&0xF][3];
 
			c2 = _colour_gradient[wi->color&0xF][7];
 

	
 
			// draw "shaded" background
 
			GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c2);
 
			GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c1 | PALETTE_MODIFIER_GREYOUT);
 

	
 
			// draw shaded lines
 
			GfxFillRect(r.left+2, r.top+10, r.left+2, r.bottom-10, c1);
 
			GfxFillRect(r.left+3, r.top+10, r.left+3, r.bottom-10, c2);
 
			GfxFillRect(r.left+7, r.top+10, r.left+7, r.bottom-10, c1);
 
			GfxFillRect(r.left+8, r.top+10, r.left+8, r.bottom-10, c2);
 

	
 
			pt = HandleScrollbarHittest(&w->vscroll, r.top, r.bottom);
 
			DrawFrameRect(r.left, pt.x, r.right, pt.y, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_MIDDLE ? FR_LOWERED : 0);
 
			break;
 
		}
 
		case WWT_SCROLL2BAR: {
 
			Point pt;
 
			int c1,c2;
 

	
 
			assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere!
 

	
 
			// draw up/down buttons
 
			clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_UP | WF_SCROLL2));
 
			DrawFrameRect(r.left, r.top, r.right, r.top + 9, wi->color,  (clicked) ? FR_LOWERED : 0);
 
			DoDrawString(UPARROW, r.left + 2 + clicked, r.top + clicked, 0x10);
 

	
 
			clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_DOWN | WF_SCROLL2));
 
			DrawFrameRect(r.left, r.bottom - 9, r.right, r.bottom, wi->color,  (clicked) ? FR_LOWERED : 0);
 
			DoDrawString(DOWNARROW, r.left + 2 + clicked, r.bottom - 9 + clicked, 0x10);
 

	
 
			c1 = _colour_gradient[wi->color&0xF][3];
 
			c2 = _colour_gradient[wi->color&0xF][7];
 

	
 
			// draw "shaded" background
 
			GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c2);
 
			GfxFillRect(r.left, r.top+10, r.right, r.bottom-10, c1 | PALETTE_MODIFIER_GREYOUT);
 

	
 
			// draw shaded lines
 
			GfxFillRect(r.left+2, r.top+10, r.left+2, r.bottom-10, c1);
 
			GfxFillRect(r.left+3, r.top+10, r.left+3, r.bottom-10, c2);
 
			GfxFillRect(r.left+7, r.top+10, r.left+7, r.bottom-10, c1);
 
			GfxFillRect(r.left+8, r.top+10, r.left+8, r.bottom-10, c2);
 

	
 
			pt = HandleScrollbarHittest(&w->vscroll2, r.top, r.bottom);
 
			DrawFrameRect(r.left, pt.x, r.right, pt.y, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_MIDDLE | WF_SCROLL2) ? FR_LOWERED : 0);
 
			break;
 
		}
 

	
 
		// horizontal scrollbar
 
		case WWT_HSCROLLBAR: {
 
			Point pt;
 
			int c1,c2;
 

	
 
			assert(r.bottom - r.top == 11); // XXX - to ensure the same sizes are used everywhere!
 

	
 
			clicked = ((w->flags4 & (WF_SCROLL_UP | WF_HSCROLL)) == (WF_SCROLL_UP | WF_HSCROLL));
 
			DrawFrameRect(r.left, r.top, r.left + 9, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
 
			DrawSprite(SPR_ARROW_LEFT, r.left + 1 + clicked, r.top + 1 + clicked);
 

	
 
			clicked = ((w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL)) == (WF_SCROLL_DOWN | WF_HSCROLL));
 
			DrawFrameRect(r.right-9, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
 
			DrawSprite(SPR_ARROW_RIGHT, r.right - 8 + clicked, r.top + 1 + clicked);
 

	
 
			c1 = _colour_gradient[wi->color&0xF][3];
 
			c2 = _colour_gradient[wi->color&0xF][7];
 

	
 
			// draw "shaded" background
 
			GfxFillRect(r.left+10, r.top, r.right-10, r.bottom, c2);
 
			GfxFillRect(r.left+10, r.top, r.right-10, r.bottom, c1 | PALETTE_MODIFIER_GREYOUT);
 

	
 
			// draw shaded lines
 
			GfxFillRect(r.left+10, r.top+2, r.right-10, r.top+2, c1);
 
			GfxFillRect(r.left+10, r.top+3, r.right-10, r.top+3, c2);
 
			GfxFillRect(r.left+10, r.top+7, r.right-10, r.top+7, c1);
 
			GfxFillRect(r.left+10, r.top+8, r.right-10, r.top+8, c2);
 

	
 
			// draw actual scrollbar
 
			pt = HandleScrollbarHittest(&w->hscroll, r.left, r.right);
 
			DrawFrameRect(pt.x, r.top, pt.y, r.bottom, wi->color, (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL)) == (WF_SCROLL_MIDDLE | WF_HSCROLL) ? FR_LOWERED : 0);
 

	
 
			break;
 
		}
 

	
 
		case WWT_FRAME: {
 
			int c1,c2;
 
			int x2 = r.left; // by default the left side is the left side of the widget
 

	
 
			if (wi->data != 0) x2 = DrawString(r.left + 6, r.top, wi->data, 0);
 

	
 
			c1 = _colour_gradient[wi->color][3];
 
			c2 = _colour_gradient[wi->color][7];
 

	
 
			//Line from upper left corner to start of text
 
			GfxFillRect(r.left, r.top+4, r.left+4,r.top+4, c1);
 
			GfxFillRect(r.left+1, r.top+5, r.left+4,r.top+5, c2);
 

	
 
			// Line from end of text to upper right corner
 
			GfxFillRect(x2, r.top+4, r.right-1,r.top+4,c1);
 
			GfxFillRect(x2, r.top+5, r.right-2,r.top+5,c2);
 

	
 
			// Line from upper left corner to bottom left corner
 
			GfxFillRect(r.left, r.top+5, r.left, r.bottom-1, c1);
 
			GfxFillRect(r.left+1, r.top+6, r.left+1, r.bottom-2, c2);
 

	
 
			//Line from upper right corner to bottom right corner
 
			GfxFillRect(r.right-1, r.top+5, r.right-1, r.bottom-2, c1);
 
			GfxFillRect(r.right, r.top+4, r.right, r.bottom-1, c2);
 

	
 
			GfxFillRect(r.left+1, r.bottom-1, r.right-1, r.bottom-1, c1);
 
			GfxFillRect(r.left, r.bottom, r.right, r.bottom, c2);
 

	
 
			goto draw_default;
 
		}
 

	
 
		case WWT_STICKYBOX: {
 
			assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere!
 

	
 
			clicked = !!(w->flags4 & WF_STICKY);
 
			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
 
			DrawSprite((clicked) ? SPR_PIN_UP : SPR_PIN_DOWN, r.left + 2 + clicked, r.top + 3 + clicked);
 
			break;
 
		}
 

	
 
		case WWT_RESIZEBOX: {
 
			assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere!
 

	
 
			clicked = !!(w->flags4 & WF_SIZING);
 
			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : 0);
 
			DrawSprite(SPR_WINDOW_RESIZE, r.left + 3 + clicked, r.top + 3 + clicked);
 
			break;
 
		}
 

	
 
		case WWT_CLOSEBOX: {
 
			assert(r.right - r.left == 10); // ensure the same sizes are used everywhere
 

	
 
			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, 0);
 
			DrawString(r.left + 2, r.top + 2, STR_00C5, 0);
 
			break;
 
		}
 

	
 
		case WWT_CAPTION: {
 
			assert(r.bottom - r.top == 13); // XXX - to ensure the same sizes are used everywhere!
 
			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_BORDERONLY);
 
			DrawFrameRect(r.left+1, r.top+1, r.right-1, r.bottom-1, wi->color, (w->caption_color == 0xFF) ? FR_LOWERED | FR_DARKENED : FR_LOWERED | FR_DARKENED | FR_BORDERONLY);
 

	
 
			if (w->caption_color != 0xFF) {
 
				GfxFillRect(r.left+2, r.top+2, r.right-2, r.bottom-2, _colour_gradient[_player_colors[w->caption_color]][4]);
 
			}
 

	
 
			DrawStringCenteredTruncated(r.left + 2, r.right - 2, r.top+2, wi->data, 0x84);
 
draw_default:;
 
			if (IsWindowWidgetDisabled(w, i)) {
 
				GfxFillRect(r.left+1, r.top+1, r.right-1, r.bottom-1, _colour_gradient[wi->color&0xF][2] | PALETTE_MODIFIER_GREYOUT);
 
			}
 
		}
 
		}
 
	}
 

	
 

	
 
	if (w->flags4 & WF_WHITE_BORDER_MASK) {
 
		//DrawFrameRect(w->left, w->top, w->left + w->width-1, w->top+w->height-1, 0xF, 0x10);
 
		DrawFrameRect(0, 0, w->width-1, w->height-1, 0xF, FR_BORDERONLY);
 
	}
 

	
 
}
 

	
 
static const Widget _dropdown_menu_widgets[] = {
 
{      WWT_PANEL,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_NULL},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{   WIDGETS_END},
 
};
 

	
 
static int GetDropdownItem(const Window *w)
 
{
 
	byte item, counter;
 
	int y;
 

	
 
	if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0)
 
		return -1;
 

	
 
	y = _cursor.pos.y - w->top - 2 + w->vscroll.pos * 10;
 

	
 
	if (y < 0)
 
		return - 1;
 

	
 
	item = y / 10;
 
	if (item >= WP(w,dropdown_d).num_items || (HASBIT(WP(w,dropdown_d).disabled_state, item) && !HASBIT(WP(w,dropdown_d).hidden_state, item)) || WP(w,dropdown_d).items[item] == 0)
 
		return - 1;
 

	
 
	// Skip hidden items -- +1 for each hidden item before the clicked item.
 
	for (counter = 0; item >= counter; ++counter)
 
		if (HASBIT(WP(w,dropdown_d).hidden_state, counter)) item++;
 

	
 
	return item;
 
}
 

	
 
static void DropdownMenuWndProc(Window *w, WindowEvent *e)
 
{
 
	int item;
 

	
 
	switch (e->event) {
 
		case WE_PAINT: {
 
			int x,y,i,sel;
 
			int width, height;
 

	
 
			DrawWindowWidgets(w);
 

	
 
			x = 1;
 
			y = 2 - w->vscroll.pos * 10;
 

	
 
			sel    = WP(w,dropdown_d).selected_index;
 
			width  = w->widget[0].right - 3;
 
			height = w->widget[0].bottom - 3;
 

	
 
			for (i = 0; WP(w,dropdown_d).items[i] != INVALID_STRING_ID; i++, sel--) {
 
				if (HASBIT(WP(w,dropdown_d).hidden_state, i)) continue;
 

	
 
				if (y >= 0 && y <= height) {
 
					if (WP(w,dropdown_d).items[i] != STR_NULL) {
 
						if (sel == 0) GfxFillRect(x + 1, y, x + width, y + 9, 0);
 
						DrawStringTruncated(x + 2, y, WP(w,dropdown_d).items[i], sel == 0 ? 12 : 16, x + width);
 

	
 
						if (HASBIT(WP(w,dropdown_d).disabled_state, i)) {
 
							GfxFillRect(x, y, x + width, y + 9,
 
								PALETTE_MODIFIER_GREYOUT | _colour_gradient[_dropdown_menu_widgets[0].color][5]
 
							);
 
						}
 
					} else {
 
						int c1 = _colour_gradient[_dropdown_menu_widgets[0].color][3];
 
						int c2 = _colour_gradient[_dropdown_menu_widgets[0].color][7];
 

	
 
						GfxFillRect(x + 1, y + 3, x + w->width - 5, y + 3, c1);
 
						GfxFillRect(x + 1, y + 4, x + w->width - 5, y + 4, c2);
 
					}
 
				}
 
				y += 10;
 
			}
 
		} break;
 

	
 
		case WE_CLICK: {
 
			if (e->we.click.widget != 0) break;
 
			item = GetDropdownItem(w);
 
			if (item >= 0) {
 
				WP(w,dropdown_d).click_delay = 4;
 
				WP(w,dropdown_d).selected_index = item;
 
				SetWindowDirty(w);
 
			}
 
		} break;
 

	
 
		case WE_MOUSELOOP: {
 
			Window *w2 = FindWindowById(WP(w,dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
 
			if (w2 == NULL) {
 
				DeleteWindow(w);
 
				return;
 
			}
 

	
 
			if (WP(w,dropdown_d).click_delay != 0 && --WP(w,dropdown_d).click_delay == 0) {
 
				WindowEvent e;
 
				e.event = WE_DROPDOWN_SELECT;
 
				e.we.dropdown.button = WP(w,dropdown_d).parent_button;
 
				e.we.dropdown.index  = WP(w,dropdown_d).selected_index;
 
				w2->wndproc(w2, &e);
 
				DeleteWindow(w);
 
				return;
 
			}
 

	
 
			if (WP(w,dropdown_d).drag_mode) {
 
				item = GetDropdownItem(w);
 

	
 
				if (!_left_button_clicked) {
 
					WP(w,dropdown_d).drag_mode = false;
 
					if (item < 0) return;
 
					WP(w,dropdown_d).click_delay = 2;
 
				} else {
 
					if (item < 0) return;
 
				}
 

	
 
				WP(w,dropdown_d).selected_index = item;
 
				SetWindowDirty(w);
 
			}
 
		} break;
 

	
 
		case WE_DESTROY: {
 
			Window *w2 = FindWindowById(WP(w,dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
 
			if (w2 != NULL) {
 
				RaiseWindowWidget(w2, WP(w,dropdown_d).parent_button);
 
				InvalidateWidget(w2, WP(w,dropdown_d).parent_button);
 
			}
 
		} break;
 
	}
 
}
 

	
 
void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask)
 
{
 
	int i;
 
	const Widget *wi;
 
	Window *w2;
 
	const Window *w3;
 
	bool is_dropdown_menu_shown = IsWindowWidgetLowered(w, button);
 
	int top, height;
 
	int screen_top, screen_bottom;
 
	bool scroll = false;
 

	
 
	DeleteWindowById(WC_DROPDOWN_MENU, 0);
 

	
 
	if (is_dropdown_menu_shown) return;
 

	
 
	LowerWindowWidget(w, button);
 

	
 
	InvalidateWidget(w, button);
 

	
 
	for (i = 0; strings[i] != INVALID_STRING_ID; i++) {}
 
	if (i == 0) return;
 

	
 
	wi = &w->widget[button];
 

	
 
	if (hidden_mask != 0) {
 
		uint j;
 

	
 
		for (j = 0; strings[j] != INVALID_STRING_ID; j++) {
 
			if (HASBIT(hidden_mask, j)) i--;
 
		}
 
	}
 

	
 
	/* The preferred position is just below the dropdown calling widget */
 
	top = w->top + wi->bottom + 2;
 
	height = i * 10 + 4;
 

	
 
	w3 = FindWindowById(WC_STATUS_BAR, 0);
 
	screen_bottom = w3 == NULL ? _screen.height : w3->top;
 

	
 
	/* Check if the dropdown will fully fit below the widget */
 
	if (top + height >= screen_bottom) {
 
		w3 = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
		screen_top = w3 == NULL ? 0 : w3->top + w3->height;
 

	
 
		/* If not, check if it will fit above the widget */
 
		if (w->top + wi->top - height - 1 > screen_top) {
 
			top = w->top + wi->top - height - 1;
 
		} else {
 
			/* ... and lastly if it won't, enable the scroll bar and fit the
 
			 * list in below the widget */
 
			int rows = (screen_bottom - 4 - top) / 10;
 
			height = rows * 10 + 4;
 
			scroll = true;
 
		}
 
	}
 

	
 
	w2 = AllocateWindow(
 
		w->left + wi[-1].left + 1,
 
		top,
 
		wi->right - wi[-1].left + 1,
 
		height,
 
		DropdownMenuWndProc,
 
		WC_DROPDOWN_MENU,
 
		_dropdown_menu_widgets);
 

	
 
	w2->widget[0].color = wi->color;
 
	w2->widget[0].right = wi->right - wi[-1].left;
 
	w2->widget[0].bottom = height - 1;
 

	
 
	SetWindowWidgetHiddenState(w2, 1, !scroll);
 

	
 
	if (scroll) {
 
		/* We're scrolling, so enable the scroll bar and shrink the list by
 
		 * the scrollbar's width */
 
		w2->widget[1].color  = wi->color;
 
		w2->widget[1].right  = w2->widget[0].right;
 
		w2->widget[1].left   = w2->widget[1].right - 11;
 
		w2->widget[1].bottom = height - 1;
 
		w2->widget[0].right -= 12;
 

	
 
		w2->vscroll.cap   = (height - 4) / 10;
 
		w2->vscroll.count = i;
 
	}
 

	
 
	w2->desc_flags = WDF_DEF_WIDGET;
 
	w2->flags4 &= ~WF_WHITE_BORDER_MASK;
 

	
 
	WP(w2,dropdown_d).disabled_state = disabled_mask;
 
	WP(w2,dropdown_d).hidden_state = hidden_mask;
 

	
 
	WP(w2,dropdown_d).parent_wnd_class = w->window_class;
 
	WP(w2,dropdown_d).parent_wnd_num = w->window_number;
 
	WP(w2,dropdown_d).parent_button = button;
 

	
 
	WP(w2,dropdown_d).num_items = i;
 
	WP(w2,dropdown_d).selected_index = selected;
 
	WP(w2,dropdown_d).items = strings;
 

	
 
	WP(w2,dropdown_d).click_delay = 0;
 
	WP(w2,dropdown_d).drag_mode = true;
 
}
src/win32.c
Show inline comments
 
deleted file
src/win32.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "macros.h"
 
#include "saveload.h"
 
#include "string.h"
 
#include "gfx.h"
 
#include "window.h"
 
#include <windows.h>
 
#include <winnt.h>
 
#include <wininet.h>
 
#include <io.h>
 
#include <fcntl.h>
 
#include <shlobj.h> // SHGetFolderPath
 
#include "variables.h"
 
#include "win32.h"
 
#include "fios.h" // opendir/readdir/closedir
 
#include <ctype.h>
 
#include <tchar.h>
 
#include <errno.h>
 
#include <sys/types.h>
 
#include <sys/stat.h>
 

	
 
static bool _has_console;
 

	
 
#if defined(__MINGW32__)
 
	#include <stdint.h>
 
#endif
 

	
 
static bool cursor_visible = true;
 

	
 
bool MyShowCursor(bool show)
 
{
 
	if (cursor_visible == show) return show;
 

	
 
	cursor_visible = show;
 
	ShowCursor(show);
 

	
 
	return !show;
 
}
 

	
 
/** Helper function needed by dynamically loading libraries
 
 * XXX: Hurray for MS only having an ANSI GetProcAddress function
 
 * on normal windows and no Wide version except for in Windows Mobile/CE */
 
bool LoadLibraryList(Function proc[], const char *dll)
 
{
 
	while (*dll != '\0') {
 
		HMODULE lib;
 
		lib = LoadLibrary(MB_TO_WIDE(dll));
 

	
 
		if (lib == NULL) return false;
 
		for (;;) {
 
			FARPROC p;
 

	
 
			while (*dll++ != '\0');
 
			if (*dll == '\0') break;
 
#if defined(WINCE)
 
			p = GetProcAddress(lib, MB_TO_WIDE(dll);
 
#else
 
			p = GetProcAddress(lib, dll);
 
#endif
 
			if (p == NULL) return false;
 
			*proc++ = (Function)p;
 
		}
 
		dll++;
 
	}
 
	return true;
 
}
 

	
 
#ifdef _MSC_VER
 
static const char *_exception_string = NULL;
 
#endif
 

	
 
void ShowOSErrorBox(const char *buf)
 
{
 
	MyShowCursor(true);
 
	MessageBox(GetActiveWindow(), MB_TO_WIDE(buf), _T("Error!"), MB_ICONSTOP);
 

	
 
// if exception tracker is enabled, we crash here to let the exception handler handle it.
 
#if defined(WIN32_EXCEPTION_TRACKER) && !defined(_DEBUG)
 
	if (*buf == '!') {
 
		_exception_string = buf;
 
		*(byte*)0 = 0;
 
	}
 
#endif
 
}
 

	
 
#ifdef _MSC_VER
 

	
 
static void *_safe_esp;
 
static char *_crash_msg;
 
static bool _expanded;
 
static bool _did_emerg_save;
 
static int _ident;
 

	
 
typedef struct DebugFileInfo {
 
	uint32 size;
 
	uint32 crc32;
 
	SYSTEMTIME file_time;
 
} DebugFileInfo;
 

	
 
static uint32 *_crc_table;
 

	
 
static void MakeCRCTable(uint32 *table) {
 
	uint32 crc, poly = 0xEDB88320L;
 
	int i;
 
	int j;
 

	
 
	_crc_table = table;
 

	
 
	for (i = 0; i != 256; i++) {
 
		crc = i;
 
		for (j = 8; j != 0; j--) {
 
			crc = (crc & 1 ? (crc >> 1) ^ poly : crc >> 1);
 
		}
 
		table[i] = crc;
 
	}
 
}
 

	
 
static uint32 CalcCRC(byte *data, uint size, uint32 crc) {
 
	for (; size > 0; size--) {
 
		crc = ((crc >> 8) & 0x00FFFFFF) ^ _crc_table[(crc ^ *data++) & 0xFF];
 
	}
 
	return crc;
 
}
 

	
 
static void GetFileInfo(DebugFileInfo *dfi, const TCHAR *filename)
 
{
 
	HANDLE file;
 
	memset(dfi, 0, sizeof(dfi));
 

	
 
	file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
 
	if (file != INVALID_HANDLE_VALUE) {
 
		byte buffer[1024];
 
		DWORD numread;
 
		uint32 filesize = 0;
 
		FILETIME write_time;
 
		uint32 crc = (uint32)-1;
 

	
 
		for (;;) {
 
			if (ReadFile(file, buffer, sizeof(buffer), &numread, NULL) == 0 || numread == 0)
 
				break;
 
			filesize += numread;
 
			crc = CalcCRC(buffer, numread, crc);
 
		}
 
		dfi->size = filesize;
 
		dfi->crc32 = crc ^ (uint32)-1;
 

	
 
		if (GetFileTime(file, NULL, NULL, &write_time)) {
 
			FileTimeToSystemTime(&write_time, &dfi->file_time);
 
		}
 
		CloseHandle(file);
 
	}
 
}
 

	
 

	
 
static char *PrintModuleInfo(char *output, HMODULE mod)
 
{
 
	TCHAR buffer[MAX_PATH];
 
	DebugFileInfo dfi;
 

	
 
	GetModuleFileName(mod, buffer, MAX_PATH);
 
	GetFileInfo(&dfi, buffer);
 
	output += sprintf(output, " %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\r\n",
 
		WIDE_TO_MB(buffer),
 
		mod,
 
		dfi.size,
 
		dfi.crc32,
 
		dfi.file_time.wYear,
 
		dfi.file_time.wMonth,
 
		dfi.file_time.wDay,
 
		dfi.file_time.wHour,
 
		dfi.file_time.wMinute,
 
		dfi.file_time.wSecond
 
	);
 
	return output;
 
}
 

	
 
static char *PrintModuleList(char *output)
 
{
 
	BOOL (WINAPI *EnumProcessModules)(HANDLE, HMODULE*, DWORD, LPDWORD);
 

	
 
	if (LoadLibraryList((Function*)&EnumProcessModules, "psapi.dll\0EnumProcessModules\0\0")) {
 
		HMODULE modules[100];
 
		DWORD needed;
 
		BOOL res;
 
		int count, i;
 

	
 
		HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
 
		if (proc != NULL) {
 
			res = EnumProcessModules(proc, modules, sizeof(modules), &needed);
 
			CloseHandle(proc);
 
			if (res) {
 
				count = min(needed / sizeof(HMODULE), lengthof(modules));
 

	
 
				for (i = 0; i != count; i++) output = PrintModuleInfo(output, modules[i]);
 
				return output;
 
			}
 
		}
 
	}
 
	output = PrintModuleInfo(output, NULL);
 
	return output;
 
}
 

	
 
static const TCHAR _crash_desc[] =
 
	_T("A serious fault condition occured in the game. The game will shut down.\n")
 
	_T("Please send the crash information and the crash.dmp file (if any) to the developers.\n")
 
	_T("This will greatly help debugging. The correct place to do this is http://bugs.openttd.org. ")
 
	_T("The information contained in the report is displayed below.\n")
 
	_T("Press \"Emergency save\" to attempt saving the game.");
 

	
 
static const TCHAR _save_succeeded[] =
 
	_T("Emergency save succeeded.\n")
 
	_T("Be aware that critical parts of the internal game state may have become ")
 
	_T("corrupted. The saved game is not guaranteed to work.");
 

	
 
static bool EmergencySave(void)
 
{
 
	SaveOrLoad("crash.sav", SL_SAVE);
 
	return true;
 
}
 

	
 
/* Disable the crash-save submit code as it's not used */
 
#if 0
 

	
 
typedef struct {
 
	HINTERNET (WINAPI *InternetOpen)(LPCTSTR,DWORD, LPCTSTR, LPCTSTR, DWORD);
 
	HINTERNET (WINAPI *InternetConnect)(HINTERNET, LPCTSTR, INTERNET_PORT, LPCTSTR, LPCTSTR, DWORD, DWORD, DWORD);
 
	HINTERNET (WINAPI *HttpOpenRequest)(HINTERNET, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR *, DWORD, DWORD);
 
	BOOL (WINAPI *HttpSendRequest)(HINTERNET, LPCTSTR, DWORD, LPVOID, DWORD);
 
	BOOL (WINAPI *InternetCloseHandle)(HINTERNET);
 
	BOOL (WINAPI *HttpQueryInfo)(HINTERNET, DWORD, LPVOID, LPDWORD, LPDWORD);
 
} WinInetProcs;
 

	
 
#define M(x) x "\0"
 
#if defined(UNICODE)
 
# define W(x) x "W"
 
#else
 
# define W(x) x "A"
 
#endif
 
static const char wininet_files[] =
 
	M("wininet.dll")
 
	M(W("InternetOpen"))
 
	M(W("InternetConnect"))
 
	M(W("HttpOpenRequest"))
 
	M(W("HttpSendRequest"))
 
	M("InternetCloseHandle")
 
	M(W("HttpQueryInfo"))
 
	M("");
 
#undef W
 
#undef M
 

	
 
static WinInetProcs _wininet;
 

	
 
static const TCHAR *SubmitCrashReport(HWND wnd, void *msg, size_t msglen, const TCHAR *arg)
 
{
 
	HINTERNET inet, conn, http;
 
	const TCHAR *err = NULL;
 
	DWORD code, len;
 
	static TCHAR buf[100];
 
	TCHAR buff[100];
 

	
 
	if (_wininet.InternetOpen == NULL && !LoadLibraryList((Function*)&_wininet, wininet_files)) return _T("can't load wininet.dll");
 

	
 
	inet = _wininet.InternetOpen(_T("OTTD"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
 
	if (inet == NULL) { err = _T("internetopen failed"); goto error1; }
 

	
 
	conn = _wininet.InternetConnect(inet, _T("www.openttd.org"), INTERNET_DEFAULT_HTTP_PORT, _T(""), _T(""), INTERNET_SERVICE_HTTP, 0, 0);
 
	if (conn == NULL) { err = _T("internetconnect failed"); goto error2; }
 

	
 
	_sntprintf(buff, lengthof(buff), _T("/crash.php?file=%s&ident=%d"), arg, _ident);
 

	
 
	http = _wininet.HttpOpenRequest(conn, _T("POST"), buff, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE , 0);
 
	if (http == NULL) { err = _T("httpopenrequest failed"); goto error3; }
 

	
 
	if (!_wininet.HttpSendRequest(http, _T("Content-type: application/binary"), -1, msg, (DWORD)msglen)) { err = _T("httpsendrequest failed"); goto error4; }
 

	
 
	len = sizeof(code);
 
	if (!_wininet.HttpQueryInfo(http, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &code, &len, 0)) { err = _T("httpqueryinfo failed"); goto error4; }
 

	
 
	if (code != 200) {
 
		int l = _sntprintf(buf, lengthof(buf), _T("Server said: %d "), code);
 
		len = sizeof(buf) - l;
 
		_wininet.HttpQueryInfo(http, HTTP_QUERY_STATUS_TEXT, buf + l, &len, 0);
 
		err = buf;
 
	}
 

	
 
error4:
 
	_wininet.InternetCloseHandle(http);
 
error3:
 
	_wininet.InternetCloseHandle(conn);
 
error2:
 
	_wininet.InternetCloseHandle(inet);
 
error1:
 
	return err;
 
}
 

	
 
static void SubmitFile(HWND wnd, const TCHAR *file)
 
{
 
	HANDLE h;
 
	unsigned long size;
 
	unsigned long read;
 
	void *mem;
 

	
 
	h = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
 
	if (h == NULL) return;
 

	
 
	size = GetFileSize(h, NULL);
 
	if (size > 500000) goto error1;
 

	
 
	mem = malloc(size);
 
	if (mem == NULL) goto error1;
 

	
 
	if (!ReadFile(h, mem, size, &read, NULL) || read != size) goto error2;
 

	
 
	SubmitCrashReport(wnd, mem, size, file);
 

	
 
error2:
 
	free(mem);
 
error1:
 
	CloseHandle(h);
 
}
 

	
 
#endif /* Disabled crash-submit procedures */
 

	
 
static const TCHAR * const _expand_texts[] = {_T("S&how report >>"), _T("&Hide report <<") };
 

	
 
static void SetWndSize(HWND wnd, int mode)
 
{
 
	RECT r,r2;
 
	int offs;
 

	
 
	GetWindowRect(wnd, &r);
 

	
 
	SetDlgItemText(wnd, 15, _expand_texts[mode == 1]);
 

	
 
	if (mode >= 0) {
 
		GetWindowRect(GetDlgItem(wnd, 11), &r2);
 
		offs = r2.bottom - r2.top + 10;
 
		if (!mode) offs = -offs;
 
		SetWindowPos(wnd, HWND_TOPMOST, 0, 0,
 
			r.right - r.left, r.bottom - r.top + offs, SWP_NOMOVE | SWP_NOZORDER);
 
	} else {
 
		SetWindowPos(wnd, HWND_TOPMOST,
 
			(GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2,
 
			(GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2,
 
			0, 0, SWP_NOSIZE);
 
	}
 
}
 

	
 
static bool DoEmergencySave(HWND wnd)
 
{
 
	bool b = false;
 

	
 
	EnableWindow(GetDlgItem(wnd, 13), FALSE);
 
	_did_emerg_save = true;
 
	__try {
 
		b = EmergencySave();
 
	} __except (1) {}
 
	return b;
 
}
 

	
 
static INT_PTR CALLBACK CrashDialogFunc(HWND wnd,UINT msg,WPARAM wParam,LPARAM lParam)
 
{
 
	switch (msg) {
 
		case WM_INITDIALOG: {
 
#if defined(UNICODE)
 
			/* We need to put the crash-log in a seperate buffer because the default
 
			 * buffer in MB_TO_WIDE is not large enough (256 chars) */
 
			wchar_t crash_msgW[8096];
 
#endif
 
			SetDlgItemText(wnd, 10, _crash_desc);
 
			SetDlgItemText(wnd, 11, MB_TO_WIDE_BUFFER(_crash_msg, crash_msgW, lengthof(crash_msgW)));
 
			SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
 
			SetWndSize(wnd, -1);
 
		} return TRUE;
 
		case WM_COMMAND:
 
			switch (wParam) {
 
				case 12: /* Close */
 
					ExitProcess(0);
 
				case 13: /* Emergency save */
 
					if (DoEmergencySave(wnd)) {
 
						MessageBox(wnd, _save_succeeded, _T("Save successful"), MB_ICONINFORMATION);
 
					} else {
 
						MessageBox(wnd, _T("Save failed"), _T("Save failed"), MB_ICONINFORMATION);
 
					}
 
					break;
 
/* Disable the crash-save submit code as it's not used */
 
#if 0
 
				case 14: { /* Submit crash report */
 
					const TCHAR *s;
 

	
 
					SetCursor(LoadCursor(NULL, IDC_WAIT));
 

	
 
					s = SubmitCrashReport(wnd, _crash_msg, strlen(_crash_msg), _T(""));
 
					if (s != NULL) {
 
						MessageBox(wnd, s, _T("Error"), MB_ICONSTOP);
 
						break;
 
					}
 

	
 
					// try to submit emergency savegame
 
					if (_did_emerg_save || DoEmergencySave(wnd)) SubmitFile(wnd, _T("crash.sav"));
 

	
 
					// try to submit the autosaved game
 
					if (_opt.autosave) {
 
						TCHAR buf[40];
 
						_sntprintf(buf, lengthof(buf), _T("autosave%d.sav"), (_autosave_ctr - 1) & 3);
 
						SubmitFile(wnd, buf);
 
					}
 
					EnableWindow(GetDlgItem(wnd, 14), FALSE);
 
					SetCursor(LoadCursor(NULL, IDC_ARROW));
 
					MessageBox(wnd, _T("Crash report submitted. Thank you."), _T("Crash Report"), MB_ICONINFORMATION);
 
				}	break;
 
#endif /* Disabled crash-submit procedures */
 
				case 15: /* Expand window to show crash-message */
 
					_expanded ^= 1;
 
					SetWndSize(wnd, _expanded);
 
					break;
 
			}
 
			return TRUE;
 
		case WM_CLOSE: ExitProcess(0);
 
	}
 

	
 
	return FALSE;
 
}
 

	
 
static void Handler2(void)
 
{
 
	ShowCursor(TRUE);
 
	ShowWindow(GetActiveWindow(), FALSE);
 
	DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(100), NULL, CrashDialogFunc);
 
}
 

	
 
extern bool CloseConsoleLogIfActive(void);
 

	
 
static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
 
{
 
	extern const char _openttd_revision[];
 
	char *output;
 
	static bool had_exception = false;
 

	
 
	if (had_exception) ExitProcess(0);
 
	had_exception = true;
 

	
 
	_ident = GetTickCount(); // something pretty unique
 

	
 
	MakeCRCTable(alloca(256 * sizeof(uint32)));
 
	_crash_msg = output = LocalAlloc(LMEM_FIXED, 8192);
 

	
 
	{
 
		SYSTEMTIME time;
 
		GetLocalTime(&time);
 
		output += sprintf(output,
 
			"*** OpenTTD Crash Report ***\r\n"
 
			"Date: %d-%.2d-%.2d %.2d:%.2d:%.2d\r\n"
 
			"Build: %s built on " __DATE__ " " __TIME__ "\r\n",
 
			time.wYear,
 
			time.wMonth,
 
			time.wDay,
 
			time.wHour,
 
			time.wMinute,
 
			time.wSecond,
 
			_openttd_revision
 
		);
 
	}
 

	
 
	if (_exception_string)
 
		output += sprintf(output, "Reason: %s\r\n", _exception_string);
 

	
 
#ifdef _M_AMD64
 
	output += sprintf(output, "Exception %.8X at %.16IX\r\n"
 
		"Registers:\r\n"
 
		"RAX: %.16llX RBX: %.16llX RCX: %.16llX RDX: %.16llX\r\n"
 
		"RSI: %.16llX RDI: %.16llX RBP: %.16llX RSP: %.16llX\r\n"
 
		"R8:  %.16llX R9:  %.16llX R10: %.16llX R11: %.16llX\r\n"
 
		"R12: %.16llX R13: %.16llX R14: %.16llX R15: %.16llX\r\n"
 
		"RIP: %.16llX EFLAGS: %.8X\r\n"
 
		"\r\nBytes at CS:RIP:\r\n",
 
		ep->ExceptionRecord->ExceptionCode,
 
		ep->ExceptionRecord->ExceptionAddress,
 
		ep->ContextRecord->Rax,
 
		ep->ContextRecord->Rbx,
 
		ep->ContextRecord->Rcx,
 
		ep->ContextRecord->Rdx,
 
		ep->ContextRecord->Rsi,
 
		ep->ContextRecord->Rdi,
 
		ep->ContextRecord->Rbp,
 
		ep->ContextRecord->Rsp,
 
		ep->ContextRecord->R8,
 
		ep->ContextRecord->R9,
 
		ep->ContextRecord->R10,
 
		ep->ContextRecord->R11,
 
		ep->ContextRecord->R12,
 
		ep->ContextRecord->R13,
 
		ep->ContextRecord->R14,
 
		ep->ContextRecord->R15,
 
		ep->ContextRecord->Rip,
 
		ep->ContextRecord->EFlags
 
	);
 
#else
 
	output += sprintf(output, "Exception %.8X at %.8X\r\n"
 
		"Registers:\r\n"
 
		" EAX: %.8X EBX: %.8X ECX: %.8X EDX: %.8X\r\n"
 
		" ESI: %.8X EDI: %.8X EBP: %.8X ESP: %.8X\r\n"
 
		" EIP: %.8X EFLAGS: %.8X\r\n"
 
		"\r\nBytes at CS:EIP:\r\n",
 
		ep->ExceptionRecord->ExceptionCode,
 
		ep->ExceptionRecord->ExceptionAddress,
 
		ep->ContextRecord->Eax,
 
		ep->ContextRecord->Ebx,
 
		ep->ContextRecord->Ecx,
 
		ep->ContextRecord->Edx,
 
		ep->ContextRecord->Esi,
 
		ep->ContextRecord->Edi,
 
		ep->ContextRecord->Ebp,
 
		ep->ContextRecord->Esp,
 
		ep->ContextRecord->Eip,
 
		ep->ContextRecord->EFlags
 
	);
 
#endif
 

	
 
	{
 
#ifdef _M_AMD64
 
		byte *b = (byte*)ep->ContextRecord->Rip;
 
#else
 
		byte *b = (byte*)ep->ContextRecord->Eip;
 
#endif
 
		int i;
 
		for (i = 0; i != 24; i++) {
 
			if (IsBadReadPtr(b, 1)) {
 
				output += sprintf(output, " ??"); // OCR: WAS: , 0);
 
			} else {
 
				output += sprintf(output, " %.2X", *b);
 
			}
 
			b++;
 
		}
 
		output += sprintf(output,
 
			"\r\n"
 
			"\r\nStack trace: \r\n"
 
		);
 
	}
 

	
 
	{
 
		int i,j;
 
#ifdef _M_AMD64
 
		uint32 *b = (uint32*)ep->ContextRecord->Rsp;
 
#else
 
		uint32 *b = (uint32*)ep->ContextRecord->Esp;
 
#endif
 
		for (j = 0; j != 24; j++) {
 
			for (i = 0; i != 8; i++) {
 
				if (IsBadReadPtr(b,sizeof(uint32))) {
 
					output += sprintf(output, " ????????"); //OCR: WAS - , 0);
 
				} else {
 
					output += sprintf(output, " %.8X", *b);
 
				}
 
				b++;
 
			}
 
			output += sprintf(output, "\r\n");
 
		}
 
	}
 

	
 
	output += sprintf(output, "\r\nModule information:\r\n");
 
	output = PrintModuleList(output);
 

	
 
	{
 
		OSVERSIONINFO os;
 
		os.dwOSVersionInfoSize = sizeof(os);
 
		GetVersionEx(&os);
 
		output += sprintf(output, "\r\nSystem information:\r\n"
 
			" Windows version %d.%d %d %s\r\n",
 
			os.dwMajorVersion, os.dwMinorVersion, os.dwBuildNumber, os.szCSDVersion);
 
	}
 

	
 
	{
 
		HANDLE file = CreateFile(_T("crash.log"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
 
		DWORD num_written;
 
		if (file != INVALID_HANDLE_VALUE) {
 
			WriteFile(file, _crash_msg, output - _crash_msg, &num_written, NULL);
 
			CloseHandle(file);
 
		}
 
	}
 

	
 
	/* Close any possible log files */
 
	CloseConsoleLogIfActive();
 

	
 
	if (_safe_esp) {
 
#ifdef _M_AMD64
 
		ep->ContextRecord->Rip = (DWORD64)Handler2;
 
		ep->ContextRecord->Rsp = (DWORD64)_safe_esp;
 
#else
 
		ep->ContextRecord->Eip = (DWORD)Handler2;
 
		ep->ContextRecord->Esp = (DWORD)_safe_esp;
 
#endif
 
		return EXCEPTION_CONTINUE_EXECUTION;
 
	}
 

	
 

	
 
	return EXCEPTION_EXECUTE_HANDLER;
 
}
 

	
 
static void Win32InitializeExceptions(void)
 
{
 
#ifdef _M_AMD64
 
	extern void *_get_save_esp(void);
 
	_safe_esp = _get_save_esp();
 
#else
 
	_asm {
 
		mov _safe_esp, esp
 
	}
 
#endif
 

	
 
	SetUnhandledExceptionFilter(ExceptionHandler);
 
}
 
#endif /* _MSC_VER */
 

	
 
/* Code below for windows version of opendir/readdir/closedir copied and
 
 * modified from Jan Wassenberg's GPL implementation posted over at
 
 * http://www.gamedev.net/community/forums/topic.asp?topic_id=364584&whichpage=1&#2398903 */
 

	
 
/* suballocator - satisfies most requests with a reusable static instance.
 
 * this avoids hundreds of alloc/free which would fragment the heap.
 
 * To guarantee reentrancy, we fall back to malloc if the instance is
 
 * already in use (it's important to avoid suprises since this is such a
 
 * low-level routine). */
 
static DIR _global_dir;
 
static bool _global_dir_is_in_use = false;
 

	
 
static inline DIR *dir_calloc(void)
 
{
 
	DIR *d;
 

	
 
	if (_global_dir_is_in_use) {
 
		d = calloc(1, sizeof(*d));
 
	} else {
 
		_global_dir_is_in_use = true;
 
		d = &_global_dir;
 
		memset(d, 0, sizeof(*d));
 
	}
 
	return d;
 
}
 

	
 
static inline void dir_free(DIR *d)
 
{
 
	if (d == &_global_dir) {
 
		_global_dir_is_in_use = false;
 
	} else {
 
		free(d);
 
	}
 
}
 

	
 
DIR *opendir(const char *path)
 
{
 
	DIR *d;
 
	UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
 
	DWORD fa = GetFileAttributesW(OTTD2FS(path));
 

	
 
	if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
 
		d = dir_calloc();
 
		if (d != NULL) {
 
			char search_path[MAX_PATH];
 
			/* build search path for FindFirstFile */
 
			snprintf(search_path, lengthof(search_path), "%s" PATHSEP "*", path);
 
			d->hFind = FindFirstFileW(OTTD2FS(search_path), &d->fd);
 

	
 
			if (d->hFind != INVALID_HANDLE_VALUE ||
 
					GetLastError() == ERROR_NO_MORE_FILES) { // the directory is empty
 
				d->ent.dir = d;
 
				d->at_first_entry = true;
 
			} else {
 
				dir_free(d);
 
				d = NULL;
 
			}
 
		} else {
 
			errno = ENOMEM;
 
		}
 
	} else {
 
		/* path not found or not a directory */
 
		d = NULL;
 
		errno = ENOENT;
 
	}
 

	
 
	SetErrorMode(sem); // restore previous setting
 
	return d;
 
}
 

	
 
struct dirent *readdir(DIR *d)
 
{
 
	DWORD prev_err = GetLastError(); // avoid polluting last error
 

	
 
	if (d->at_first_entry) {
 
		/* the directory was empty when opened */
 
		if (d->hFind == INVALID_HANDLE_VALUE) return NULL;
 
		d->at_first_entry = false;
 
	} else if (!FindNextFileW(d->hFind, &d->fd)) { // determine cause and bail
 
		if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
 
		return NULL;
 
	}
 

	
 
	/* This entry has passed all checks; return information about it.
 
	 * (note: d_name is a pointer; see struct dirent definition) */
 
	d->ent.d_name = d->fd.cFileName;
 
	return &d->ent;
 
}
 

	
 
int closedir(DIR *d)
 
{
 
	FindClose(d->hFind);
 
	dir_free(d);
 
	return 0;
 
}
 

	
 
bool FiosIsRoot(const char *file)
 
{
 
	return file[3] == '\0'; // C:\...
 
}
 

	
 
void FiosGetDrives(void)
 
{
 
	TCHAR drives[256];
 
	const TCHAR *s;
 

	
 
	GetLogicalDriveStrings(sizeof(drives), drives);
 
	for (s = drives; *s != '\0';) {
 
		FiosItem *fios = FiosAlloc();
 
		fios->type = FIOS_TYPE_DRIVE;
 
		fios->mtime = 0;
 
		snprintf(fios->name, lengthof(fios->name),  "%c:", s[0] & 0xFF);
 
		ttd_strlcpy(fios->title, fios->name, lengthof(fios->title));
 
		while (*s++ != '\0');
 
	}
 
}
 

	
 
bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb)
 
{
 
	// hectonanoseconds between Windows and POSIX epoch
 
	static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
 
	const WIN32_FIND_DATAW *fd = &ent->dir->fd;
 
	if (fd->dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) return false;
 

	
 
	sb->st_size  = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
 
	/* UTC FILETIME to seconds-since-1970 UTC
 
	 * we just have to subtract POSIX epoch and scale down to units of seconds.
 
	 * http://www.gamedev.net/community/forums/topic.asp?topic_id=294070&whichpage=1&#1860504
 
	 * XXX - not entirely correct, since filetimes on FAT aren't UTC but local,
 
	 * this won't entirely be correct, but we use the time only for comparsion. */
 
	sb->st_mtime = (time_t)((*(uint64*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7);
 
	sb->st_mode  = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
 

	
 
	return true;
 
}
 

	
 
bool FiosGetDiskFreeSpace(const char *path, uint32 *tot)
 
{
 
	UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);  // disable 'no-disk' message box
 
	bool retval = false;
 
	TCHAR root[4];
 
	DWORD spc, bps, nfc, tnc;
 

	
 
	_sntprintf(root, lengthof(root), _T("%c:") _T(PATHSEP), path[0]);
 
	if (tot != NULL && GetDiskFreeSpace(root, &spc, &bps, &nfc, &tnc)) {
 
		*tot = ((spc * bps) * (uint64)nfc) >> 20;
 
		retval = true;
 
	}
 

	
 
	SetErrorMode(sem); // reset previous setting
 
	return retval;
 
}
 

	
 
static int ParseCommandLine(char *line, char **argv, int max_argc)
 
{
 
	int n = 0;
 

	
 
	do {
 
		// skip whitespace
 
		while (*line == ' ' || *line == '\t') line++;
 

	
 
		// end?
 
		if (*line == '\0') break;
 

	
 
		// special handling when quoted
 
		if (*line == '"') {
 
			argv[n++] = ++line;
 
			while (*line != '"') {
 
				if (*line == '\0') return n;
 
				line++;
 
			}
 
		} else {
 
			argv[n++] = line;
 
			while (*line != ' ' && *line != '\t') {
 
				if (*line == '\0') return n;
 
				line++;
 
			}
 
		}
 
		*line++ = '\0';
 
	} while (n != max_argc);
 

	
 
	return n;
 
}
 

	
 
void CreateConsole(void)
 
{
 
	HANDLE hand;
 
	CONSOLE_SCREEN_BUFFER_INFO coninfo;
 

	
 
	if (_has_console) return;
 
	_has_console = true;
 

	
 
	AllocConsole();
 

	
 
	hand = GetStdHandle(STD_OUTPUT_HANDLE);
 
	GetConsoleScreenBufferInfo(hand, &coninfo);
 
	coninfo.dwSize.Y = 500;
 
	SetConsoleScreenBufferSize(hand, coninfo.dwSize);
 

	
 
	// redirect unbuffered STDIN, STDOUT, STDERR to the console
 
#if !defined(__CYGWIN__)
 
	*stdout = *_fdopen( _open_osfhandle((intptr_t)hand, _O_TEXT), "w" );
 
	*stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "r" );
 
	*stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT), "w" );
 
#else
 
	// open_osfhandle is not in cygwin
 
	*stdout = *fdopen(1, "w" );
 
	*stdin = *fdopen(0, "r" );
 
	*stderr = *fdopen(2, "w" );
 
#endif
 

	
 
	setvbuf(stdin, NULL, _IONBF, 0);
 
	setvbuf(stdout, NULL, _IONBF, 0);
 
	setvbuf(stderr, NULL, _IONBF, 0);
 
}
 

	
 
void ShowInfo(const char *str)
 
{
 
	if (_has_console) {
 
		fprintf(stderr, str);
 
	} else {
 
		bool old;
 

	
 
		ReleaseCapture();
 
		_left_button_clicked =_left_button_down = false;
 

	
 
		old = MyShowCursor(true);
 
		if (MessageBox(GetActiveWindow(), MB_TO_WIDE(str), _T("OpenTTD"), MB_ICONINFORMATION | MB_OKCANCEL) == IDCANCEL) {
 
			CreateConsole();
 
		}
 
		MyShowCursor(old);
 
	}
 
}
 

	
 
#ifdef __MINGW32__
 
	/* _set_error_mode() constants&function (do not exist in mingw headers) */
 
	#define _OUT_TO_DEFAULT      0
 
	#define _OUT_TO_STDERR       1
 
	#define _OUT_TO_MSGBOX       2
 
	#define _REPORT_ERRMODE      3
 
	int _set_error_mode(int);
 
#endif
 

	
 
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 
	LPTSTR lpCmdLine, int nCmdShow)
 
{
 
	int argc;
 
	char *argv[64]; // max 64 command line arguments
 
	char *cmdline;
 

	
 
#if defined(UNICODE)
 
	/* For UNICODE we need to convert the commandline to char* _AND_
 
	 * save it because argv[] points into this buffer and thus needs to
 
	 * be available between subsequent calls to FS2OTTD() */
 
	char cmdlinebuf[MAX_PATH];
 
#endif
 

	
 
	cmdline = WIDE_TO_MB_BUFFER(GetCommandLine(), cmdlinebuf, lengthof(cmdlinebuf));
 

	
 
#if defined(_DEBUG)
 
	CreateConsole();
 
#endif
 

	
 
	_set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox
 

	
 
	/* setup random seed to something quite random */
 
	_random_seeds[1][0] = _random_seeds[0][0] = GetTickCount();
 
	_random_seeds[1][1] = _random_seeds[0][1] = _random_seeds[0][0] * 0x1234567;
 
	SeedMT(_random_seeds[0][0]);
 

	
 
	argc = ParseCommandLine(cmdline, argv, lengthof(argv));
 

	
 
#if defined(WIN32_EXCEPTION_TRACKER)
 
	Win32InitializeExceptions();
 
#endif
 

	
 
#if defined(WIN32_EXCEPTION_TRACKER_DEBUG)
 
	_try {
 
		LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep);
 
#endif
 
		ttd_main(argc, argv);
 

	
 
#if defined(WIN32_EXCEPTION_TRACKER_DEBUG)
 
	} _except (ExceptionHandler(_exception_info())) {}
 
#endif
 

	
 
	return 0;
 
}
 

	
 
void DeterminePaths(void)
 
{
 
	char *s, *cfg;
 
	wchar_t path[MAX_PATH];
 

	
 
	_paths.personal_dir = _paths.game_data_dir = cfg = malloc(MAX_PATH);
 
	GetCurrentDirectoryW(MAX_PATH - 1, path);
 
	convert_from_fs(path, cfg, MAX_PATH);
 

	
 
	cfg[0] = toupper(cfg[0]);
 
	s = strchr(cfg, '\0');
 
	if (s[-1] != '\\') strcpy(s, "\\");
 

	
 
	_paths.save_dir = str_fmt("%ssave", cfg);
 
	_paths.autosave_dir = str_fmt("%s\\autosave", _paths.save_dir);
 
	_paths.scenario_dir = str_fmt("%sscenario", cfg);
 
	_paths.heightmap_dir = str_fmt("%sscenario\\heightmap", cfg);
 
	_paths.gm_dir = str_fmt("%sgm\\", cfg);
 
	_paths.data_dir = str_fmt("%sdata\\", cfg);
 
	_paths.lang_dir = str_fmt("%slang\\", cfg);
 

	
 
	if (_config_file == NULL)
 
		_config_file = str_fmt("%sopenttd.cfg", _paths.personal_dir);
 

	
 
	_highscore_file = str_fmt("%shs.dat", _paths.personal_dir);
 
	_log_file = str_fmt("%sopenttd.log", _paths.personal_dir);
 

	
 
	// make (auto)save and scenario folder
 
	CreateDirectoryW(OTTD2FS(_paths.save_dir), NULL);
 
	CreateDirectoryW(OTTD2FS(_paths.autosave_dir), NULL);
 
	CreateDirectoryW(OTTD2FS(_paths.scenario_dir), NULL);
 
	CreateDirectoryW(OTTD2FS(_paths.heightmap_dir), NULL);
 
}
 

	
 
/**
 
 * Insert a chunk of text from the clipboard onto the textbuffer. Get TEXT clipboard
 
 * and append this up to the maximum length (either absolute or screenlength). If maxlength
 
 * is zero, we don't care about the screenlength but only about the physical length of the string
 
 * @param tb @Textbuf type to be changed
 
 * @return Return true on successfull change of Textbuf, or false otherwise
 
 */
 
bool InsertTextBufferClipboard(Textbuf *tb)
 
{
 
	HGLOBAL cbuf;
 
	char utf8_buf[512];
 
	const char *ptr;
 

	
 
	WChar c;
 
	uint16 width, length;
 

	
 
	if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
 
		const char *ret;
 

	
 
		OpenClipboard(NULL);
 
		cbuf = GetClipboardData(CF_UNICODETEXT);
 

	
 
		ptr = GlobalLock(cbuf);
 
		ret = convert_from_fs((wchar_t*)ptr, utf8_buf, lengthof(utf8_buf));
 
		GlobalUnlock(cbuf);
 
		CloseClipboard();
 

	
 
		if (*ret == '\0') return false;
 
	} else if (IsClipboardFormatAvailable(CF_TEXT)) {
 
		OpenClipboard(NULL);
 
		cbuf = GetClipboardData(CF_TEXT);
 

	
 
		ptr = GlobalLock(cbuf);
 
		ttd_strlcpy(utf8_buf, ptr, lengthof(utf8_buf));
 
		GlobalUnlock(cbuf);
 
		CloseClipboard();
 
	} else {
 
		return false;
 
	}
 

	
 
	width = length = 0;
 

	
 
	for (ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
 
		byte charwidth;
 

	
 
		if (!IsPrintable(c)) break;
 
		if (tb->length + length >= tb->maxlength - 1) break;
 
		charwidth = GetCharacterWidth(FS_NORMAL, c);
 

	
 
		if (tb->maxwidth != 0 && width + tb->width + charwidth > tb->maxwidth) break;
 

	
 
		width += charwidth;
 
		length += Utf8CharLen(c);
 
	}
 

	
 
	if (length == 0) return false;
 

	
 
	memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->length - tb->caretpos);
 
	memcpy(tb->buf + tb->caretpos, utf8_buf, length);
 
	tb->width += width;
 
	tb->caretxoffs += width;
 

	
 
	tb->length += length;
 
	tb->caretpos += length;
 
	tb->buf[tb->length] = '\0'; // terminating zero
 

	
 
	return true;
 
}
 

	
 

	
 
void CSleep(int milliseconds)
 
{
 
	Sleep(milliseconds);
 
}
 

	
 

	
 
// Utility function to get the current timestamp in milliseconds
 
// Useful for profiling
 
int64 GetTS(void)
 
{
 
	static double freq;
 
	__int64 value;
 
	if (!freq) {
 
		QueryPerformanceFrequency((LARGE_INTEGER*)&value);
 
		freq = (double)1000000 / value;
 
	}
 
	QueryPerformanceCounter((LARGE_INTEGER*)&value);
 
	return (__int64)(value * freq);
 
}
 

	
 
/** Convert from OpenTTD's encoding to that of the local environment in
 
 * UNICODE. OpenTTD encoding is UTF8, local is wide-char
 
 * @param name pointer to a valid string that will be converted
 
 * @param utf16_buf pointer to a valid wide-char buffer that will receive the
 
 * converted string
 
 * @param buflen length in wide characters of the receiving buffer
 
 * @return pointer to utf16_buf. If conversion fails the string is of zero-length */
 
wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen)
 
{
 
	int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, utf16_buf, buflen);
 
	if (len == 0) {
 
		DEBUG(misc, 0, "[utf8] error converting '%s'. Errno %d", name, GetLastError());
 
		utf16_buf[0] = '\0';
 
	}
 

	
 
	return utf16_buf;
 
}
 

	
 
/** Convert from OpenTTD's encoding to that of the local environment in
 
 * UNICODE. OpenTTD encoding is UTF8, local is wide-char.
 
 * The returned value's contents can only be guaranteed until the next call to
 
 * this function. So if the value is needed for anything else, use convert_from_fs
 
 * @param name pointer to a valid string that will be converted
 
 * @return pointer to the converted string; if failed string is of zero-length */
 
const wchar_t *OTTD2FS(const char *name)
 
{
 
	static wchar_t utf16_buf[512];
 
	return convert_to_fs(name, utf16_buf, lengthof(utf16_buf));
 
}
 

	
 

	
 
/** Convert to OpenTTD's encoding from that of the local environment in
 
 * UNICODE. OpenTTD encoding is UTF8, local is wide-char
 
 * @param name pointer to a valid string that will be converted
 
 * @param utf8_buf pointer to a valid buffer that will receive the converted string
 
 * @param buflen length in characters of the receiving buffer
 
 * @return pointer to utf8_buf. If conversion fails the string is of zero-length */
 
char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen)
 
{
 
	int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, buflen, NULL, NULL);
 
	if (len == 0) {
 
		DEBUG(misc, 0, "[utf8] error converting wide-string. Errno %d", GetLastError());
 
		utf8_buf[0] = '\0';
 
	}
 

	
 
	return utf8_buf;
 
}
 

	
 
/** Convert to OpenTTD's encoding from that of the local environment in
 
 * UNICODE. OpenTTD encoding is UTF8, local is wide-char.
 
 * The returned value's contents can only be guaranteed until the next call to
 
 * this function. So if the value is needed for anything else, use convert_from_fs
 
 * @param name pointer to a valid string that will be converted
 
 * @return pointer to the converted string; if failed string is of zero-length */
 
const char *FS2OTTD(const wchar_t *name)
 
{
 
	static char utf8_buf[512];
 
	return convert_from_fs(name, utf8_buf, lengthof(utf8_buf));
 
}
 

	
 
/** Our very own SHGetFolderPath function for support of windows operating
 
 * systems that don't have this function (eg Win9x, etc.). We try using the
 
 * native function, and if that doesn't exist we will try a more crude approach
 
 * of environment variables and hope for the best */
 
HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
 
{
 
	static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) = NULL;
 
	static bool first_time = true;
 

	
 
	/* We only try to load the library one time; if it fails, it fails */
 
	if (first_time) {
 
#if defined(UNICODE)
 
# define W(x) x "W"
 
#else
 
# define W(x) x "A"
 
#endif
 
		if (!LoadLibraryList((Function*)&SHGetFolderPath, "SHFolder.dll\0" W("SHGetFolderPath") "\0\0")) {
 
			DEBUG(misc, 0, "Unable to load " W("SHGetFolderPath") "from SHFolder.dll");
 
		}
 
#undef W
 
		first_time = false;
 
	}
 

	
 
	if (SHGetFolderPath != NULL) return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
 

	
 
	/* SHGetFolderPath doesn't exist, try a more conservative approach,
 
	 * eg environment variables. This is only included for legacy modes
 
	 * MSDN says: that 'pszPath' is a "Pointer to a null-terminated string of
 
	 * length MAX_PATH which will receive the path" so let's assume that
 
	 * Windows 95 with Internet Explorer 5.0, Windows 98 with Internet Explorer 5.0,
 
	 * Windows 98 Second Edition (SE), Windows NT 4.0 with Internet Explorer 5.0,
 
	 * Windows NT 4.0 with Service Pack 4 (SP4) */
 
	{
 
		DWORD ret;
 
		switch (csidl) {
 
			case CSIDL_FONTS: /* Get the system font path, eg %WINDIR%\Fonts */
 
				ret = GetEnvironmentVariable(_T("WINDIR"), pszPath, MAX_PATH);
 
				if (ret == 0) break;
 
				_tcsncat(pszPath, _T("\\Fonts"), MAX_PATH);
 

	
 
				return (HRESULT)0;
 
				break;
 
			/* XXX - other types to go here when needed... */
 
		}
 
	}
 

	
 
	return E_INVALIDARG;
 
}
src/window.c
Show inline comments
 
deleted file
src/window.cpp
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "stdafx.h"
 
#include <stdarg.h>
 
#include "openttd.h"
 
#include "debug.h"
 
#include "functions.h"
 
#include "map.h"
 
#include "player.h"
 
#include "window.h"
 
#include "gfx.h"
 
#include "viewport.h"
 
#include "console.h"
 
#include "variables.h"
 
#include "table/sprites.h"
 
#include "genworld.h"
 

	
 
// delta between mouse cursor and upper left corner of dragged window
 
static Point _drag_delta;
 

	
 
static Window _windows[25];
 
Window *_z_windows[lengthof(_windows)];
 
Window **_last_z_window; ///< always points to the next free space in the z-array
 

	
 
void CDECL SetWindowWidgetsDisabledState(Window *w, bool disab_stat, int widgets, ...)
 
{
 
	va_list wdg_list;
 

	
 
	va_start(wdg_list, widgets);
 

	
 
	while (widgets != WIDGET_LIST_END) {
 
		SetWindowWidgetDisabledState(w, widgets, disab_stat);
 
		widgets = va_arg(wdg_list, int);
 
	}
 

	
 
	va_end(wdg_list);
 
}
 

	
 
void CDECL SetWindowWidgetsHiddenState(Window *w, bool hidden_stat, int widgets, ...)
 
{
 
	va_list wdg_list;
 

	
 
	va_start(wdg_list, widgets);
 

	
 
	while (widgets != WIDGET_LIST_END) {
 
		SetWindowWidgetHiddenState(w, widgets, hidden_stat);
 
		widgets = va_arg(wdg_list, int);
 
	}
 

	
 
	va_end(wdg_list);
 
}
 

	
 
void CDECL SetWindowWidgetsLoweredState(Window *w, bool lowered_stat, int widgets, ...)
 
{
 
	va_list wdg_list;
 

	
 
	va_start(wdg_list, widgets);
 

	
 
	while (widgets != WIDGET_LIST_END) {
 
		SetWindowWidgetLoweredState(w, widgets, lowered_stat);
 
		widgets = va_arg(wdg_list, int);
 
	}
 

	
 
	va_end(wdg_list);
 
}
 

	
 
void RaiseWindowButtons(Window *w)
 
{
 
	uint i;
 

	
 
	for (i = 0; i < w->widget_count; i++) {
 
		if (IsWindowWidgetLowered(w, i)) {
 
			RaiseWindowWidget(w, i);
 
			InvalidateWidget(w, i);
 
		}
 
	}
 
}
 

	
 
void HandleButtonClick(Window *w, byte widget)
 
{
 
	LowerWindowWidget(w, widget);
 
	w->flags4 |= 5 << WF_TIMEOUT_SHL;
 
	InvalidateWidget(w, widget);
 
}
 

	
 

	
 
static void StartWindowDrag(Window *w);
 
static void StartWindowSizing(Window *w);
 

	
 
static void DispatchLeftClickEvent(Window *w, int x, int y)
 
{
 
	WindowEvent e;
 
	const Widget *wi;
 

	
 
	e.we.click.pt.x = x;
 
	e.we.click.pt.y = y;
 
	e.event = WE_CLICK;
 

	
 
	if (w->desc_flags & WDF_DEF_WIDGET) {
 
		e.we.click.widget = GetWidgetFromPos(w, x, y);
 
		if (e.we.click.widget < 0) return; /* exit if clicked outside of widgets */
 

	
 
		/* don't allow any interaction if the button has been disabled */
 
		if (IsWindowWidgetDisabled(w, e.we.click.widget)) return;
 

	
 
		wi = &w->widget[e.we.click.widget];
 

	
 
		if (wi->type & WWB_MASK) {
 
			/* special widget handling for buttons*/
 
			switch (wi->type) {
 
				case WWT_PANEL   | WWB_PUSHBUTTON: /* WWT_PUSHBTN */
 
				case WWT_IMGBTN  | WWB_PUSHBUTTON: /* WWT_PUSHIMGBTN */
 
				case WWT_TEXTBTN | WWB_PUSHBUTTON: /* WWT_PUSHTXTBTN */
 
					HandleButtonClick(w, e.we.click.widget);
 
					break;
 
			}
 
		} else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) {
 
			ScrollbarClickHandler(w, wi, e.we.click.pt.x, e.we.click.pt.y);
 
		}
 

	
 
		if (w->desc_flags & WDF_STD_BTN) {
 
			if (e.we.click.widget == 0) { /* 'X' */
 
				DeleteWindow(w);
 
				return;
 
			}
 

	
 
			if (e.we.click.widget == 1) { /* 'Title bar' */
 
				StartWindowDrag(w);
 
				return;
 
			}
 
		}
 

	
 
		if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) {
 
			StartWindowSizing(w);
 
			InvalidateWidget(w, e.we.click.widget);
 
			return;
 
		}
 

	
 
		if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) {
 
			w->flags4 ^= WF_STICKY;
 
			InvalidateWidget(w, e.we.click.widget);
 
			return;
 
		}
 
	}
 

	
 
	w->wndproc(w, &e);
 
}
 

	
 
static void DispatchRightClickEvent(Window *w, int x, int y)
 
{
 
	WindowEvent e;
 

	
 
	/* default tooltips handler? */
 
	if (w->desc_flags & WDF_STD_TOOLTIPS) {
 
		e.we.click.widget = GetWidgetFromPos(w, x, y);
 
		if (e.we.click.widget < 0)
 
			return; /* exit if clicked outside of widgets */
 

	
 
		if (w->widget[e.we.click.widget].tooltips != 0) {
 
			GuiShowTooltips(w->widget[e.we.click.widget].tooltips);
 
			return;
 
		}
 
	}
 

	
 
	e.event = WE_RCLICK;
 
	e.we.click.pt.x = x;
 
	e.we.click.pt.y = y;
 
	w->wndproc(w, &e);
 
}
 

	
 
/** Dispatch the mousewheel-action to the window which will scroll any
 
 * compatible scrollbars if the mouse is pointed over the bar or its contents
 
 * @param *w Window
 
 * @param widget the widget where the scrollwheel was used
 
 * @param wheel scroll up or down
 
 */
 
static void DispatchMouseWheelEvent(Window *w, int widget, int wheel)
 
{
 
	const Widget *wi1, *wi2;
 
	Scrollbar *sb;
 

	
 
	if (widget < 0) return;
 

	
 
	wi1 = &w->widget[widget];
 
	wi2 = &w->widget[widget + 1];
 

	
 
	/* The listbox can only scroll if scrolling was done on the scrollbar itself,
 
	 * or on the listbox (and the next item is (must be) the scrollbar)
 
	 * XXX - should be rewritten as a widget-dependent scroller but that's
 
	 * not happening until someone rewrites the whole widget-code */
 
	if ((sb = &w->vscroll,  wi1->type == WWT_SCROLLBAR)  || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR)  ||
 
			(sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) {
 

	
 
		if (sb->count > sb->cap) {
 
			int pos = clamp(sb->pos + wheel, 0, sb->count - sb->cap);
 
			if (pos != sb->pos) {
 
				sb->pos = pos;
 
				SetWindowDirty(w);
 
			}
 
		}
 
	}
 
}
 

	
 
static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right, int bottom);
 

	
 
void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
 
{
 
	Window* const *wz;
 
	DrawPixelInfo bk;
 
	_cur_dpi = &bk;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		if (right > w->left &&
 
				bottom > w->top &&
 
				left < w->left + w->width &&
 
				top < w->top + w->height) {
 
			DrawOverlappedWindow(wz, left, top, right, bottom);
 
		}
 
	}
 
}
 

	
 
static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right, int bottom)
 
{
 
	Window* const *vz = wz;
 
	int x;
 

	
 
	while (++vz != _last_z_window) {
 
		const Window *v = *vz;
 

	
 
		if (right > v->left &&
 
				bottom > v->top &&
 
				left < v->left + v->width &&
 
				top < v->top + v->height) {
 
			if (left < (x=v->left)) {
 
				DrawOverlappedWindow(wz, left, top, x, bottom);
 
				DrawOverlappedWindow(wz, x, top, right, bottom);
 
				return;
 
			}
 

	
 
			if (right > (x=v->left + v->width)) {
 
				DrawOverlappedWindow(wz, left, top, x, bottom);
 
				DrawOverlappedWindow(wz, x, top, right, bottom);
 
				return;
 
			}
 

	
 
			if (top < (x=v->top)) {
 
				DrawOverlappedWindow(wz, left, top, right, x);
 
				DrawOverlappedWindow(wz, left, x, right, bottom);
 
				return;
 
			}
 

	
 
			if (bottom > (x=v->top + v->height)) {
 
				DrawOverlappedWindow(wz, left, top, right, x);
 
				DrawOverlappedWindow(wz, left, x, right, bottom);
 
				return;
 
			}
 

	
 
			return;
 
		}
 
	}
 

	
 
	{
 
		DrawPixelInfo *dp = _cur_dpi;
 
		dp->width = right - left;
 
		dp->height = bottom - top;
 
		dp->left = left - (*wz)->left;
 
		dp->top = top - (*wz)->top;
 
		dp->pitch = _screen.pitch;
 
		dp->dst_ptr = _screen.dst_ptr + top * _screen.pitch + left;
 
		dp->zoom = 0;
 
		CallWindowEventNP(*wz, WE_PAINT);
 
	}
 
}
 

	
 
void CallWindowEventNP(Window *w, int event)
 
{
 
	WindowEvent e;
 

	
 
	e.event = event;
 
	w->wndproc(w, &e);
 
}
 

	
 
void SetWindowDirty(const Window *w)
 
{
 
	if (w == NULL) return;
 
	SetDirtyBlocks(w->left, w->top, w->left + w->width, w->top + w->height);
 
}
 

	
 
/** Find the Window whose parent pointer points to this window
 
 * @parent w Window to find child of
 
 * @return return a Window pointer that is the child of w, or NULL otherwise */
 
static Window *FindChildWindow(const Window *w)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *v = *wz;
 
		if (v->parent == w) return v;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/** Find the z-value of a window. A window must already be open
 
 * or the behaviour is undefined but function should never fail */
 
Window **FindWindowZPosition(const Window *w)
 
{
 
	Window **wz;
 

	
 
	for (wz = _z_windows; wz != _last_z_window; wz++) {
 
		if (*wz == w) return wz;
 
	}
 

	
 
	DEBUG(misc, 3, "Window (class %d, number %d) is not open, probably removed by recursive calls",
 
		w->window_class, w->window_number);
 
	return NULL;
 
}
 

	
 
void DeleteWindow(Window *w)
 
{
 
	Window *v;
 
	Window **wz;
 
	if (w == NULL) return;
 

	
 
	/* Delete any children a window might have in a head-recursive manner */
 
	v = FindChildWindow(w);
 
	if (v != NULL) DeleteWindow(v);
 

	
 
	if (_thd.place_mode != VHM_NONE &&
 
			_thd.window_class == w->window_class &&
 
			_thd.window_number == w->window_number) {
 
		ResetObjectToPlace();
 
	}
 

	
 
	CallWindowEventNP(w, WE_DESTROY);
 
	if (w->viewport != NULL) DeleteWindowViewport(w);
 

	
 
	SetWindowDirty(w);
 
	free(w->widget);
 
	w->widget = NULL;
 
	w->widget_count = 0;
 
	w->parent = NULL;
 

	
 
	/* Find the window in the z-array, and effectively remove it
 
	 * by moving all windows after it one to the left */
 
	wz = FindWindowZPosition(w);
 
	if (wz == NULL) return;
 
	memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz);
 
	_last_z_window--;
 
}
 

	
 
Window *FindWindowById(WindowClass cls, WindowNumber number)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class == cls && w->window_number == number) return w;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
void DeleteWindowById(WindowClass cls, WindowNumber number)
 
{
 
	DeleteWindow(FindWindowById(cls, number));
 
}
 

	
 
void DeleteWindowByClass(WindowClass cls)
 
{
 
	Window* const *wz;
 

	
 
restart_search:
 
	/* When we find the window to delete, we need to restart the search
 
	 * as deleting this window could cascade in deleting (many) others
 
	 * anywhere in the z-array */
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class == cls) {
 
			DeleteWindow(w);
 
			goto restart_search;
 
		}
 
	}
 
}
 

	
 
/** Delete all windows of a player. We identify windows of a player
 
 * by looking at the caption colour. If it is equal to the player ID
 
 * then we say the window belongs to the player and should be deleted
 
 * @param id PlayerID player identifier */
 
void DeletePlayerWindows(PlayerID id)
 
{
 
	Window* const *wz;
 

	
 
restart_search:
 
	/* When we find the window to delete, we need to restart the search
 
	 * as deleting this window could cascade in deleting (many) others
 
	 * anywhere in the z-array */
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->caption_color == id) {
 
			DeleteWindow(w);
 
			goto restart_search;
 
		}
 
	}
 

	
 
	/* Also delete the player specific windows, that don't have a player-colour */
 
	DeleteWindowById(WC_BUY_COMPANY, id);
 
}
 

	
 
/** Change the owner of all the windows one player can take over from another
 
 * player in the case of a company merger. Do not change ownership of windows
 
 * that need to be deleted once takeover is complete
 
 * @param old_player PlayerID of original owner of the window
 
 * @param new_player PlayerID of the new owner of the window */
 
void ChangeWindowOwner(PlayerID old_player, PlayerID new_player)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 

	
 
		if (w->caption_color != old_player)      continue;
 
		if (w->window_class == WC_PLAYER_COLOR)  continue;
 
		if (w->window_class == WC_FINANCES)      continue;
 
		if (w->window_class == WC_STATION_LIST)  continue;
 
		if (w->window_class == WC_TRAINS_LIST)   continue;
 
		if (w->window_class == WC_ROADVEH_LIST)  continue;
 
		if (w->window_class == WC_SHIPS_LIST)    continue;
 
		if (w->window_class == WC_AIRCRAFT_LIST) continue;
 
		if (w->window_class == WC_BUY_COMPANY)   continue;
 
		if (w->window_class == WC_COMPANY)       continue;
 

	
 
		w->caption_color = new_player;
 
	}
 
}
 

	
 
static void BringWindowToFront(const Window *w);
 

	
 
/** Find a window and make it the top-window on the screen. The window
 
 * gets a white border for a brief period of time to visualize its
 
 * "activation"
 
 * @return a pointer to the window thus activated */
 
Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
 
{
 
	Window *w = FindWindowById(cls, number);
 

	
 
	if (w != NULL) {
 
		w->flags4 |= WF_WHITE_BORDER_MASK;
 
		BringWindowToFront(w);
 
		SetWindowDirty(w);
 
	}
 

	
 
	return w;
 
}
 

	
 
static inline bool IsVitalWindow(const Window *w)
 
{
 
	WindowClass wc = w->window_class;
 
	return (wc == WC_MAIN_TOOLBAR || wc == WC_STATUS_BAR || wc == WC_NEWS_WINDOW || wc == WC_SEND_NETWORK_MSG);
 
}
 

	
 
/** On clicking on a window, make it the frontmost window of all. However
 
 * there are certain windows that always need to be on-top; these include
 
 * - Toolbar, Statusbar (always on)
 
 * - New window, Chatbar (only if open)
 
 * The window is marked dirty for a repaint if the window is actually moved
 
 * @param w window that is put into the foreground
 
 * @return pointer to the window, the same as the input pointer
 
 */
 
static void BringWindowToFront(const Window *w)
 
{
 
	Window *tempz;
 
	Window **wz = FindWindowZPosition(w);
 
	Window **vz = _last_z_window;
 

	
 
	/* Bring the window just below the vital windows */
 
	do {
 
		if (--vz < _z_windows) return;
 
	} while (IsVitalWindow(*vz));
 

	
 
	if (wz == vz) return; // window is already in the right position
 
	assert(wz < vz);
 

	
 
	tempz = *wz;
 
	memmove(wz, wz + 1, (byte*)vz - (byte*)wz);
 
	*vz = tempz;
 

	
 
	SetWindowDirty(w);
 
}
 

	
 
/** We have run out of windows, so find a suitable candidate for replacement.
 
 * Keep all important windows intact. These are
 
 * - Main window (gamefield), Toolbar, Statusbar (always on)
 
 * - News window, Chatbar (when on)
 
 * - Any sticked windows since we wanted to keep these
 
 * @return w pointer to the window that is going to be deleted
 
 */
 
static Window *FindDeletableWindow(void)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w) && !(w->flags4 & WF_STICKY)) {
 
			return w;
 
		}
 
	}
 
	return NULL;
 
}
 

	
 
/** A window must be freed, and all are marked as important windows. Ease the
 
 * restriction a bit by allowing to delete sticky windows. Keep important/vital
 
 * windows intact (Main window, Toolbar, Statusbar, News Window, Chatbar)
 
 * Start finding an appropiate candidate from the lowest z-values (bottom)
 
 * @see FindDeletableWindow()
 
 * @return w Pointer to the window that is being deleted
 
 */
 
static Window *ForceFindDeletableWindow(void)
 
{
 
	Window* const *wz;
 

	
 
	for (wz = _z_windows;; wz++) {
 
		Window *w = *wz;
 
		assert(wz < _last_z_window);
 
		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) return w;
 
	}
 
}
 

	
 
bool IsWindowOfPrototype(const Window *w, const Widget *widget)
 
{
 
	return (w->original_widget == widget);
 
}
 

	
 
/* Copies 'widget' to 'w->widget' to allow for resizable windows */
 
void AssignWidgetToWindow(Window *w, const Widget *widget)
 
{
 
	w->original_widget = widget;
 

	
 
	if (widget != NULL) {
 
		uint index = 1;
 
		const Widget *wi;
 

	
 
		for (wi = widget; wi->type != WWT_LAST; wi++) index++;
 

	
 
		w->widget = realloc(w->widget, sizeof(*w->widget) * index);
 
		memcpy(w->widget, widget, sizeof(*w->widget) * index);
 
		w->widget_count = index - 1;
 
	} else {
 
		w->widget = NULL;
 
		w->widget_count = 0;
 
	}
 
}
 

	
 
static Window *FindFreeWindow(void)
 
{
 
	Window *w;
 

	
 
	for (w = _windows; w < endof(_windows); w++) {
 
		Window* const *wz;
 
		bool window_in_use = false;
 

	
 
		FOR_ALL_WINDOWS(wz) {
 
			if (*wz == w) {
 
				window_in_use = true;
 
				break;
 
			}
 
		}
 

	
 
		if (!window_in_use) return w;
 
	}
 

	
 
	assert(_last_z_window == endof(_z_windows));
 
	return NULL;
 
}
 

	
 
/* Open a new window.
 
 * This function is called from AllocateWindow() or AllocateWindowDesc()
 
 * See descriptions for those functions for usage
 
 * See AllocateWindow() for description of arguments.
 
 * Only addition here is window_number, which is the window_number being assigned to the new window
 
 */
 
static Window *LocalAllocateWindow(
 
							int x, int y, int width, int height,
 
							WindowProc *proc, WindowClass cls, const Widget *widget, int window_number)
 
{
 
	Window *w = FindFreeWindow();
 

	
 
	/* We have run out of windows, close one and use that as the place for our new one */
 
	if (w == NULL) {
 
		w = FindDeletableWindow();
 
		if (w == NULL) w = ForceFindDeletableWindow();
 
		DeleteWindow(w);
 
	}
 

	
 
	// Set up window properties
 
	memset(w, 0, sizeof(*w));
 
	w->window_class = cls;
 
	w->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border
 
	w->caption_color = 0xFF;
 
	w->left = x;
 
	w->top = y;
 
	w->width = width;
 
	w->height = height;
 
	w->wndproc = proc;
 
	AssignWidgetToWindow(w, widget);
 
	w->resize.width = width;
 
	w->resize.height = height;
 
	w->resize.step_width = 1;
 
	w->resize.step_height = 1;
 
	w->window_number = window_number;
 

	
 
	{
 
		Window **wz = _last_z_window;
 

	
 
		/* Hacky way of specifying always-on-top windows. These windows are
 
		 * always above other windows because they are moved below them.
 
		 * status-bar is above news-window because it has been created earlier.
 
		 * Also, as the chat-window is excluded from this, it will always be
 
		 * the last window, thus always on top.
 
		 * XXX - Yes, ugly, probably needs something like w->always_on_top flag
 
		 * to implement correctly, but even then you need some kind of distinction
 
		 * between on-top of chat/news and status windows, because these conflict */
 
		if (wz != _z_windows && w->window_class != WC_SEND_NETWORK_MSG) {
 
			if (FindWindowById(WC_MAIN_TOOLBAR, 0)     != NULL) wz--;
 
			if (FindWindowById(WC_STATUS_BAR, 0)       != NULL) wz--;
 
			if (FindWindowById(WC_NEWS_WINDOW, 0)      != NULL) wz--;
 
			if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--;
 

	
 
			assert(wz >= _z_windows);
 
			if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz);
 
		}
 

	
 
		*wz = w;
 
		_last_z_window++;
 
	}
 

	
 
	SetWindowDirty(w);
 
	CallWindowEventNP(w, WE_CREATE);
 

	
 
	return w;
 
}
 

	
 
/**
 
 * Open a new window. If there is no space for a new window, close an open
 
 * window. Try to avoid stickied windows, but if there is no else, close one of
 
 * those as well. Then make sure all created windows are below some always-on-top
 
 * ones. Finally set all variables and call the WE_CREATE event
 
 * @param x offset in pixels from the left of the screen
 
 * @param y offset in pixels from the top of the screen
 
 * @param width width in pixels of the window
 
 * @param height height in pixels of the window
 
 * @param *proc @see WindowProc function to call when any messages/updates happen to the window
 
 * @param cls @see WindowClass class of the window, used for identification and grouping
 
 * @param *widget @see Widget pointer to the window layout and various elements
 
 * @return @see Window pointer of the newly created window
 
 */
 
Window *AllocateWindow(
 
							int x, int y, int width, int height,
 
							WindowProc *proc, WindowClass cls, const Widget *widget)
 
{
 
	return LocalAllocateWindow(x, y, width, height, proc, cls, widget, 0);
 
}
 

	
 
typedef struct SizeRect {
 
	int left,top,width,height;
 
} SizeRect;
 

	
 

	
 
static SizeRect _awap_r;
 

	
 
static bool IsGoodAutoPlace1(int left, int top)
 
{
 
	int right,bottom;
 
	Window* const *wz;
 

	
 
	_awap_r.left= left;
 
	_awap_r.top = top;
 
	right = _awap_r.width + left;
 
	bottom = _awap_r.height + top;
 

	
 
	if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height)
 
		return false;
 

	
 
	// Make sure it is not obscured by any window.
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		if (w->window_class == WC_MAIN_WINDOW) continue;
 

	
 
		if (right > w->left &&
 
				w->left + w->width > left &&
 
				bottom > w->top &&
 
				w->top + w->height > top) {
 
			return false;
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
static bool IsGoodAutoPlace2(int left, int top)
 
{
 
	int width,height;
 
	Window* const *wz;
 

	
 
	_awap_r.left= left;
 
	_awap_r.top = top;
 
	width = _awap_r.width;
 
	height = _awap_r.height;
 

	
 
	if (left < -(width>>2) || left > _screen.width - (width>>1)) return false;
 
	if (top < 22 || top > _screen.height - (height>>2)) return false;
 

	
 
	// Make sure it is not obscured by any window.
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		if (w->window_class == WC_MAIN_WINDOW) continue;
 

	
 
		if (left + width > w->left &&
 
				w->left + w->width > left &&
 
				top + height > w->top &&
 
				w->top + w->height > top) {
 
			return false;
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
static Point GetAutoPlacePosition(int width, int height)
 
{
 
	Window* const *wz;
 
	Point pt;
 

	
 
	_awap_r.width = width;
 
	_awap_r.height = height;
 

	
 
	if (IsGoodAutoPlace1(0, 24)) goto ok_pos;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		if (w->window_class == WC_MAIN_WINDOW) continue;
 

	
 
		if (IsGoodAutoPlace1(w->left+w->width+2,w->top)) goto ok_pos;
 
		if (IsGoodAutoPlace1(w->left-   width-2,w->top)) goto ok_pos;
 
		if (IsGoodAutoPlace1(w->left,w->top+w->height+2)) goto ok_pos;
 
		if (IsGoodAutoPlace1(w->left,w->top-   height-2)) goto ok_pos;
 
		if (IsGoodAutoPlace1(w->left+w->width+2,w->top+w->height-height)) goto ok_pos;
 
		if (IsGoodAutoPlace1(w->left-   width-2,w->top+w->height-height)) goto ok_pos;
 
		if (IsGoodAutoPlace1(w->left+w->width-width,w->top+w->height+2)) goto ok_pos;
 
		if (IsGoodAutoPlace1(w->left+w->width-width,w->top-   height-2)) goto ok_pos;
 
	}
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		if (w->window_class == WC_MAIN_WINDOW) continue;
 

	
 
		if (IsGoodAutoPlace2(w->left+w->width+2,w->top)) goto ok_pos;
 
		if (IsGoodAutoPlace2(w->left-   width-2,w->top)) goto ok_pos;
 
		if (IsGoodAutoPlace2(w->left,w->top+w->height+2)) goto ok_pos;
 
		if (IsGoodAutoPlace2(w->left,w->top-   height-2)) goto ok_pos;
 
	}
 

	
 
	{
 
		int left=0,top=24;
 

	
 
restart:;
 
		FOR_ALL_WINDOWS(wz) {
 
			const Window *w = *wz;
 

	
 
			if (w->left == left && w->top == top) {
 
				left += 5;
 
				top += 5;
 
				goto restart;
 
			}
 
		}
 

	
 
		pt.x = left;
 
		pt.y = top;
 
		return pt;
 
	}
 

	
 
ok_pos:;
 
	pt.x = _awap_r.left;
 
	pt.y = _awap_r.top;
 
	return pt;
 
}
 

	
 
static Window *LocalAllocateWindowDesc(const WindowDesc *desc, int window_number)
 
{
 
	Point pt;
 
	Window *w;
 

	
 
	/* By default position a child window at an offset of 10/10 of its parent.
 
	 * However if it falls too extremely outside window positions, reposition
 
	 * it to an automatic place */
 
	if (desc->parent_cls != 0 /* WC_MAIN_WINDOW */ &&
 
			(w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
 
			w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
 

	
 
		pt.x = w->left + 10;
 
		if (pt.x > _screen.width + 10 - desc->width) {
 
			pt.x = (_screen.width + 10 - desc->width) - 20;
 
		}
 
		pt.y = w->top + 10;
 
	} else {
 
		switch (desc->left) {
 
			case WDP_ALIGN_TBR: { /* Align the right side with the top toolbar */
 
				w = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
				pt.x = (w->left + w->width) - desc->width;
 
			}	break;
 
			case WDP_ALIGN_TBL: /* Align the left side with the top toolbar */
 
				pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left;
 
				break;
 
			case WDP_AUTO: /* Find a good automatic position for the window */
 
				pt = GetAutoPlacePosition(desc->width, desc->height);
 
				goto allocate_window;
 
			case WDP_CENTER: /* Centre the window horizontally */
 
				pt.x = (_screen.width - desc->width) / 2;
 
				break;
 
			default:
 
				pt.x = desc->left;
 
				if (pt.x < 0) pt.x += _screen.width; // negative is from right of the screen
 
		}
 

	
 
		switch (desc->top) {
 
			case WDP_CENTER: /* Centre the window vertically */
 
				pt.y = (_screen.height - desc->height) / 2;
 
				break;
 
			/* WDP_AUTO sets the position at once and is controlled by desc->left.
 
			 * Both left and top must be set to WDP_AUTO */
 
			case WDP_AUTO:
 
				NOT_REACHED();
 
				assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO);
 
				/* fallthrough */
 
			default:
 
				pt.y = desc->top;
 
				if (pt.y < 0) pt.y += _screen.height; // negative is from bottom of the screen
 
				break;
 
		}
 
	}
 

	
 
allocate_window:
 
	w = LocalAllocateWindow(pt.x, pt.y, desc->width, desc->height, desc->proc, desc->cls, desc->widgets, window_number);
 
	w->desc_flags = desc->flags;
 
	return w;
 
}
 

	
 
/**
 
 * Open a new window.
 
 * @param *desc The pointer to the WindowDesc to be created
 
 * @return @see Window pointer of the newly created window
 
 */
 
Window *AllocateWindowDesc(const WindowDesc *desc)
 
{
 
	return LocalAllocateWindowDesc(desc, 0);
 
}
 

	
 
/**
 
 * Open a new window.
 
 * @param *desc The pointer to the WindowDesc to be created
 
 * @param window_number the window number of the new window
 
 * @return @see Window pointer of the newly created window
 
 */
 
Window *AllocateWindowDescFront(const WindowDesc *desc, int window_number)
 
{
 
	Window *w;
 

	
 
	if (BringWindowToFrontById(desc->cls, window_number)) return NULL;
 
	w = LocalAllocateWindowDesc(desc, window_number);
 
	return w;
 
}
 

	
 
/** Do a search for a window at specific coordinates. For this we start
 
 * at the topmost window, obviously and work our way down to the bottom
 
 * @return a pointer to the found window if any, NULL otherwise */
 
Window *FindWindowFromPt(int x, int y)
 
{
 
	Window* const *wz;
 

	
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		Window *w = *--wz;
 
		if (IS_INSIDE_1D(x, w->left, w->width) && IS_INSIDE_1D(y, w->top, w->height)) {
 
			return w;
 
		}
 
	}
 

	
 
	return NULL;
 
}
 

	
 
void InitWindowSystem(void)
 
{
 
	IConsoleClose();
 

	
 
	memset(&_windows, 0, sizeof(_windows));
 
	_last_z_window = _z_windows;
 
	InitViewports();
 
	_no_scroll = 0;
 
}
 

	
 
void UnInitWindowSystem(void)
 
{
 
	Window **wz;
 
	/* Delete all malloced widgets, and reset z-array */
 
	FOR_ALL_WINDOWS(wz) {
 
		free((*wz)->widget);
 
		(*wz)->widget = NULL;
 
		(*wz)->widget_count = 0;
 
		*wz = NULL;
 
	}
 
	_last_z_window = _z_windows;
 
}
 

	
 
void ResetWindowSystem(void)
 
{
 
	UnInitWindowSystem();
 
	InitWindowSystem();
 
	_thd.pos.x = 0;
 
	_thd.pos.y = 0;
 
	_thd.new_pos.x = 0;
 
	_thd.new_pos.y = 0;
 
}
 

	
 
static void DecreaseWindowCounters(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		w = *--wz;
 
		// Unclick scrollbar buttons if they are pressed.
 
		if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
 
			w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
 
			SetWindowDirty(w);
 
		}
 
		CallWindowEventNP(w, WE_MOUSELOOP);
 
	}
 

	
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		w = *--wz;
 

	
 
		if (w->flags4&WF_TIMEOUT_MASK && !(--w->flags4&WF_TIMEOUT_MASK)) {
 
			CallWindowEventNP(w, WE_TIMEOUT);
 
			if (w->desc_flags & WDF_UNCLICK_BUTTONS) RaiseWindowButtons(w);
 
		}
 
	}
 
}
 

	
 
Window *GetCallbackWnd(void)
 
{
 
	return FindWindowById(_thd.window_class, _thd.window_number);
 
}
 

	
 
static void HandlePlacePresize(void)
 
{
 
	Window *w;
 
	WindowEvent e;
 

	
 
	if (_special_mouse_mode != WSM_PRESIZE) return;
 

	
 
	w = GetCallbackWnd();
 
	if (w == NULL) return;
 

	
 
	e.we.place.pt = GetTileBelowCursor();
 
	if (e.we.place.pt.x == -1) {
 
		_thd.selend.x = -1;
 
		return;
 
	}
 
	e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y);
 
	e.event = WE_PLACE_PRESIZE;
 
	w->wndproc(w, &e);
 
}
 

	
 
static bool HandleDragDrop(void)
 
{
 
	Window *w;
 
	WindowEvent e;
 

	
 
	if (_special_mouse_mode != WSM_DRAGDROP) return true;
 

	
 
	if (_left_button_down) return false;
 

	
 
	w = GetCallbackWnd();
 

	
 
	ResetObjectToPlace();
 

	
 
	if (w != NULL) {
 
		// send an event in client coordinates.
 
		e.event = WE_DRAGDROP;
 
		e.we.dragdrop.pt.x = _cursor.pos.x - w->left;
 
		e.we.dragdrop.pt.y = _cursor.pos.y - w->top;
 
		e.we.dragdrop.widget = GetWidgetFromPos(w, e.we.dragdrop.pt.x, e.we.dragdrop.pt.y);
 
		w->wndproc(w, &e);
 
	}
 
	return false;
 
}
 

	
 
static bool HandlePopupMenu(void)
 
{
 
	Window *w;
 
	WindowEvent e;
 

	
 
	if (!_popup_menu_active) return true;
 

	
 
	w = FindWindowById(WC_TOOLBAR_MENU, 0);
 
	if (w == NULL) {
 
		_popup_menu_active = false;
 
		return false;
 
	}
 

	
 
	if (_left_button_down) {
 
		e.event = WE_POPUPMENU_OVER;
 
		e.we.popupmenu.pt = _cursor.pos;
 
	} else {
 
		_popup_menu_active = false;
 
		e.event = WE_POPUPMENU_SELECT;
 
		e.we.popupmenu.pt = _cursor.pos;
 
	}
 

	
 
	w->wndproc(w, &e);
 

	
 
	return false;
 
}
 

	
 
static bool HandleMouseOver(void)
 
{
 
	Window *w;
 
	WindowEvent e;
 
	static Window *last_w = NULL;
 

	
 
	w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
 

	
 
	// We changed window, put a MOUSEOVER event to the last window
 
	if (last_w != NULL && last_w != w) {
 
		e.event = WE_MOUSEOVER;
 
		e.we.mouseover.pt.x = -1;
 
		e.we.mouseover.pt.y = -1;
 
		if (last_w->wndproc) last_w->wndproc(last_w, &e);
 
	}
 
	last_w = w;
 

	
 
	if (w != NULL) {
 
		// send an event in client coordinates.
 
		e.event = WE_MOUSEOVER;
 
		e.we.mouseover.pt.x = _cursor.pos.x - w->left;
 
		e.we.mouseover.pt.y = _cursor.pos.y - w->top;
 
		if (w->widget != NULL) {
 
			e.we.mouseover.widget = GetWidgetFromPos(w, e.we.mouseover.pt.x, e.we.mouseover.pt.y);
 
		}
 
		w->wndproc(w, &e);
 
	}
 

	
 
	// Mouseover never stops execution
 
	return true;
 
}
 

	
 
/** Update all the widgets of a window based on their resize flags
 
 * Both the areas of the old window and the new sized window are set dirty
 
 * ensuring proper redrawal.
 
 * @param w Window to resize
 
 * @param x delta x-size of changed window (positive if larger, etc.(
 
 * @param y delta y-size of changed window */
 
void ResizeWindow(Window *w, int x, int y)
 
{
 
	Widget *wi;
 
	bool resize_height = false;
 
	bool resize_width = false;
 

	
 
	if (x == 0 && y == 0) return;
 

	
 
	SetWindowDirty(w);
 
	for (wi = w->widget; wi->type != WWT_LAST; wi++) {
 
		/* Isolate the resizing flags */
 
		byte rsizeflag = GB(wi->display_flags, 0, 4);
 

	
 
		if (rsizeflag == RESIZE_NONE) continue;
 

	
 
		/* Resize the widget based on its resize-flag */
 
		if (rsizeflag & RESIZE_LEFT) {
 
			wi->left += x;
 
			resize_width = true;
 
		}
 

	
 
		if (rsizeflag & RESIZE_RIGHT) {
 
			wi->right += x;
 
			resize_width = true;
 
		}
 

	
 
		if (rsizeflag & RESIZE_TOP) {
 
			wi->top += y;
 
			resize_height = true;
 
		}
 

	
 
		if (rsizeflag & RESIZE_BOTTOM) {
 
			wi->bottom += y;
 
			resize_height = true;
 
		}
 
	}
 

	
 
	/* We resized at least 1 widget, so let's resize the window totally */
 
	if (resize_width)  w->width  += x;
 
	if (resize_height) w->height += y;
 

	
 
	SetWindowDirty(w);
 
}
 

	
 
static bool _dragging_window;
 

	
 
static bool HandleWindowDragging(void)
 
{
 
	Window* const *wz;
 
	// Get out immediately if no window is being dragged at all.
 
	if (!_dragging_window) return true;
 

	
 
	// Otherwise find the window...
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 

	
 
		if (w->flags4 & WF_DRAGGING) {
 
			const Widget *t = &w->widget[1]; // the title bar ... ugh
 
			const Window *v;
 
			int x;
 
			int y;
 
			int nx;
 
			int ny;
 

	
 
			// Stop the dragging if the left mouse button was released
 
			if (!_left_button_down) {
 
				w->flags4 &= ~WF_DRAGGING;
 
				break;
 
			}
 

	
 
			SetWindowDirty(w);
 

	
 
			x = _cursor.pos.x + _drag_delta.x;
 
			y = _cursor.pos.y + _drag_delta.y;
 
			nx = x;
 
			ny = y;
 

	
 
			if (_patches.window_snap_radius != 0) {
 
				Window* const *vz;
 

	
 
				int hsnap = _patches.window_snap_radius;
 
				int vsnap = _patches.window_snap_radius;
 
				int delta;
 

	
 
				FOR_ALL_WINDOWS(vz) {
 
					const Window *v = *vz;
 

	
 
					if (v == w) continue; // Don't snap at yourself
 

	
 
					if (y + w->height > v->top && y < v->top + v->height) {
 
						// Your left border <-> other right border
 
						delta = abs(v->left + v->width - x);
 
						if (delta <= hsnap) {
 
							nx = v->left + v->width;
 
							hsnap = delta;
 
						}
 

	
 
						// Your right border <-> other left border
 
						delta = abs(v->left - x - w->width);
 
						if (delta <= hsnap) {
 
							nx = v->left - w->width;
 
							hsnap = delta;
 
						}
 
					}
 

	
 
					if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
 
						// Your left border <-> other left border
 
						delta = abs(v->left - x);
 
						if (delta <= hsnap) {
 
							nx = v->left;
 
							hsnap = delta;
 
						}
 

	
 
						// Your right border <-> other right border
 
						delta = abs(v->left + v->width - x - w->width);
 
						if (delta <= hsnap) {
 
							nx = v->left + v->width - w->width;
 
							hsnap = delta;
 
						}
 
					}
 

	
 
					if (x + w->width > v->left && x < v->left + v->width) {
 
						// Your top border <-> other bottom border
 
						delta = abs(v->top + v->height - y);
 
						if (delta <= vsnap) {
 
							ny = v->top + v->height;
 
							vsnap = delta;
 
						}
 

	
 
						// Your bottom border <-> other top border
 
						delta = abs(v->top - y - w->height);
 
						if (delta <= vsnap) {
 
							ny = v->top - w->height;
 
							vsnap = delta;
 
						}
 
					}
 

	
 
					if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
 
						// Your top border <-> other top border
 
						delta = abs(v->top - y);
 
						if (delta <= vsnap) {
 
							ny = v->top;
 
							vsnap = delta;
 
						}
 

	
 
						// Your bottom border <-> other bottom border
 
						delta = abs(v->top + v->height - y - w->height);
 
						if (delta <= vsnap) {
 
							ny = v->top + v->height - w->height;
 
							vsnap = delta;
 
						}
 
					}
 
				}
 
			}
 

	
 
			// Make sure the window doesn't leave the screen
 
			// 13 is the height of the title bar
 
			nx = clamp(nx, 13 - t->right, _screen.width - 13 - t->left);
 
			ny = clamp(ny, 0, _screen.height - 13);
 

	
 
			// Make sure the title bar isn't hidden by behind the main tool bar
 
			v = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
			if (v != NULL) {
 
				int v_bottom = v->top + v->height;
 
				int v_right = v->left + v->width;
 
				if (ny + t->top >= v->top && ny + t->top < v_bottom) {
 
					if ((v->left < 13 && nx + t->left < v->left) ||
 
							(v_right > _screen.width - 13 && nx + t->right > v_right)) {
 
						ny = v_bottom;
 
					} else {
 
						if (nx + t->left > v->left - 13 &&
 
								nx + t->right < v_right + 13) {
 
							if (w->top >= v_bottom) {
 
								ny = v_bottom;
 
							} else if (w->left < nx) {
 
								nx = v->left - 13 - t->left;
 
							} else {
 
								nx = v_right + 13 - t->right;
 
							}
 
						}
 
					}
 
				}
 
			}
 

	
 
			if (w->viewport != NULL) {
 
				w->viewport->left += nx - w->left;
 
				w->viewport->top  += ny - w->top;
 
			}
 
			w->left = nx;
 
			w->top  = ny;
 

	
 
			SetWindowDirty(w);
 
			return false;
 
		} else if (w->flags4 & WF_SIZING) {
 
			WindowEvent e;
 
			int x, y;
 

	
 
			/* Stop the sizing if the left mouse button was released */
 
			if (!_left_button_down) {
 
				w->flags4 &= ~WF_SIZING;
 
				SetWindowDirty(w);
 
				break;
 
			}
 

	
 
			x = _cursor.pos.x - _drag_delta.x;
 
			y = _cursor.pos.y - _drag_delta.y;
 

	
 
			/* X and Y has to go by step.. calculate it.
 
			 * The cast to int is necessary else x/y are implicitly casted to
 
			 * unsigned int, which won't work. */
 
			if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
 

	
 
			if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
 

	
 
			/* Check if we don't go below the minimum set size */
 
			if ((int)w->width + x < (int)w->resize.width)
 
				x = w->resize.width - w->width;
 
			if ((int)w->height + y < (int)w->resize.height)
 
				y = w->resize.height - w->height;
 

	
 
			/* Window already on size */
 
			if (x == 0 && y == 0) return false;
 

	
 
			/* Now find the new cursor pos.. this is NOT _cursor, because
 
			    we move in steps. */
 
			_drag_delta.x += x;
 
			_drag_delta.y += y;
 

	
 
			/* ResizeWindow sets both pre- and after-size to dirty for redrawal */
 
			ResizeWindow(w, x, y);
 

	
 
			e.event = WE_RESIZE;
 
			e.we.sizing.size.x = x + w->width;
 
			e.we.sizing.size.y = y + w->height;
 
			e.we.sizing.diff.x = x;
 
			e.we.sizing.diff.y = y;
 
			w->wndproc(w, &e);
 
			return false;
 
		}
 
	}
 

	
 
	_dragging_window = false;
 
	return false;
 
}
 

	
 
static void StartWindowDrag(Window *w)
 
{
 
	w->flags4 |= WF_DRAGGING;
 
	_dragging_window = true;
 

	
 
	_drag_delta.x = w->left - _cursor.pos.x;
 
	_drag_delta.y = w->top  - _cursor.pos.y;
 

	
 
	BringWindowToFront(w);
 
	DeleteWindowById(WC_DROPDOWN_MENU, 0);
 
}
 

	
 
static void StartWindowSizing(Window *w)
 
{
 
	w->flags4 |= WF_SIZING;
 
	_dragging_window = true;
 

	
 
	_drag_delta.x = _cursor.pos.x;
 
	_drag_delta.y = _cursor.pos.y;
 

	
 
	BringWindowToFront(w);
 
	DeleteWindowById(WC_DROPDOWN_MENU, 0);
 
}
 

	
 

	
 
static bool HandleScrollbarScrolling(void)
 
{
 
	Window* const *wz;
 
	int i;
 
	int pos;
 
	Scrollbar *sb;
 

	
 
	// Get out quickly if no item is being scrolled
 
	if (!_scrolling_scrollbar) return true;
 

	
 
	// Find the scrolling window
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 

	
 
		if (w->flags4 & WF_SCROLL_MIDDLE) {
 
			// Abort if no button is clicked any more.
 
			if (!_left_button_down) {
 
				w->flags4 &= ~WF_SCROLL_MIDDLE;
 
				SetWindowDirty(w);
 
				break;
 
			}
 

	
 
			if (w->flags4 & WF_HSCROLL) {
 
				sb = &w->hscroll;
 
				i = _cursor.pos.x - _cursorpos_drag_start.x;
 
			} else if (w->flags4 & WF_SCROLL2){
 
				sb = &w->vscroll2;
 
				i = _cursor.pos.y - _cursorpos_drag_start.y;
 
			} else {
 
				sb = &w->vscroll;
 
				i = _cursor.pos.y - _cursorpos_drag_start.y;
 
			}
 

	
 
			// Find the item we want to move to and make sure it's inside bounds.
 
			pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap));
 
			if (pos != sb->pos) {
 
				sb->pos = pos;
 
				SetWindowDirty(w);
 
			}
 
			return false;
 
		}
 
	}
 

	
 
	_scrolling_scrollbar = false;
 
	return false;
 
}
 

	
 
static bool HandleViewportScroll(void)
 
{
 
	WindowEvent e;
 
	Window *w;
 

	
 
	if (!_scrolling_viewport) return true;
 

	
 
	w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
 

	
 
	if (!_right_button_down || w == NULL) {
 
		_cursor.fix_at = false;
 
		_scrolling_viewport = false;
 
		return true;
 
	}
 

	
 
	if (_patches.reverse_scroll) {
 
		e.we.scroll.delta.x = -_cursor.delta.x;
 
		e.we.scroll.delta.y = -_cursor.delta.y;
 
	} else {
 
		e.we.scroll.delta.x = _cursor.delta.x;
 
		e.we.scroll.delta.y = _cursor.delta.y;
 
	}
 

	
 
	/* Create a scroll-event and send it to the window */
 
	e.event = WE_SCROLL;
 
	w->wndproc(w, &e);
 

	
 
	_cursor.delta.x = 0;
 
	_cursor.delta.y = 0;
 
	return false;
 
}
 

	
 
/** Check if a window can be made top-most window, and if so do
 
 * it. If a window does not obscure any other windows, it will not
 
 * be brought to the foreground. Also if the only obscuring windows
 
 * are so-called system-windows, the window will not be moved.
 
 * The function will return false when a child window of this window is a
 
 * modal-popup; function returns a false and child window gets a white border
 
 * @param w Window to bring on-top
 
 * @return false if the window has an active modal child, true otherwise */
 
static bool MaybeBringWindowToFront(const Window *w)
 
{
 
	bool bring_to_front = false;
 
	Window* const *wz;
 
	Window* const *uz;
 

	
 
	if (w->window_class == WC_MAIN_WINDOW ||
 
			IsVitalWindow(w) ||
 
			w->window_class == WC_TOOLTIPS ||
 
			w->window_class == WC_DROPDOWN_MENU) {
 
		return true;
 
	}
 

	
 
	wz = FindWindowZPosition(w);
 
	for (uz = wz; ++uz != _last_z_window;) {
 
		Window *u = *uz;
 

	
 
		/* A modal child will prevent the activation of the parent window */
 
		if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
 
			u->flags4 |= WF_WHITE_BORDER_MASK;
 
			SetWindowDirty(u);
 
			return false;
 
		}
 

	
 
		if (u->window_class == WC_MAIN_WINDOW ||
 
				IsVitalWindow(u) ||
 
				u->window_class == WC_TOOLTIPS ||
 
				u->window_class == WC_DROPDOWN_MENU) {
 
			continue;
 
		}
 

	
 
		/* Window sizes don't interfere, leave z-order alone */
 
		if (w->left + w->width <= u->left ||
 
				u->left + u->width <= w->left ||
 
				w->top  + w->height <= u->top ||
 
				u->top + u->height <= w->top) {
 
			continue;
 
		}
 

	
 
		bring_to_front = true;
 
	}
 

	
 
	if (bring_to_front) BringWindowToFront(w);
 
	return true;
 
}
 

	
 
/** Send a message from one window to another. The receiving window is found by
 
 * @param w @see Window pointer pointing to the other window
 
 * @param msg Specifies the message to be sent
 
 * @param wparam Specifies additional message-specific information
 
 * @param lparam Specifies additional message-specific information
 
 */
 
static void SendWindowMessageW(Window *w, uint msg, uint wparam, uint lparam)
 
{
 
	WindowEvent e;
 

	
 
	e.event             = WE_MESSAGE;
 
	e.we.message.msg    = msg;
 
	e.we.message.wparam = wparam;
 
	e.we.message.lparam = lparam;
 

	
 
	w->wndproc(w, &e);
 
}
 

	
 
/** Send a message from one window to another. The receiving window is found by
 
 * @param wnd_class @see WindowClass class AND
 
 * @param wnd_num @see WindowNumber number, mostly 0
 
 * @param msg Specifies the message to be sent
 
 * @param wparam Specifies additional message-specific information
 
 * @param lparam Specifies additional message-specific information
 
 */
 
void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, uint msg, uint wparam, uint lparam)
 
{
 
	Window *w = FindWindowById(wnd_class, wnd_num);
 
	if (w != NULL) SendWindowMessageW(w, msg, wparam, lparam);
 
}
 

	
 
/** Send a message from one window to another. The message will be sent
 
 * to ALL windows of the windowclass specified in the first parameter
 
 * @param wnd_class @see WindowClass class
 
 * @param msg Specifies the message to be sent
 
 * @param wparam Specifies additional message-specific information
 
 * @param lparam Specifies additional message-specific information
 
 */
 
void SendWindowMessageClass(WindowClass wnd_class, uint msg, uint wparam, uint lparam)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		if ((*wz)->window_class == wnd_class) SendWindowMessageW(*wz, msg, wparam, lparam);
 
	}
 
}
 

	
 
/** Handle keyboard input.
 
 * @param key Lower 8 bits contain the ASCII character, the higher
 
 * 16 bits the keycode */
 
void HandleKeypress(uint32 key)
 
{
 
	Window* const *wz;
 
	WindowEvent e;
 
	/* Stores if a window with a textfield for typing is open
 
	 * If this is the case, keypress events are only passed to windows with text fields and
 
	 * to thein this main toolbar. */
 
	bool query_open = false;
 

	
 
	/*
 
	* During the generation of the world, there might be
 
	* another thread that is currently building for example
 
	* a road. To not interfere with those tasks, we should
 
	* NOT change the _current_player here.
 
	*
 
	* This is not necessary either, as the only events that
 
	* can be handled are the 'close application' events
 
	*/
 
	if (!IsGeneratingWorld()) _current_player = _local_player;
 

	
 
	// Setup event
 
	e.event = WE_KEYPRESS;
 
	e.we.keypress.key     = GB(key,  0, 16);
 
	e.we.keypress.keycode = GB(key, 16, 16);
 
	e.we.keypress.cont = true;
 

	
 
	// check if we have a query string window open before allowing hotkeys
 
	if (FindWindowById(WC_QUERY_STRING,       0) != NULL ||
 
			FindWindowById(WC_SEND_NETWORK_MSG,   0) != NULL ||
 
			FindWindowById(WC_GENERATE_LANDSCAPE, 0) != NULL ||
 
			FindWindowById(WC_CONSOLE,            0) != NULL ||
 
			FindWindowById(WC_SAVELOAD,           0) != NULL) {
 
		query_open = true;
 
	}
 

	
 
	// Call the event, start with the uppermost window.
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		Window *w = *--wz;
 

	
 
		// if a query window is open, only call the event for certain window types
 
		if (query_open &&
 
				w->window_class != WC_QUERY_STRING &&
 
				w->window_class != WC_SEND_NETWORK_MSG &&
 
				w->window_class != WC_GENERATE_LANDSCAPE &&
 
				w->window_class != WC_CONSOLE &&
 
				w->window_class != WC_SAVELOAD) {
 
			continue;
 
		}
 
		w->wndproc(w, &e);
 
		if (!e.we.keypress.cont) break;
 
	}
 

	
 
	if (e.we.keypress.cont) {
 
		Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
		// When there is no toolbar w is null, check for that
 
		if (w != NULL) w->wndproc(w, &e);
 
	}
 
}
 

	
 
extern void UpdateTileSelection(void);
 
extern bool VpHandlePlaceSizingDrag(void);
 

	
 
static int _input_events_this_tick = 0;
 

	
 
static void HandleAutoscroll(void)
 
{
 
	Window *w;
 
	ViewPort *vp;
 
	int x = _cursor.pos.x;
 
	int y = _cursor.pos.y;
 

	
 
	if (_input_events_this_tick != 0) {
 
		/* HandleAutoscroll is called only once per GameLoop() - so we can clear the counter here */
 
		_input_events_this_tick = 0;
 
		/* there were some inputs this tick, don't scroll ??? */
 
		return;
 
	}
 

	
 
	if (_patches.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
 
		w = FindWindowFromPt(x, y);
 
		if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
 
		vp = IsPtInWindowViewport(w, x, y);
 
		if (vp != NULL) {
 
			x -= vp->left;
 
			y -= vp->top;
 
			//here allows scrolling in both x and y axis
 
#define scrollspeed 3
 
			if (x - 15 < 0) {
 
				WP(w, vp_d).scrollpos_x += (x - 15) * scrollspeed << vp->zoom;
 
			} else if (15 - (vp->width - x) > 0) {
 
				WP(w, vp_d).scrollpos_x += (15 - (vp->width - x)) * scrollspeed << vp->zoom;
 
			}
 
			if (y - 15 < 0) {
 
				WP(w, vp_d).scrollpos_y += (y - 15) * scrollspeed << vp->zoom;
 
			} else if (15 - (vp->height - y) > 0) {
 
				WP(w,vp_d).scrollpos_y += (15 - (vp->height - y)) * scrollspeed << vp->zoom;
 
			}
 
#undef scrollspeed
 
		}
 
	}
 
}
 

	
 
void MouseLoop(int click, int mousewheel)
 
{
 
	int x,y;
 
	Window *w;
 
	ViewPort *vp;
 

	
 
	DecreaseWindowCounters();
 
	HandlePlacePresize();
 
	UpdateTileSelection();
 
	if (!VpHandlePlaceSizingDrag())  return;
 
	if (!HandleDragDrop())           return;
 
	if (!HandlePopupMenu())          return;
 
	if (!HandleWindowDragging())     return;
 
	if (!HandleScrollbarScrolling()) return;
 
	if (!HandleViewportScroll())     return;
 
	if (!HandleMouseOver())          return;
 

	
 
	x = _cursor.pos.x;
 
	y = _cursor.pos.y;
 

	
 
	if (click == 0 && mousewheel == 0) return;
 

	
 
	w = FindWindowFromPt(x, y);
 
	if (w == NULL) return;
 
	if (!MaybeBringWindowToFront(w)) return;
 
	vp = IsPtInWindowViewport(w, x, y);
 

	
 
	/* Don't allow any action in a viewport if either in menu of in generating world */
 
	if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
 

	
 
	if (mousewheel != 0) {
 
		WindowEvent e;
 

	
 
		/* Send WE_MOUSEWHEEL event to window */
 
		e.event = WE_MOUSEWHEEL;
 
		e.we.wheel.wheel = mousewheel;
 
		w->wndproc(w, &e);
 

	
 
		/* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
 
		if (vp == NULL) DispatchMouseWheelEvent(w, GetWidgetFromPos(w, x - w->left, y - w->top), mousewheel);
 
	}
 

	
 
	if (vp != NULL) {
 
		switch (click) {
 
			case 1:
 
				DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
 
				if (_thd.place_mode != 0 &&
 
						// query button and place sign button work in pause mode
 
						_cursor.sprite != SPR_CURSOR_QUERY &&
 
						_cursor.sprite != SPR_CURSOR_SIGN &&
 
						_pause != 0 &&
 
						!_cheats.build_in_pause.value) {
 
					return;
 
				}
 

	
 
				if (_thd.place_mode == 0) {
 
					HandleViewportClicked(vp, x, y);
 
				} else {
 
					PlaceObject();
 
				}
 
				break;
 

	
 
			case 2:
 
				if (!(w->flags4 & WF_DISABLE_VP_SCROLL)) {
 
					_scrolling_viewport = true;
 
					_cursor.fix_at = true;
 
				}
 
				break;
 
		}
 
	} else {
 
		switch (click) {
 
			case 1: DispatchLeftClickEvent(w, x - w->left, y - w->top);  break;
 
			case 2: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
 
		}
 
	}
 
}
 

	
 
void HandleMouseEvents(void)
 
{
 
	int click;
 
	int mousewheel;
 

	
 
	/*
 
	 * During the generation of the world, there might be
 
	 * another thread that is currently building for example
 
	 * a road. To not interfere with those tasks, we should
 
	 * NOT change the _current_player here.
 
	 *
 
	 * This is not necessary either, as the only events that
 
	 * can be handled are the 'close application' events
 
	 */
 
	if (!IsGeneratingWorld()) _current_player = _local_player;
 

	
 
	// Mouse event?
 
	click = 0;
 
	if (_left_button_down && !_left_button_clicked) {
 
		_left_button_clicked = true;
 
		click = 1;
 
		_input_events_this_tick++;
 
	} else if (_right_button_clicked) {
 
		_right_button_clicked = false;
 
		click = 2;
 
		_input_events_this_tick++;
 
	}
 

	
 
	mousewheel = 0;
 
	if (_cursor.wheel) {
 
		mousewheel = _cursor.wheel;
 
		_cursor.wheel = 0;
 
		_input_events_this_tick++;
 
	}
 

	
 
	MouseLoop(click, mousewheel);
 
}
 

	
 
void InputLoop(void)
 
{
 
	HandleMouseEvents();
 
	HandleAutoscroll();
 
}
 

	
 
void UpdateWindows(void)
 
{
 
	Window* const *wz;
 
	static int we4_timer = 0;
 
	int t = we4_timer + 1;
 

	
 
	if (t >= 100) {
 
		for (wz = _last_z_window; wz != _z_windows;) {
 
			CallWindowEventNP(*--wz, WE_4);
 
		}
 
		t = 0;
 
	}
 
	we4_timer = t;
 

	
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		Window *w = *--wz;
 
		if (w->flags4 & WF_WHITE_BORDER_MASK) {
 
			w->flags4 -= WF_WHITE_BORDER_ONE;
 

	
 
			if (!(w->flags4 & WF_WHITE_BORDER_MASK)) SetWindowDirty(w);
 
		}
 
	}
 

	
 
	DrawDirtyBlocks();
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		if ((*wz)->viewport != NULL) UpdateViewportPosition(*wz);
 
	}
 
	DrawTextMessage();
 
	// Redraw mouse cursor in case it was hidden
 
	DrawMouseCursor();
 
}
 

	
 

	
 
int GetMenuItemIndex(const Window *w, int x, int y)
 
{
 
	if ((x -= w->left) >= 0 && x < w->width && (y -= w->top + 1) >= 0) {
 
		y /= 10;
 

	
 
		if (y < WP(w, const menu_d).item_count &&
 
				!HASBIT(WP(w, const menu_d).disabled_items, y)) {
 
			return y;
 
		}
 
	}
 
	return -1;
 
}
 

	
 
void InvalidateWindow(WindowClass cls, WindowNumber number)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		if (w->window_class == cls && w->window_number == number) SetWindowDirty(w);
 
	}
 
}
 

	
 
void InvalidateWidget(const Window *w, byte widget_index)
 
{
 
	const Widget *wi = &w->widget[widget_index];
 

	
 
	/* Don't redraw the window if the widget is invisible or of no-type */
 
	if (wi->type == WWT_EMPTY || IsWindowWidgetHidden(w, widget_index)) return;
 

	
 
	SetDirtyBlocks(w->left + wi->left, w->top + wi->top, w->left + wi->right + 1, w->top + wi->bottom + 1);
 
}
 

	
 
void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		if (w->window_class == cls && w->window_number == number) {
 
			InvalidateWidget(w, widget_index);
 
		}
 
	}
 
}
 

	
 
void InvalidateWindowClasses(WindowClass cls)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		if ((*wz)->window_class == cls) SetWindowDirty(*wz);
 
	}
 
}
 

	
 
void InvalidateThisWindowData(Window *w)
 
{
 
	CallWindowEventNP(w, WE_INVALIDATE_DATA);
 
	SetWindowDirty(w);
 
}
 

	
 
void InvalidateWindowData(WindowClass cls, WindowNumber number)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w);
 
	}
 
}
 

	
 
void InvalidateWindowClassesData(WindowClass cls)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz);
 
	}
 
}
 

	
 
void CallWindowTickEvent(void)
 
{
 
	Window* const *wz;
 

	
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		CallWindowEventNP(*--wz, WE_TICK);
 
	}
 
}
 

	
 
void DeleteNonVitalWindows(void)
 
{
 
	Window* const *wz;
 

	
 
restart_search:
 
	/* When we find the window to delete, we need to restart the search
 
	 * as deleting this window could cascade in deleting (many) others
 
	 * anywhere in the z-array */
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class != WC_MAIN_WINDOW &&
 
				w->window_class != WC_SELECT_GAME &&
 
				w->window_class != WC_MAIN_TOOLBAR &&
 
				w->window_class != WC_STATUS_BAR &&
 
				w->window_class != WC_TOOLBAR_MENU &&
 
				w->window_class != WC_TOOLTIPS &&
 
				(w->flags4 & WF_STICKY) == 0) { // do not delete windows which are 'pinned'
 

	
 
			DeleteWindow(w);
 
			goto restart_search;
 
		}
 
	}
 
}
 

	
 
/* It is possible that a stickied window gets to a position where the
 
 * 'close' button is outside the gaming area. You cannot close it then; except
 
 * with this function. It closes all windows calling the standard function,
 
 * then, does a little hacked loop of closing all stickied windows. Note
 
 * that standard windows (status bar, etc.) are not stickied, so these aren't affected */
 
void DeleteAllNonVitalWindows(void)
 
{
 
	Window* const *wz;
 

	
 
	/* Delete every window except for stickied ones, then sticky ones as well */
 
	DeleteNonVitalWindows();
 

	
 
restart_search:
 
	/* When we find the window to delete, we need to restart the search
 
	 * as deleting this window could cascade in deleting (many) others
 
	 * anywhere in the z-array */
 
	FOR_ALL_WINDOWS(wz) {
 
		if ((*wz)->flags4 & WF_STICKY) {
 
			DeleteWindow(*wz);
 
			goto restart_search;
 
		}
 
	}
 
}
 

	
 
/* Delete all always on-top windows to get an empty screen */
 
void HideVitalWindows(void)
 
{
 
	DeleteWindowById(WC_MAIN_TOOLBAR, 0);
 
	DeleteWindowById(WC_STATUS_BAR, 0);
 
}
 

	
 
int PositionMainToolbar(Window *w)
 
{
 
	DEBUG(misc, 5, "Repositioning Main Toolbar...");
 

	
 
	if (w == NULL || w->window_class != WC_MAIN_TOOLBAR) {
 
		w = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
	}
 

	
 
	switch (_patches.toolbar_pos) {
 
		case 1:  w->left = (_screen.width - w->width) / 2; break;
 
		case 2:  w->left = _screen.width - w->width; break;
 
		default: w->left = 0;
 
	}
 
	SetDirtyBlocks(0, 0, _screen.width, w->height); // invalidate the whole top part
 
	return w->left;
 
}
 

	
 
void RelocateAllWindows(int neww, int newh)
 
{
 
	Window* const *wz;
 

	
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		int left, top;
 

	
 
		if (w->window_class == WC_MAIN_WINDOW) {
 
			ViewPort *vp = w->viewport;
 
			vp->width = w->width = neww;
 
			vp->height = w->height = newh;
 
			vp->virtual_width = neww << vp->zoom;
 
			vp->virtual_height = newh << vp->zoom;
 
			continue; // don't modify top,left
 
		}
 

	
 
		/* XXX - this probably needs something more sane. For example specying
 
		 * in a 'backup'-desc that the window should always be centred. */
 
		switch (w->window_class) {
 
			case WC_MAIN_TOOLBAR:
 
				top = w->top;
 
				left = PositionMainToolbar(w); // changes toolbar orientation
 
				break;
 

	
 
			case WC_SELECT_GAME:
 
			case WC_GAME_OPTIONS:
 
			case WC_NETWORK_WINDOW:
 
				top = (newh - w->height) >> 1;
 
				left = (neww - w->width) >> 1;
 
				break;
 

	
 
			case WC_NEWS_WINDOW:
 
				top = newh - w->height;
 
				left = (neww - w->width) >> 1;
 
				break;
 

	
 
			case WC_STATUS_BAR:
 
				top = newh - w->height;
 
				left = (neww - w->width) >> 1;
 
				break;
 

	
 
			case WC_SEND_NETWORK_MSG:
 
				top = (newh - 26); // 26 = height of status bar + height of chat bar
 
				left = (neww - w->width) >> 1;
 
				break;
 

	
 
			case WC_CONSOLE:
 
				IConsoleResize(w);
 
				continue;
 

	
 
			default:
 
				left = w->left;
 
				if (left + (w->width >> 1) >= neww) left = neww - w->width;
 
				top = w->top;
 
				if (top + (w->height >> 1) >= newh) top = newh - w->height;
 
				break;
 
		}
 

	
 
		if (w->viewport != NULL) {
 
			w->viewport->left += left - w->left;
 
			w->viewport->top += top - w->top;
 
		}
 

	
 
		w->left = left;
 
		w->top = top;
 
	}
 
}
0 comments (0 inline, 0 general)