Changeset - r2293:b55e49a164c0
[Not reviewed]
master
0 14 0
bjarni - 19 years ago 2005-08-06 16:07:22
bjarni@openttd.org
(svn r2817) -Codechange: [autoreplace]: moved autoreplace and autorenew to serverside
-This means that one company can only have one setting for renew and replacing
more clients will not fight due to different settings anymore
-This is a needed step in the line to fix autoreplacing dualheaded locomotives
NOTE: savegame revision bump (peter1138 + me in coop)
14 files changed with 234 insertions and 75 deletions:
0 comments (0 inline, 0 general)
aircraft_cmd.c
Show inline comments
 
@@ -1430,262 +1430,264 @@ static void AircraftEventHandler_InHanga
 
			v->current_order.type != OT_GOTO_DEPOT)
 
		return;
 

	
 
	// if the block of the next position is busy, stay put
 
	if (AirportHasBlock(v, &Airport->layout[v->u.air.pos], Airport)) {return;}
 

	
 
	// We are already at the target airport, we need to find a terminal
 
	if (v->current_order.station == v->u.air.targetairport) {
 
		// FindFreeTerminal:
 
		// 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal
 
		if (v->subtype != 0) {if(!AirportFindFreeTerminal(v, Airport)) {return;}} // airplane
 
		else {if(!AirportFindFreeHelipad(v, Airport)) {return;}} // helicopter
 
	}
 
	else { // Else prepare for launch.
 
		// airplane goto state takeoff, helicopter to helitakeoff
 
		v->u.air.state = (v->subtype != 0) ? TAKEOFF : HELITAKEOFF;
 
	}
 
	AircraftLeaveHangar(v);
 
	AirportMove(v, Airport);
 
}
 

	
 
// At one of the Airport's Terminals
 
static void AircraftEventHandler_AtTerminal(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	// if we just arrived, execute EnterTerminal first
 
	if (v->u.air.previous_pos != v->u.air.pos) {
 
		AircraftEventHandler_EnterTerminal(v, Airport);
 
		// on an airport with helipads, a helicopter will always land there
 
		// and get serviced at the same time - patch setting
 
		if (_patches.serviceathelipad) {
 
			if (v->subtype == 0 && Airport->helipads != NULL) {
 
				// an exerpt of ServiceAircraft, without the invisibility stuff
 
				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);
 
			}
 
		}
 
		return;
 
	}
 

	
 
	if (v->current_order.type == OT_NOTHING) return;
 

	
 
	// if the block of the next position is busy, stay put
 
	if (AirportHasBlock(v, &Airport->layout[v->u.air.pos], Airport)) {
 
		return;
 
	}
 

	
 
	// airport-road is free. We either have to go to another airport, or to the hangar
 
	// ---> start moving
 

	
 
	switch (v->current_order.type) {
 
		case OT_GOTO_STATION: // ready to fly to another airport
 
			// airplane goto state takeoff, helicopter to helitakeoff
 
			v->u.air.state = (v->subtype != 0) ? TAKEOFF : HELITAKEOFF;
 
			break;
 
		case OT_GOTO_DEPOT:   // visit hangar for serivicing, sale, etc.
 
			if (v->current_order.station == v->u.air.targetairport)
 
				v->u.air.state = HANGAR;
 
			else
 
				v->u.air.state = (v->subtype != 0) ? TAKEOFF : HELITAKEOFF;
 
			break;
 
		default:  // orders have been deleted (no orders), goto depot and don't bother us
 
			v->current_order.type = OT_NOTHING;
 
			v->current_order.flags = 0;
 
			v->u.air.state = HANGAR;
 
	}
 
	AirportMove(v, Airport);
 
}
 

	
 
static void AircraftEventHandler_General(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	DEBUG(misc, 0) ("OK, you shouldn't be here, check your Airport Scheme!");
 
	assert(0);
 
}
 

	
 
static void AircraftEventHandler_TakeOff(Vehicle *v, const AirportFTAClass *Airport) {
 
	PlayAircraftSound(v); // play takeoffsound for airplanes
 
	v->u.air.state = STARTTAKEOFF;
 
}
 

	
 
static void AircraftEventHandler_StartTakeOff(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	v->sprite_width = v->sprite_height = 24; // ??? no idea what this is
 
	v->u.air.state = ENDTAKEOFF;
 
}
 

	
 
static void AircraftEventHandler_EndTakeOff(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	v->u.air.state = FLYING;
 
	// get the next position to go to, differs per airport
 
	AircraftNextAirportPos_and_Order(v);
 
}
 

	
 
static void AircraftEventHandler_HeliTakeOff(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	Player *p = GetPlayer(v->owner);
 
	v->sprite_width = v->sprite_height = 24; // ??? no idea what this is
 
	v->u.air.state = FLYING;
 
	// get the next position to go to, differs per airport
 
	AircraftNextAirportPos_and_Order(v);
 

	
 
	// check if the aircraft needs to be replaced or renewed and send it to a hangar if needed
 
	if ((v->owner == _local_player && _autoreplace_array[v->engine_type] != v->engine_type) ||
 
		(v->owner == _local_player && _patches.autorenew && v->age - v->max_age > (_patches.autorenew_months * 30))) {
 
	if ((v->owner == _local_player && p->engine_replacement[v->engine_type] != v->engine_type) ||
 
		(v->owner == _local_player && p->engine_renew && v->age - v->max_age > (p->engine_renew_months * 30))) {
 
		_current_player = _local_player;
 
		DoCommandP(v->tile, v->index, 1, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_SHOW_NO_ERROR);
 
		_current_player = OWNER_NONE;
 
	}
 
}
 

	
 
static void AircraftEventHandler_Flying(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	Station *st;
 
	byte landingtype;
 
	AirportFTA *current;
 
	uint16 tcur_speed, tsubspeed;
 

	
 
	st = GetStation(v->u.air.targetairport);
 
	// flying device is accepted at this station
 
	// small airport --> no helicopters (AIRCRAFT_ONLY)
 
	// all other airports --> all types of flying devices (ALL)
 
	// heliport/oilrig, etc --> no airplanes (HELICOPTERS_ONLY)
 
	// runway busy or not allowed to use this airstation, circle
 
	if (! (v->subtype == Airport->acc_planes ||
 
			st->airport_tile == 0 || (st->owner != OWNER_NONE && st->owner != v->owner) )) {
 

	
 
		// {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
 
		// if it is an airplane, look for LANDING, for helicopter HELILANDING
 
		// it is possible to choose from multiple landing runways, so loop until a free one is found
 
		landingtype = (v->subtype != 0) ? LANDING : HELILANDING;
 
		current = Airport->layout[v->u.air.pos].next_in_chain;
 
		while (current != NULL) {
 
			if (current->heading == landingtype) {
 
				// save speed before, since if AirportHasBlock is false, it resets them to 0
 
				// we don't want that for plane in air
 
				// hack for speed thingie
 
				tcur_speed = v->cur_speed;
 
				tsubspeed = v->subspeed;
 
				if (!AirportHasBlock(v, current, Airport)) {
 
					v->u.air.state = landingtype; // LANDING / HELILANDING
 
					// it's a bit dirty, but I need to set position to next position, otherwise
 
					// if there are multiple runways, plane won't know which one it took (because
 
					// they all have heading LANDING). And also occupy that block!
 
					v->u.air.pos = current->next_position;
 
					SETBITS(st->airport_flags, Airport->layout[v->u.air.pos].block);
 
					return;
 
				}
 
				v->cur_speed = tcur_speed;
 
				v->subspeed = tsubspeed;
 
			}
 
			current = current->next_in_chain;
 
		}
 
	}
 
	v->u.air.state = FLYING;
 
	v->u.air.pos = Airport->layout[v->u.air.pos].next_position;
 
}
 

	
 
static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	Player *p = GetPlayer(v->owner);
 
	AircraftLandAirplane(v);  // maybe crash airplane
 
	v->u.air.state = ENDLANDING;
 
	// check if the aircraft needs to be replaced or renewed and send it to a hangar if needed
 
	if (v->current_order.type != OT_GOTO_DEPOT && v->owner == _local_player) {
 
		// only the vehicle owner needs to calculate the rest (locally)
 
		if ((_autoreplace_array[v->engine_type] != v->engine_type) ||
 
			(_patches.autorenew && v->age - v->max_age > (_patches.autorenew_months * 30))) {
 
		if ((p->engine_replacement[v->engine_type] != v->engine_type) ||
 
			(p->engine_renew && v->age - v->max_age > (p->engine_renew_months * 30))) {
 
			// send the aircraft to the hangar at next airport (bit 17 set)
 
			_current_player = _local_player;
 
			DoCommandP(v->tile, v->index, 1 << 16, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_SHOW_NO_ERROR);
 
			_current_player = OWNER_NONE;
 
		}
 
	}
 
}
 

	
 
static void AircraftEventHandler_HeliLanding(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	AircraftLand(v); // helicopters don't crash
 
	v->u.air.state = HELIENDLANDING;
 
}
 

	
 
static void AircraftEventHandler_EndLanding(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	// next block busy, don't do a thing, just wait
 
	if(AirportHasBlock(v, &Airport->layout[v->u.air.pos], Airport)) {return;}
 

	
 
	// if going to terminal (OT_GOTO_STATION) choose one
 
	// 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
 
	// 2. not going for terminal (but depot, no order),
 
	// --> get out of the way to the hangar.
 
	if (v->current_order.type == OT_GOTO_STATION) {
 
		if (AirportFindFreeTerminal(v, Airport)) {return;}
 
	}
 
	v->u.air.state = HANGAR;
 

	
 
}
 

	
 
static void AircraftEventHandler_HeliEndLanding(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	// next block busy, don't do a thing, just wait
 
	if(AirportHasBlock(v, &Airport->layout[v->u.air.pos], Airport)) {return;}
 

	
 
	// if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
 
	// 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
 
	// 2. not going for terminal (but depot, no order),
 
	// --> get out of the way to the hangar IF there are terminals on the airport.
 
	// --> else TAKEOFF
 
	// the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
 
	// must go to a hangar.
 
	if (v->current_order.type == OT_GOTO_STATION) {
 
		if (AirportFindFreeHelipad(v, Airport)) {return;}
 
	}
 
	v->u.air.state = (Airport->terminals != NULL) ? HANGAR : HELITAKEOFF;
 
}
 

	
 
typedef void AircraftStateHandler(Vehicle *v, const AirportFTAClass *Airport);
 
static AircraftStateHandler * const _aircraft_state_handlers[] = {
 
	AircraftEventHandler_General,				// TO_ALL         =  0
 
	AircraftEventHandler_InHangar,			// HANGAR         =  1
 
	AircraftEventHandler_AtTerminal,		// TERM1          =  2
 
	AircraftEventHandler_AtTerminal,		// TERM2          =  3
 
	AircraftEventHandler_AtTerminal,		// TERM3          =  4
 
	AircraftEventHandler_AtTerminal,		// TERM4          =  5
 
	AircraftEventHandler_AtTerminal,		// TERM5          =  6
 
	AircraftEventHandler_AtTerminal,		// TERM6          =  7
 
	AircraftEventHandler_AtTerminal,		// HELIPAD1       =  8
 
	AircraftEventHandler_AtTerminal,		// HELIPAD2       =  9
 
	AircraftEventHandler_TakeOff,				// TAKEOFF        = 10
 
	AircraftEventHandler_StartTakeOff,	// STARTTAKEOFF   = 11
 
	AircraftEventHandler_EndTakeOff,		// ENDTAKEOFF     = 12
 
	AircraftEventHandler_HeliTakeOff,		// HELITAKEOFF    = 13
 
	AircraftEventHandler_Flying,				// FLYING         = 14
 
	AircraftEventHandler_Landing,				// LANDING        = 15
 
	AircraftEventHandler_EndLanding,		// ENDLANDING     = 16
 
	AircraftEventHandler_HeliLanding,		// HELILANDING    = 17
 
	AircraftEventHandler_HeliEndLanding,// HELIENDLANDING = 18
 
};
 

	
 
static void AirportClearBlock(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	Station *st;
 
	// we have left the previous block, and entered the new one. Free the previous block
 
	if (Airport->layout[v->u.air.previous_pos].block != Airport->layout[v->u.air.pos].block) {
 
		st = GetStation(v->u.air.targetairport);
 
		CLRBITS(st->airport_flags, Airport->layout[v->u.air.previous_pos].block);
 
	}
 
}
 

	
 
static void AirportGoToNextPosition(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	// if aircraft is not in position, wait until it is
 
	if (!AircraftController(v)) {return;}
 

	
 
	AirportClearBlock(v, Airport);
 
	AirportMove(v, Airport); // move aircraft to next position
 
}
 

	
 
// gets pos from vehicle and next orders
 
static bool AirportMove(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	AirportFTA *current;
 
	byte prev_pos;
 
	bool retval = false;
misc.c
Show inline comments
 
@@ -29,200 +29,194 @@ char _name_array[512][32];
 
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 (uint16)DoRandom(line, file) * max >> 16;
 
}
 
#else
 
uint RandomRange(uint max)
 
{
 
	return (uint16)Random() * 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 (uint16)InteractiveRandom() * max >> 16;
 
}
 

	
 
void SetDate(uint date)
 
{
 
	YearMonthDay ymd;
 
	ConvertDayToYMD(&ymd, _date = date);
 
	_cur_year = ymd.year;
 
	_cur_month = ymd.month;
 
#ifdef ENABLE_NETWORK
 
	_network_last_advertise_date = 0;
 
#endif /* ENABLE_NETWORK */
 
}
 

	
 
void InitializeVehicles(void);
 
void InitializeWaypoints(void);
 
void InitializeDepot(void);
 
void InitializeOrders(void);
 
void InitializeClearLand(void);
 
void InitializeRail(void);
 
void InitializeRailGui(void);
 
void InitializeRoad(void);
 
void InitializeRoadGui(void);
 
void InitializeAirportGui(void);
 
void InitializeDock(void);
 
void InitializeDockGui(void);
 
void InitializeIndustries(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 GenerateLandscape(void);
 
void GenerateClearTile(void);
 

	
 
void GenerateIndustries(void);
 
void GenerateUnmovables(void);
 
void GenerateTowns(void);
 

	
 
void StartupPlayers(void);
 
void StartupDisasters(void);
 
void GenerateTrees(void);
 

	
 
void ConvertGroundTilesIntoWaterTiles(void);
 

	
 
void InitializeGame(uint size_x, uint size_y)
 
{
 
	uint i;
 

	
 
	AllocateMap(size_x, size_y);
 

	
 
	// Initialize the autoreplace array. Needs to be cleared between each game
 
	for (i = 0; i < lengthof(_autoreplace_array); i++)
 
		_autoreplace_array[i] = i;
 

	
 
	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;
 

	
 
	{
 
		uint starting = ConvertIntDate(_patches.starting_date);
 
		if ( starting == (uint)-1) starting = 10958;
 
		SetDate(starting);
 
	}
 

	
 
	InitializeVehicles();
 
	InitializeWaypoints();
 
	InitializeDepot();
 
	InitializeOrders();
 

	
 
	InitNewsItemStructs();
 
	InitializeLandscape();
 
	InitializeClearLand();
 
	InitializeRail();
 
	InitializeRailGui();
 
	InitializeRoad();
 
	InitializeRoadGui();
 
	InitializeAirportGui();
 
	InitializeDock();
 
	InitializeDockGui();
 
	InitializeTowns();
 
	InitializeTrees();
 
	InitializeSigns();
 
	InitializeStations();
 
	InitializeIndustries();
 

	
 
	InitializeNameMgr();
 
	InitializeVehiclesGuiList();
 
	InitializeTrains();
 
	InitializeNPF();
 

	
 
	InitializePlayers();
 
	InitializeCheats();
 

	
 
	InitTextEffects();
 
	InitTextMessage();
 
	InitializeAnimatedTiles();
 

	
 
	InitializeLandscapeVariables(false);
 

	
 
	ResetObjectToPlace();
 
}
 

	
 
void GenerateWorld(int mode, uint size_x, uint size_y)
 
{
 
	int i;
 

	
 
	// Make sure everything is done via OWNER_NONE
 
	_current_player = OWNER_NONE;
 

	
 
	_generating_world = true;
 
	InitializeGame(size_x, size_y);
 
	SetObjectToPlace(SPR_CURSOR_ZZZ, 0, 0, 0);
 

	
 
	// Must start economy early because of the costs.
 
	StartupEconomy();
 

	
 
	// Don't generate landscape items when in the scenario editor.
 
	if (mode == 1) {
 
		// empty world in scenario editor
 
		ConvertGroundTilesIntoWaterTiles();
 
	} else {
 
		GenerateLandscape();
 
		GenerateClearTile();
 

	
 
		// only generate towns, tree and industries in newgame mode.
 
		if (mode == 0) {
 
			GenerateTowns();
 
			GenerateTrees();
 
			GenerateIndustries();
 
			GenerateUnmovables();
 
		}
 
	}
 

	
 
	// These are probably pointless when inside the scenario editor.
 
	StartupPlayers();
 
	StartupEngines();
 
	StartupDisasters();
 
	_generating_world = false;
 

	
 
	// No need to run the tile loop in the scenario editor.
 
	if (mode != 1) {
 
		for(i=0x500; i!=0; i--)
 
			RunTileLoop();
 
	}
network_client.c
Show inline comments
 
@@ -434,192 +434,195 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER
 
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)("[NET] The server is currently busy sending the map to someone else.. please hold..." );
 
	DEBUG(net, 1)("[NET]  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->quited)
 
		return NETWORK_RECV_STATUS_CONN_LOST;
 

	
 
	// First packet, init some stuff
 
	if (maptype == MAP_PACKET_START) {
 
		// The name for the temp-map
 
		sprintf(filename, "%s%snetwork_client.tmp",  _path.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);
 
	}
 

	
 
	if (maptype == MAP_PACKET_PATCH) {
 
		NetworkRecvPatchSettings(MY_CLIENT, p);
 
	}
 

	
 
	// 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
 
		// Load the map
 
		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;
 
		}
 

	
 
		_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)();
 

	
 
		if (_network_playas == 0 || _network_playas > MAX_PLAYERS ||
 
				!GetPlayer(_network_playas - 1)->is_active) {
 

	
 
			if (_network_playas == OWNER_SPECTATOR) {
 
				// The client wants to be a spectator..
 
				_local_player = OWNER_SPECTATOR;
 
				DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
			} else {
 
				/* 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 */
 
				_local_player = 0;
 
				NetworkSend_Command(0, 0, 0, CMD_PLAYER_CTRL, NULL);
 
				_local_player = OWNER_SPECTATOR;
 
			}
 
		} else {
 
			// take control over an existing company
 
			_local_player = _network_playas - 1;
 
			_patches.autorenew = GetPlayer(_local_player)->engine_renew;
 
			_patches.autorenew_months = GetPlayer(_local_player)->engine_renew_months;
 
			_patches.autorenew_money = GetPlayer(_local_player)->engine_renew_money;
 
			DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
		}
 
	}
 

	
 
	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, 7)("[NET] 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,6)("[NET] 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)
 
{
 
	NetworkAction action = NetworkRecv_uint8(MY_CLIENT, p);
 
	char msg[MAX_TEXT_MSG_LEN];
 
	NetworkClientInfo *ci = NULL, *ci_to;
 
	uint16 index;
 
	char name[NETWORK_NAME_LENGTH];
 
	bool self_send;
 

	
 
	index = NetworkRecv_uint16(MY_CLIENT, p);
 
	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;
 

	
 
	/* Do we display the action locally? */
 
	if (self_send) {
 
		switch (action) {
 
			case NETWORK_ACTION_CHAT_CLIENT:
 
				/* For speak to client we need the client-name */
 
				snprintf(name, sizeof(name), "%s", ci_to->client_name);
openttd.c
Show inline comments
 
@@ -486,264 +486,268 @@ int ttd_main(int argc, char* argv[])
 

	
 
	GenerateWorld(1, 64, 64); // Make the viewport initialization happy
 

	
 
#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;
 

	
 
			ParseConnectionString(&player, &port, network_conn);
 

	
 
			if (player != NULL) _network_playas = atoi(player);
 
			if (port != NULL) rport = atoi(port);
 

	
 
			LoadIntroGame();
 
			_switch_mode = SM_NONE;
 
			NetworkClientConnectGame(network_conn, rport);
 
		}
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	_video_driver->main_loop();
 

	
 
	WaitTillSaved();
 
	IConsoleFree();
 

	
 
#ifdef ENABLE_NETWORK
 
	if (_network_available) {
 
		// Shut down the network and close any open connections
 
		NetworkDisconnect();
 
		NetworkUDPClose();
 
		NetworkShutDown();
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	_video_driver->stop();
 
	_music_driver->stop();
 
	_sound_driver->stop();
 

	
 
	SaveToConfig();
 
	SaveToHighScore();
 

	
 
	// uninitialize airport state machines
 
	UnInitializeAirports();
 

	
 
	/* uninitialize variables that are allocated dynamic */
 
	UnInitializeDynamicVariables();
 

	
 
	/* Close all and any open filehandles */
 
	FioCloseAll();
 
	UnInitializeGame();
 

	
 
	return 0;
 
}
 

	
 
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 MakeNewGame(void)
 
{
 
	_game_mode = GM_NORMAL;
 

	
 
	// Copy in game options
 
	_opt_ptr = &_opt;
 
	memcpy(_opt_ptr, &_opt_newgame, sizeof(GameOptions));
 

	
 
	GfxLoadSprites();
 

	
 
	// Reinitialize windows
 
	ResetWindowSystem();
 
	LoadStringWidthTable();
 

	
 
	SetupColorsAndInitialWindow();
 

	
 
	// Randomize world
 
	GenerateWorld(0, 1<<_patches.map_x, 1<<_patches.map_y);
 

	
 
	// In a dedicated server, the server does not play
 
	if (_network_dedicated) {
 
		_local_player = OWNER_SPECTATOR;
 
	} else {
 
		// Create a single player
 
		DoStartupNewPlayer(false);
 

	
 
		_local_player = 0;
 
		_current_player = _local_player;
 
		DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_REPLACE_VEHICLE);
 
	}
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
static void MakeNewEditorWorld(void)
 
{
 
	_game_mode = GM_EDITOR;
 

	
 
	// Copy in game options
 
	_opt_ptr = &_opt;
 
	memcpy(_opt_ptr, &_opt_newgame, sizeof(GameOptions));
 

	
 
	GfxLoadSprites();
 

	
 
	// Re-init the windowing system
 
	ResetWindowSystem();
 

	
 
	// Create toolbars
 
	SetupColorsAndInitialWindow();
 

	
 
	// Startup the game system
 
	GenerateWorld(1, 1<<_patches.map_x, 1<<_patches.map_y);
 

	
 
	_local_player = OWNER_NONE;
 
	MarkWholeScreenDirty();
 
}
 

	
 
void StartupPlayers(void);
 
void StartupDisasters(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) {
 
		printf("Savegame is obsolete or invalid format: %s\n", _file_to_saveload.name);
 
		ShowErrorMessage(_error_message, STR_4009_GAME_LOAD_FAILED, 0, 0);
 
		_game_mode = GM_MENU;
 
		return;
 
	}
 

	
 
	GfxLoadSprites();
 

	
 
	// Reinitialize windows
 
	ResetWindowSystem();
 
	LoadStringWidthTable();
 

	
 
	SetupColorsAndInitialWindow();
 

	
 
	// Load game
 
	if (SaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode) != SL_OK) {
 
		LoadIntroGame();
 
		ShowErrorMessage(_error_message, STR_4009_GAME_LOAD_FAILED, 0, 0);
 
	}
 

	
 
	_opt_ptr = &_opt;
 
	memcpy(&_opt_ptr->diff, &_opt_newgame.diff, sizeof(GameDifficulty));
 
	_opt.diff_level = _opt_newgame.diff_level;
 

	
 
	// Inititalize data
 
	StartupPlayers();
 
	StartupEngines();
 
	StartupDisasters();
 

	
 
	_local_player = 0;
 
	_current_player = _local_player;
 
	DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_REPLACE_VEHICLE);
 

	
 
	MarkWholeScreenDirty();
 
}
 

	
 
bool SafeSaveOrLoad(const char *filename, int mode, int newgm)
 
{
 
	byte ogm = _game_mode;
 
	int r;
 

	
 
	_game_mode = newgm;
 
	r = SaveOrLoad(filename, mode);
 
	if (r == SL_REINIT) {
 
		if (ogm == GM_MENU)
 
			LoadIntroGame();
 
		else if (ogm == GM_EDITOR)
 
			MakeNewEditorWorld();
 
		else
 
			MakeNewGame();
 
		return false;
 
	} else if (r != SL_OK) {
 
		_game_mode = ogm;
 
		return false;
 
	} else
 
		return true;
 
}
 

	
 
void SwitchMode(int new_mode)
 
{
 
	_in_state_game_loop = true;
 

	
 
#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();
 
				NetworkUDPClose();
 
			} else {
 
				NetworkDisconnect();
 
				NetworkUDPClose();
 
			}
 
		}
 

	
 
		// 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, NETWORK_NAME_LENGTH, "Random Map");
 
#endif /* ENABLE_NETWORK */
 
		MakeNewGame();
 
		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, NETWORK_NAME_LENGTH, "%s (Loaded scenario)", _file_to_saveload.title);
 
		#endif /* ENABLE_NETWORK */
 
		StartScenario();
 
		break;
 

	
 
	case SM_LOAD: { /* Load game, Play Scenario */
 
		_opt_ptr = &_opt;
 

	
 
		_error_message = INVALID_STRING_ID;
 
		if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) {
 
			LoadIntroGame();
 
			ShowErrorMessage(_error_message, STR_4009_GAME_LOAD_FAILED, 0, 0);
 
		} else {
 
			_local_player = 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, NETWORK_NAME_LENGTH, "%s (Loaded game)", _file_to_saveload.title);
 
#endif /* ENABLE_NETWORK */
 
		}
 
		break;
 
	}
 

	
 
@@ -1149,102 +1153,119 @@ bool AfterLoadGame(uint version)
 
	// in version 2.2 of the savegame, we have new airports
 
	if (version <= 0x201) {
 
		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.0 of the savegame, is_active was introduced to determine
 
	// if a player does exist, rather then checking name_1
 
	if (version <= 0x400) {
 
		CheckIsPlayerActive();
 
	}
 

	
 
	// the void tiles on the southern border used to belong to a wrong class.
 
	if (version <= 0x402)
 
		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 (version <= 0x500) {
 
		UpdateOilRig();
 
	}
 

	
 
	if (version <= 0x600) {
 
		BEGIN_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0) {
 
			if (IsTileType(tile, MP_HOUSE)) {
 
				_m[tile].m4 = _m[tile].m2;
 
				//XXX magic
 
				SetTileType(tile, MP_VOID);
 
				_m[tile].m2 = ClosestTownFromTile(tile,(uint)-1)->index;
 
				SetTileType(tile, MP_HOUSE);
 
			} else if (IsTileType(tile, MP_STREET)) {
 
				//XXX magic
 
				_m[tile].m4 |= (_m[tile].m2 << 4);
 
				if (IsTileOwner(tile, OWNER_TOWN)) {
 
					SetTileType(tile, MP_VOID);
 
					_m[tile].m2 = ClosestTownFromTile(tile,(uint)-1)->index;
 
					SetTileType(tile, MP_STREET);
 
				} else {
 
					_m[tile].m2 = 0;
 
				}
 
			}
 
		} END_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0);
 
	}
 

	
 
	if (version < 0x900) {
 
		Town *t;
 
		FOR_ALL_TOWNS(t) {
 
			UpdateTownMaxPass(t);
 
		}
 
	}
 

	
 
	if (version < 0xF00) {
 
		BEGIN_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0) {
 
			if (IsTileType(tile, MP_RAILWAY) && HasSignals(tile) && HASBIT(_m[tile].m4, 2)) {
 
				CLRBIT(_m[tile].m4, 2);
 
				SETBIT(_m[tile].m4, 3);
 
			}
 
		} END_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0);
 
	}
 

	
 
	if (version < 0x1000) {
 
		int i;
 
		FOR_ALL_PLAYERS(p) {
 
			for (i = 0; i < 256; i++) {
 
				p->engine_replacement[i] = INVALID_ENGINE;
 
			}
 
			p->engine_renew = false;
 
			p->engine_renew_months = -6;
 
			p->engine_renew_money = 100000;
 
		}
 
		// Set the human controlled player to the patch settings
 
		p = GetPlayer(_local_player);
 
		p->engine_renew = _patches.autorenew;
 
		p->engine_renew_months = _patches.autorenew_months;
 
		p->engine_renew_money = _patches.autorenew_money;
 
	}
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		p->avail_railtypes = GetPlayerRailtypes(p->index);
 
	}
 

	
 
	return true;
 
}
player.h
Show inline comments
 
@@ -94,176 +94,180 @@ typedef struct Ai_SpecialVehicle {
 
typedef struct PlayerAiNew {
 
	uint8 state;
 
	uint tick;
 
	uint idle;
 

	
 
	int temp; 	// A value used in more than one function, but it just temporary
 
				// The use is pretty simple: with this we can 'think' about stuff
 
				//   in more than one tick, and more than one AI. A static will not
 
				//   do, because they are not saved. This way, the AI is almost human ;)
 
	int counter; 	// For the same reason as temp, we have counter. It can count how
 
					//  long we are trying something, and just abort if it takes too long
 

	
 
	// Pathfinder stuff
 
	Ai_PathFinderInfo path_info;
 
	AyStar *pathfinder;
 

	
 
	// Route stuff
 

	
 
	byte cargo;
 
	byte tbt; // train/bus/truck 0/1/2 AI_TRAIN/AI_BUS/AI_TRUCK
 
	int new_cost;
 

	
 
	byte action;
 

	
 
	int last_id; // here is stored the last id of the searched city/industry
 
	uint last_vehiclecheck_date; // Used in CheckVehicle
 
	Ai_SpecialVehicle special_vehicles[AI_MAX_SPECIAL_VEHICLES]; // Some vehicles have some special flags
 

	
 
	TileIndex from_tile;
 
	TileIndex to_tile;
 

	
 
	byte from_direction;
 
	byte to_direction;
 

	
 
	bool from_deliver; // True if this is the station that GIVES cargo
 
	bool to_deliver;
 

	
 
	TileIndex depot_tile;
 
	byte depot_direction;
 

	
 
	byte amount_veh; // How many vehicles we are going to build in this route
 
	byte cur_veh; // How many vehicles did we bought?
 
	VehicleID veh_id; // Used when bought a vehicle
 
	VehicleID veh_main_id; // The ID of the first vehicle, for shared copy
 

	
 
	int from_ic; // ic = industry/city. This is the ID of them
 
	byte from_type; // AI_NO_TYPE/AI_CITY/AI_INDUSTRY
 
	int to_ic;
 
	byte to_type;
 

	
 
} PlayerAiNew;
 

	
 

	
 

	
 
typedef struct Player {
 
	uint32 name_2;
 
	uint16 name_1;
 

	
 
	uint16 president_name_1;
 
	uint32 president_name_2;
 

	
 
	uint32 face;
 

	
 
	int32 player_money;
 
	int32 current_loan;
 
	int64 money64; // internal 64-bit version of the money. the 32-bit field will be clamped to plus minus 2 billion
 

	
 
	byte player_color;
 
	byte player_money_fraction;
 
	byte avail_railtypes;
 
	byte block_preview;
 
	PlayerID index;
 

	
 
	uint16 cargo_types; /* which cargo types were transported the last year */
 

	
 
	TileIndex location_of_house;
 
	TileIndex last_build_coordinate;
 

	
 
	byte share_owners[4];
 

	
 
	byte inaugurated_year;
 
	byte num_valid_stat_ent;
 

	
 
	byte quarters_of_bankrupcy;
 
	byte bankrupt_asked; // which players were asked about buying it?
 
	int16 bankrupt_timeout;
 
	int32 bankrupt_value;
 

	
 
	bool is_active;
 
	byte is_ai;
 
	PlayerAI ai;
 
	PlayerAiNew ainew;
 

	
 
	int64 yearly_expenses[3][13];
 
	PlayerEconomyEntry cur_economy;
 
	PlayerEconomyEntry old_economy[24];
 
	EngineID engine_replacement[256];
 
	bool engine_renew;
 
	int16 engine_renew_months;
 
	uint32 engine_renew_money;
 
} Player;
 

	
 
void ChangeOwnershipOfPlayerItems(byte old_player, byte new_player);
 
void GetNameOfOwner(byte owner, TileIndex tile);
 
int64 CalculateCompanyValue(Player *p);
 
void InvalidatePlayerWindows(Player *p);
 
void AiDoGameLoop(Player *p);
 
void UpdatePlayerMoney32(Player *p);
 
#define FOR_ALL_PLAYERS(p) for(p=_players; p != endof(_players); p++)
 

	
 
extern PlayerID _current_player;
 

	
 
#define MAX_PLAYERS 8
 
VARDEF Player _players[MAX_PLAYERS];
 
// NOSAVE: can be determined from player structs
 
VARDEF byte _player_colors[MAX_PLAYERS];
 

	
 
static inline Player* GetPlayer(uint i)
 
{
 
  assert(i < lengthof(_players));
 
  return &_players[i];
 
}
 

	
 
/** Returns the number of rail types the player can build
 
  * @param *p Player in question
 
  */
 
static inline int GetNumRailtypes(Player *p)
 
{
 
	int num = 0;
 
	int i;
 

	
 
	for (i = 0; i < (int)sizeof(p->avail_railtypes) * 8; i++)
 
		if (HASBIT(p->avail_railtypes, i)) num++;
 

	
 
	assert(num <= RAILTYPE_END);
 
	return num;
 
}
 

	
 
byte GetPlayerRailtypes(int p);
 

	
 
/** Finds out if a Player has a certain railtype available
 
  */
 
static inline bool HasRailtypeAvail(Player *p, RailType Railtype)
 
{
 
	return HASBIT(p->avail_railtypes, Railtype);
 
}
 

	
 
/* Validate functions for rail building */
 
static inline bool ValParamRailtype(uint32 rail) { return HASBIT(GetPlayer(_current_player)->avail_railtypes, rail);}
 

	
 
/** Returns the "best" railtype a player can build.
 
  * As the AI doesn't know what the BEST one is, we
 
  * have our own priority list here. When adding
 
  * new railtypes, modify this function
 
  * @param p the player "in action"
 
  * @return The "best" railtype a player has available
 
  */
 
static inline byte GetBestRailtype(Player *p)
 
{
 
	if (HasRailtypeAvail(p, RAILTYPE_MAGLEV)) return RAILTYPE_MAGLEV;
 
	if (HasRailtypeAvail(p, RAILTYPE_MONO)) return RAILTYPE_MONO;
 
	return RAILTYPE_RAIL;
 
}
 

	
 
#define IS_HUMAN_PLAYER(p) (!GetPlayer((byte)(p))->is_ai)
 
#define IS_INTERACTIVE_PLAYER(p) (((byte)p) == _local_player)
 

	
 
typedef struct HighScore {
 
	char company[100];
 
	StringID title;
 
	uint16 score;
 
} HighScore;
 

	
 
VARDEF HighScore _highscore_table[5][5]; // 4 difficulty-settings (+ network); top 5
 
void SaveToHighScore(void);
 
void LoadFromHighScore(void);
 
int8 SaveHighScoreValue(const Player *p);
 
int8 SaveHighScoreValueNetwork(void);
 

	
 
#endif /* PLAYER_H */
players.c
Show inline comments
 
@@ -381,424 +381,547 @@ static byte GeneratePlayerColor(void)
 
		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
 
	i = 0;
 
	for(;;) {
 
		if (colors[i] != 0xFF)
 
			return colors[i];
 
		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);
 
		if (strlen(buffer) >= 32 || GetStringWidth(buffer) >= 94)
 
			continue;
 

	
 
		FOR_ALL_PLAYERS(pp) {
 
			if (pp->is_active && p != pp) {
 
				SetDParam(0, pp->president_name_2);
 
				GetString(buffer2, pp->president_name_1);
 
				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;
 
}
 

	
 
Player *DoStartupNewPlayer(bool is_ai)
 
{
 
	Player *p;
 
	int index;
 
	int index, i;
 

	
 
	p = AllocatePlayer();
 
	if (p == NULL) return NULL;
 

	
 
	index = p->index;
 

	
 
	// Make a color
 
	p->player_color = GeneratePlayerColor();
 
	_player_colors[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] = 0xFF;
 

	
 
	p->avail_railtypes = GetPlayerRailtypes(index);
 
	p->inaugurated_year = _cur_year;
 
	p->face = Random();
 

	
 
	/* Engine renewal settings */
 
	for (i = 0; i < 256; i++)
 
		p->engine_replacement[i] = INVALID_ENGINE;
 

	
 
	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);
 

	
 
	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(p=_players; p!=endof(_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)
 
		if (n < (!_network_server ? RandomRange(_opt.diff.max_no_competitors + 2) : InteractiveRandomRange(_opt.diff.max_no_competitors + 2)) )
 
		DoStartupNewPlayer(true);
 

	
 
	// 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) ? RandomRange(60 * DAY_TICKS) : InteractiveRandomRange(60 * DAY_TICKS);
 
}
 

	
 
void InitializePlayers(void)
 
{
 
	int 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 (!_networking && _game_mode != GM_MENU && !--_next_competitor_start) {
 
		MaybeStartNewPlayer();
 
	}
 
}
 

	
 
void RunOtherPlayersLoop(void)
 
{
 
	Player *p;
 

	
 
	_is_ai_player = true;
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active && p->is_ai) {
 
			_current_player = p->index;
 
			if (_patches.ainew_active)
 
				AiNewDoGameLoop(p);
 
			else
 
				AiDoGameLoop(p);
 
		}
 
	}
 

	
 
	_is_ai_player = false;
 
	_current_player = OWNER_NONE;
 
}
 

	
 
// index is the next parameter in _decode_parameters to set up
 
StringID GetPlayerNameString(byte player, byte index)
 
{
 
	if (IS_HUMAN_PLAYER(player) && player < MAX_PLAYERS) {
 
		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 != OWNER_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);
 
		}
 
	}
 
}
 

	
 
void DeletePlayerWindows(int pi)
 
{
 
	DeleteWindowById(WC_COMPANY, pi);
 
	DeleteWindowById(WC_FINANCES, pi);
 
	DeleteWindowById(WC_STATION_LIST, pi);
 
	/* The vehicle list windows also have station in the window_number
 
	 * A stationindex of -1 means the global vehicle list */
 
	DeleteWindowById(WC_TRAINS_LIST, (-1 << 16) | pi);
 
	DeleteWindowById(WC_ROADVEH_LIST, (-1 << 16) | pi);
 
	DeleteWindowById(WC_SHIPS_LIST, (-1 << 16) | pi);
 
	DeleteWindowById(WC_AIRCRAFT_LIST, (-1 << 16) | pi);
 
	DeleteWindowById(WC_BUY_COMPANY, pi);
 
}
 

	
 
byte GetPlayerRailtypes(int p)
 
{
 
	Engine *e;
 
	int rt = 0;
 
	int i;
 

	
 
	for(e = _engines, i = 0; i != lengthof(_engines); e++, i++) {
 
		if (!HASBIT(e->player_avail, p))
 
			continue;
 

	
 
		/* Skip all wagons */
 
		if ((i >= 27 && i < 54) || (i >= 57 && i < 84) || (i >= 89 && i < 116))
 
			continue;
 

	
 
		assert(e->railtype < RAILTYPE_END);
 
		SETBIT(rt, e->railtype);
 
	}
 

	
 
	return rt;
 
}
 

	
 
static void DeletePlayerStuff(int 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 x,y 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
 
 * @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
 
 */
 
int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Player *p;
 
	if (!(_current_player < MAX_PLAYERS))
 
		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 (_current_player == _local_player) {
 
					_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 (_current_player == _local_player) {
 
					_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 (_current_player == _local_player) {
 
					_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);
 

	
 
			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 && HASBIT(AircraftVehInfo(old_engine_type)->subtype, 0) != HASBIT(AircraftVehInfo(new_engine_type)->subtype, 0))
 
					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;
 
			}
 

	
 
			if (flags & DC_EXEC) {
 
				p->engine_replacement[old_engine_type] = new_engine_type;
 
			}
 
		} break;
 
		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 (_current_player == _local_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);
 
				}
 
			}
 
			break;
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Control the players: add, delete, etc.
 
 * @param x,y 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(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (flags & DC_EXEC) _current_player = OWNER_NONE;
 

	
 
	switch (p1) {
 
	case 0: { /* Create a new player */
 
		Player *p;
 
		PlayerID pid = p2;
 

	
 
		if (!(flags & DC_EXEC) || pid >= MAX_PLAYERS) return 0;
 

	
 
		p = DoStartupNewPlayer(false);
 

	
 
#ifdef ENABLE_NETWORK
 
		if (_networking && !_network_server && _local_player == OWNER_SPECTATOR)
 
			/* In case we are a client joining a server... */
 
			DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
#endif /* ENABLE_NETWORK */
 

	
 
		if (p != NULL) {
 
			if (_local_player == OWNER_SPECTATOR) {
 
				/* Check if we do not want to be a spectator in network */
 
				if (!_networking ||  (_network_server && !_network_dedicated) || _network_playas != OWNER_SPECTATOR) {
 
					_local_player = p->index;
 
					MarkWholeScreenDirty();
 
				}
 
			} else if (p->index == _local_player) {
 
				DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_REPLACE_VEHICLE);
 
			}
 
#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[pid];
 
				ci->client_playas = p->index + 1;
 
				NetworkUpdateClientInfo(ci->client_index);
 

	
 
				if (ci->client_playas != 0 && ci->client_playas <= MAX_PLAYERS) {
 
					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 - 1;
 
					NetworkSend_Command(0, 0, 0, CMD_CHANGE_PRESIDENT_NAME, NULL);
 
					_local_player = player_backup;
 
				}
 
			}
 
		} else 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[pid];
 
			ci->client_playas = OWNER_SPECTATOR;
 
			NetworkUpdateClientInfo(ci->client_index);
 
		}
 
#else
 
		}
 
#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 (p2 >= MAX_PLAYERS) return CMD_ERROR;
 

	
 
		if (!(flags & DC_EXEC)) return 0;
 

	
 
		p = GetPlayer(p2);
 

	
 
		/* Only allow removal of HUMAN companies */
 
		if (IS_HUMAN_PLAYER(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 + 16*3), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 

	
 
			/* Remove the company */
 
			ChangeOwnershipOfPlayerItems(p->index, OWNER_SPECTATOR);
 
			p->money64 = p->player_money = 100000000; // XXX - wtf?
 
			p->is_active = false;
 
		}
 
	} 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 (pid_old >= MAX_PLAYERS || pid_new >= MAX_PLAYERS) 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_performance_titles[16] = {
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
@@ -929,192 +1052,198 @@ int8 SaveHighScoreValueNetwork(void)
 
/* Save HighScore table to file */
 
void SaveToHighScore(void)
 
{
 
	FILE *fp = fopen(_highscore_file, "w");
 

	
 
	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 : 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(&hs->title, sizeof(hs->title), 1, fp);
 
			}
 
		}
 
		fclose(fp);
 
	}
 
}
 

	
 
/* Initialize the highscore table to 0 and if any file exists, load in values */
 
void LoadFromHighScore(void)
 
{
 
	FILE *fp = fopen(_highscore_file, "r");
 

	
 
	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);
 
				fread(&hs->title, sizeof(hs->title), 1, fp);
 
			}
 
		}
 
		fclose(fp);
 
	}
 

	
 
	/* Initialize end of game variable (when to show highscore chart) */
 
	 _patches.ending_date = 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, 255),
 

	
 
	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, 255),
 
	SLE_CONDVAR(Player, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(Player, last_build_coordinate, SLE_UINT32, 6, 255),
 
	SLE_VAR(Player,inaugurated_year,SLE_UINT8),
 

	
 
	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, 255),
 

	
 
	SLE_CONDVAR(Player,is_ai,			SLE_UINT8, 2, 255),
 
	SLE_CONDVAR(Player,is_active,	SLE_UINT8, 4, 255),
 

	
 
	// Engine renewal settings
 
	SLE_CONDARR(Player,engine_replacement,  SLE_UINT16, 256, 16, 255),
 
	SLE_CONDVAR(Player,engine_renew,         SLE_UINT8,      16, 255),
 
	SLE_CONDVAR(Player,engine_renew_months,  SLE_INT16,      16, 255),
 
	SLE_CONDVAR(Player,engine_renew_money,  SLE_UINT32,      16, 255),
 

	
 
	// reserve extra space in savegame here. (currently 64 bytes)
 
	SLE_CONDARR(NullStruct,null,SLE_FILE_U64 | SLE_VAR_NULL, 8, 2, 255),
 

	
 
	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, 255),
 
	SLE_CONDVAR(PlayerEconomyEntry,expenses,SLE_FILE_I64 | SLE_VAR_I32, 2, 255),
 
	SLE_CONDVAR(PlayerEconomyEntry,company_value, SLE_INT64, 2, 255),
 

	
 
	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, 255),
 
	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, 255),
 
	SLE_CONDVAR(PlayerAI, cur_tile_a,   SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(PlayerAI, cur_tile_a,   SLE_UINT32, 6, 255),
 
	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, 255),
 
	SLE_CONDVAR(PlayerAI, cur_tile_b,   SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(PlayerAI, cur_tile_b,   SLE_UINT32, 6, 255),
 
	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_CONDARR(NullStruct,null,SLE_FILE_U64 | SLE_VAR_NULL, 8, 2, 255),
 
	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, 255),
 
	SLE_CONDVAR(AiBuildRec,use_tile,  SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(AiBuildRec,use_tile,  SLE_UINT32, 6, 255),
 
	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 void SaveLoad_PLYR(Player *p) {
 
	int i;
 

	
 
	SlObject(p, _player_desc);
 

	
 
	// Write AI?
 
	if (!IS_HUMAN_PLAYER(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);
 

	
railtypes.h
Show inline comments
 
/* $Id$ */
 

	
 
/** @file railtypes.h
 
 * All the railtype-specific information is stored here.
 
 */
 

	
 
/** Global Railtype definition
 
 */
 
const RailtypeInfo _railtypes[RAILTYPE_END] = {
 
	/** Railway */
 
	{ /* Main Sprites */
 
		{ SPR_RAIL_TRACK_Y, SPR_RAIL_TRACK_N_S, SPR_RAIL_TRACK_BASE, SPR_RAIL_SINGLE_Y, SPR_RAIL_SINGLE_X,
 
			SPR_RAIL_SINGLE_NORTH, SPR_RAIL_SINGLE_SOUTH, SPR_RAIL_SINGLE_EAST, SPR_RAIL_SINGLE_WEST },
 

	
 
		/* GUI sprites */
 
		{ 0x4E3, 0x4E4, 0x4E5, 0x4E6,
 
			SPR_OPENTTD_BASE + 0, 0x50E, 0x97E, SPR_OPENTTD_BASE + 25 },
 

	
 
		/* strings */
 
		{ STR_100A_RAILROAD_CONSTRUCTION },
 

	
 
		/* Offset of snow tiles */
 
		SPR_RAIL_SNOW_OFFSET,
 

	
 
		/* Compatible railtypes */
 
		(1 << RAILTYPE_RAIL),
 
	
 

	
 
		/* main offset */
 
		0,
 
	},
 

	
 
	/** Monorail */
 
	{ /* Main Sprites */
 
		{ SPR_MONO_TRACK_Y, SPR_MONO_TRACK_N_S, SPR_MONO_TRACK_BASE, SPR_MONO_SINGLE_Y, SPR_MONO_SINGLE_X,
 
			SPR_MONO_SINGLE_NORTH, SPR_MONO_SINGLE_SOUTH, SPR_MONO_SINGLE_EAST, SPR_MONO_SINGLE_WEST },
 

	
 
		/* GUI sprites */
 
		{ 0x4E7, 0x4E8, 0x4E9, 0x4EA,
 
			SPR_OPENTTD_BASE + 1, SPR_OPENTTD_BASE + 12, 0x97F, SPR_OPENTTD_BASE + 27 },
 

	
 
		/* strings */
 
		{ STR_100B_MONORAIL_CONSTRUCTION },
 

	
 
		/* Offset of snow tiles */
 
		SPR_MONO_SNOW_OFFSET,
 

	
 
		/* Compatible Railtypes */
 
		(1 << RAILTYPE_MONO),
 

	
 
		/* main offset */
 
		82,
 
	},
 

	
 
	/** Maglev */
 
	{ /* Main sprites */
 
		{ SPR_MGLV_TRACK_Y, SPR_MGLV_TRACK_N_S, SPR_MGLV_TRACK_BASE, SPR_MGLV_SINGLE_Y, SPR_MGLV_SINGLE_X,
 
			SPR_MGLV_SINGLE_NORTH, SPR_MGLV_SINGLE_SOUTH, SPR_MGLV_SINGLE_EAST, SPR_MGLV_SINGLE_WEST },
 

	
 
		/* GUI sprites */
 
		{ 0x4EB, 0x4EC, 0x4EE, 0x4ED,
 
			SPR_OPENTTD_BASE + 2, SPR_OPENTTD_BASE + 13, 0x980, SPR_OPENTTD_BASE + 29 },
 

	
 
		/* strings */
 
		{ STR_100C_MAGLEV_CONSTRUCTION },
 

	
 
		/* Offset of snow tiles */
 
		SPR_MGLV_SNOW_OFFSET,
 

	
 
		/* Compatible Railtypes */
 
		(1 << RAILTYPE_MAGLEV),
 

	
 
		/* main offset */
 
		164,
 
	},
 
};
 

	
saveload.c
Show inline comments
 
/* $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 "vehicle.h"
 
#include "station.h"
 
#include "thread.h"
 
#include "town.h"
 
#include "player.h"
 
#include "saveload.h"
 
#include "variables.h"
 

	
 
enum {
 
	SAVEGAME_MAJOR_VERSION = 15,
 
	SAVEGAME_MAJOR_VERSION = 16,
 
	SAVEGAME_MINOR_VERSION = 0,
 

	
 
	SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION
 
};
 

	
 
enum NeedLengthValues {NL_NONE = 0, NL_WANTLENGTH = 1, NL_CALCLENGTH = 2};
 

	
 
SaverLoader _sl;
 

	
 
/**
 
 * 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);}
 

	
 
/** 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 int SlReadByteInternal(void)
 
{
 
	if (_sl.bufp == _sl.bufe) SlReadFill();
 
	return *_sl.bufp++;
 
}
 

	
 
/** Wrapper for SlReadByteInternal */
 
int 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;
 
}
settings_gui.c
Show inline comments
 
@@ -525,265 +525,283 @@ static const Widget _game_difficulty_wid
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,     3,   271,   357,    16,    27, STR_6804_CUSTOM,						STR_NULL},
 
{   WWT_CLOSEBOX,   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' */
 
	memcpy(&_opt_mod_temp, _opt_ptr, sizeof(GameOptions));
 
	AllocateWindowDesc(&_game_difficulty_desc);
 
}
 

	
 
// 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(-1, TEMP_AI_ACTIVATED, 0, 0);
 

	
 
  return 0;
 
}
 

	
 
static int32 PopulationInLabelActive(int32 p1)
 
{
 
	Town *t;
 

	
 
	FOR_ALL_TOWNS(t) {
 
		if (t->xy) {
 
			UpdateTownVirtCoord(t);
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
static int32 InvisibleTreesActive(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;
 
	if (p1) {
 
		warning = ( (IS_INT_INSIDE(_patches.servint_trains,   5, 90+1) || _patches.servint_trains   == 0) &&
 
								(IS_INT_INSIDE(_patches.servint_roadveh,  5, 90+1) || _patches.servint_roadveh  == 0) &&
 
								(IS_INT_INSIDE(_patches.servint_aircraft, 5, 90+1) || _patches.servint_aircraft == 0) &&
 
								(IS_INT_INSIDE(_patches.servint_ships,    5, 90+1) || _patches.servint_ships    == 0) );
 
	} else {
 
		warning = ( (IS_INT_INSIDE(_patches.servint_trains,   30, 800+1) || _patches.servint_trains   == 0) &&
 
								(IS_INT_INSIDE(_patches.servint_roadveh,  30, 800+1) || _patches.servint_roadveh  == 0) &&
 
								(IS_INT_INSIDE(_patches.servint_aircraft, 30, 800+1) || _patches.servint_aircraft == 0) &&
 
								(IS_INT_INSIDE(_patches.servint_ships,    30, 800+1) || _patches.servint_ships    == 0) );
 
	}
 

	
 
	if (!warning)
 
		ShowErrorMessage(-1, STR_CONFIG_PATCHES_SERVICE_INTERVAL_INCOMPATIBLE, 0, 0);
 

	
 
	return InValidateDetailsWindow(0);
 
}
 

	
 
static int32 EngineRenewUpdate(int32 p1)
 
{
 
	DoCommandP(0, 0, _patches.autorenew, NULL, CMD_REPLACE_VEHICLE);
 
	return 0;
 
}
 

	
 
static int32 EngineRenewMonthsUpdate(int32 p1)
 
{
 
	DoCommandP(0, 1, _patches.autorenew_months, NULL, CMD_REPLACE_VEHICLE);
 
	return 0;
 
}
 

	
 
static int32 EngineRenewMoneyUpdate(int32 p1)
 
{
 
	DoCommandP(0, 2, _patches.autorenew_money, NULL, CMD_REPLACE_VEHICLE);
 
	return 0;
 
}
 

	
 
typedef int32 PatchButtonClick(int32);
 

	
 
typedef struct PatchEntry {
 
  byte type;                    // type of selector
 
  byte flags;                   // selector flags
 
  StringID str;                 // string with descriptive text
 
  char console_name[40];        // the name this patch has in console
 
  void *variable;               // pointer to the variable
 
  int32 min, max;               // range for spinbox setting
 
  uint32 step;                  // step for spinbox
 
  PatchButtonClick *click_proc; // callback procedure
 
} PatchEntry;
 

	
 
enum {
 
	PE_BOOL			= 0,
 
	PE_UINT8		= 1,
 
	PE_INT16		= 2,
 
	PE_UINT16		= 3,
 
	PE_INT32		= 4,
 
	PE_CURRENCY	= 5,
 
	// selector flags
 
	PF_0ISDIS				= 1 << 0,
 
	PF_NOCOMMA			= 1 << 1,
 
	PF_MULTISTRING	= 1 << 2,
 
	PF_PLAYERBASED	= 1 << 3, // This has to match the entries that are in settings.c, patch_player_settings
 
	PF_NETWORK_ONLY = 1 << 4, // this setting only applies to network games
 
};
 

	
 
static const PatchEntry _patches_ui[] = {
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_VEHICLESPEED,		"vehicle_speed",		&_patches.vehicle_speed,						0,  0,  0, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_LONGDATE,				"long_date",				&_patches.status_long_date,					0,  0,  0, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_SHOWFINANCES,		"show_finances",		&_patches.show_finances,						0,  0,  0, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTOSCROLL,			"autoscroll",				&_patches.autoscroll,								0,  0,  0, NULL},
 

	
 
	{PE_UINT8,	PF_PLAYERBASED, STR_CONFIG_PATCHES_ERRMSG_DURATION,	"errmsg_duration",	&_patches.errmsg_duration,					0, 20,  1, NULL},
 

	
 
	{PE_UINT8,	PF_MULTISTRING | PF_PLAYERBASED, STR_CONFIG_PATCHES_TOOLBAR_POS, "toolbar_pos", &_patches.toolbar_pos,			0,  2,  1, &v_PositionMainToolbar},
 
	{PE_UINT8,	PF_0ISDIS | PF_PLAYERBASED, STR_CONFIG_PATCHES_SNAP_RADIUS, "window_snap_radius", &_patches.window_snap_radius,     1, 32,  1, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_INVISIBLE_TREES,	"invisible_trees", &_patches.invisible_trees,					0,  1,  1, &InvisibleTreesActive},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_POPULATION_IN_LABEL, "population_in_label", &_patches.population_in_label, 0, 1, 1, &PopulationInLabelActive},
 

	
 
	{PE_INT32, 0, STR_CONFIG_PATCHES_MAP_X, "map_x", &_patches.map_x, 6, 11, 1, NULL},
 
	{PE_INT32, 0, STR_CONFIG_PATCHES_MAP_Y, "map_y", &_patches.map_y, 6, 11, 1, NULL},
 
};
 

	
 
static const PatchEntry _patches_construction[] = {
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_BUILDONSLOPES,		"build_on_slopes",	&_patches.build_on_slopes,					0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_EXTRADYNAMITE,		"extra_dynamite",		&_patches.extra_dynamite,						0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_LONGBRIDGES,			"long_bridges",			&_patches.longbridges,							0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_SIGNALSIDE,				"signal_side",			&_patches.signal_side,							0,  0,  0, NULL},
 

	
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_SMALL_AIRPORTS,		"always_small_airport", &_patches.always_small_airport,			0,  0,  0, NULL},
 
	{PE_UINT8,	PF_PLAYERBASED, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY, "drag_signals_density", &_patches.drag_signals_density, 1, 20,  1, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_AUTO_PBS_PLACEMENT, "auto_pbs_placement", &_patches.auto_pbs_placement, 1, 20,  1, NULL},
 

	
 
};
 

	
 
static const PatchEntry _patches_vehicles[] = {
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_REALISTICACCEL,		"realistic_acceleration", &_patches.realistic_acceleration,		0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_FORBID_90_DEG,		"forbid_90_deg", 		&_patches.forbid_90_deg,						0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_MAMMOTHTRAINS,		"mammoth_trains", 	&_patches.mammoth_trains,						0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_GOTODEPOT,				"goto_depot", 			&_patches.gotodepot,								0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_ROADVEH_QUEUE,		"roadveh_queue", 		&_patches.roadveh_queue,						0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_NEW_PATHFINDING_ALL, "new_pathfinding_all", &_patches.new_pathfinding_all,		0,  0,  0, NULL},
 

	
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_WARN_INCOME_LESS, "train_income_warn", &_patches.train_income_warn,				0,  0,  0, NULL},
 
	{PE_UINT8,	PF_MULTISTRING | PF_PLAYERBASED, STR_CONFIG_PATCHES_ORDER_REVIEW, "order_review_system", &_patches.order_review_system,0,2,  1, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_NEVER_EXPIRE_VEHICLES, "never_expire_vehicles", &_patches.never_expire_vehicles,0,0,0, NULL},
 

	
 
	{PE_UINT16, PF_0ISDIS | PF_PLAYERBASED, STR_CONFIG_PATCHES_LOST_TRAIN_DAYS, "lost_train_days", &_patches.lost_train_days,	180,720, 60, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTORENEW_VEHICLE,"autorenew", &_patches.autorenew,								0,  0,  0, NULL},
 
	{PE_INT16,	PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTORENEW_MONTHS, "autorenew_months", &_patches.autorenew_months,				-12, 12,  1, NULL},
 
	{PE_CURRENCY, PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTORENEW_MONEY,"autorenew_money", &_patches.autorenew_money,					0, 2000000, 100000, NULL},
 
	{PE_BOOL,     PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTORENEW_VEHICLE, "autorenew",        &_patches.autorenew,                   0, 0, 0, &EngineRenewUpdate},
 
	{PE_INT16,	  PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTORENEW_MONTHS,  "autorenew_months", &_patches.autorenew_months,         -12, 12, 1, &EngineRenewMonthsUpdate},
 
	{PE_CURRENCY, PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTORENEW_MONEY,   "autorenew_money",  &_patches.autorenew_money,  0, 2000000, 100000, &EngineRenewMoneyUpdate},
 

	
 
	{PE_UINT16,	0, STR_CONFIG_PATCHES_MAX_TRAINS,				"max_trains", &_patches.max_trains,								0,5000, 50, NULL},
 
	{PE_UINT16,	0, STR_CONFIG_PATCHES_MAX_ROADVEH,			"max_roadveh", &_patches.max_roadveh,							0,5000, 50, NULL},
 
	{PE_UINT16,	0, STR_CONFIG_PATCHES_MAX_AIRCRAFT,			"max_aircraft", &_patches.max_aircraft,						0,5000, 50, NULL},
 
	{PE_UINT16,	0, STR_CONFIG_PATCHES_MAX_SHIPS,				"max_ships", &_patches.max_ships,									0,5000, 50, NULL},
 

	
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_SERVINT_ISPERCENT,"servint_isperfect",&_patches.servint_ispercent,				0,  0,  0, &CheckInterval},
 
	{PE_UINT16, PF_0ISDIS, STR_CONFIG_PATCHES_SERVINT_TRAINS,		"servint_trains",   &_patches.servint_trains,		5,800,  5, &InValidateDetailsWindow},
 
	{PE_UINT16, PF_0ISDIS, STR_CONFIG_PATCHES_SERVINT_ROADVEH,	"servint_roadveh",  &_patches.servint_roadveh,	5,800,  5, &InValidateDetailsWindow},
 
	{PE_UINT16, PF_0ISDIS, STR_CONFIG_PATCHES_SERVINT_AIRCRAFT, "servint_aircraft", &_patches.servint_aircraft, 5,800,  5, &InValidateDetailsWindow},
 
	{PE_UINT16, PF_0ISDIS, STR_CONFIG_PATCHES_SERVINT_SHIPS,		"servint_ships",    &_patches.servint_ships,		5,800,  5, &InValidateDetailsWindow},
 
	{PE_BOOL,   0,         STR_CONFIG_PATCHES_NOSERVICE,        "no_servicing_if_no_breakdowns", &_patches.no_servicing_if_no_breakdowns, 0, 0, 0, NULL},
 
};
 

	
 
static const PatchEntry _patches_stations[] = {
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_JOINSTATIONS,			"join_stations", &_patches.join_stations,						0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_FULLLOADANY,			"full_load_any", &_patches.full_load_any,						0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_IMPROVEDLOAD,			"improved_load", &_patches.improved_load,						0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_SELECTGOODS,			"select_goods",  &_patches.selectgoods,							0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_NEW_NONSTOP,			"new_nonstop", &_patches.new_nonstop,							0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_NONUNIFORM_STATIONS, "nonuniform_stations", &_patches.nonuniform_stations,		0,  0,  0, NULL},
 
	{PE_UINT8,	0, STR_CONFIG_PATCHES_STATION_SPREAD,		"station_spread", &_patches.station_spread,						4, 64,  1, &InvalidateStationBuildWindow},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_SERVICEATHELIPAD, "service_at_helipad", &_patches.serviceathelipad,					0,  0,  0, NULL},
 
	{PE_BOOL, 0, STR_CONFIG_PATCHES_CATCHMENT, "modified_catchment", &_patches.modified_catchment, 0, 0, 0, NULL},
 

	
 
};
 

	
 
static const PatchEntry _patches_economy[] = {
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_INFLATION,				"inflation", &_patches.inflation,								0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_BUILDXTRAIND,			"build_rawmaterial", &_patches.build_rawmaterial_ind,		0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_MULTIPINDTOWN,		"multiple_industry_per_town", &_patches.multiple_industry_per_town,0, 0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_SAMEINDCLOSE,			"same_industry_close", &_patches.same_industry_close,			0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_BRIBE,						"bribe", &_patches.bribe,										0,  0,  0, NULL},
 
	{PE_UINT8,	0, STR_CONFIG_PATCHES_SNOWLINE_HEIGHT,	"snow_line_height", &_patches.snow_line_height,					2, 13,  1, NULL},
 

	
 
	{PE_INT32,	PF_NOCOMMA, STR_CONFIG_PATCHES_COLORED_NEWS_DATE, "colored_new_data", &_patches.colored_news_date, 1900, 2200, 5, NULL},
 
	{PE_INT32,	PF_NOCOMMA, STR_CONFIG_PATCHES_STARTING_DATE, "starting_date", &_patches.starting_date,	 MAX_YEAR_BEGIN_REAL, MAX_YEAR_END_REAL, 1, NULL},
 
	{PE_INT32,	PF_NOCOMMA | PF_NETWORK_ONLY, STR_CONFIG_PATCHES_ENDING_DATE, "ending_date", &_patches.ending_date,	 MAX_YEAR_BEGIN_REAL, MAX_YEAR_END_REAL, 1, NULL},
 

	
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_SMOOTH_ECONOMY,		"smooth_economy", &_patches.smooth_economy,						0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_ALLOW_SHARES,			"allow_shares", &_patches.allow_shares,						0,  0,  0, NULL},
 
};
 

	
 
static const PatchEntry _patches_ai[] = {
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_AINEW_ACTIVE, "ainew_active", &_patches.ainew_active, 0, 1, 1, &AiNew_PatchActive_Warning},
 

	
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_AI_BUILDS_TRAINS, "ai_disable_veh_train", &_patches.ai_disable_veh_train,			0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_AI_BUILDS_ROADVEH,"ai_disable_veh_roadveh",&_patches.ai_disable_veh_roadveh,		0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_AI_BUILDS_AIRCRAFT,"ai_disable_veh_aircraft",&_patches.ai_disable_veh_aircraft,0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_AI_BUILDS_SHIPS,"ai_disable_veh_ship",&_patches.ai_disable_veh_ship,			0,  0,  0, NULL},
 
};
 

	
 
typedef struct PatchPage {
 
	const PatchEntry *entries;
 
	uint num;
 
} PatchPage;
 

	
 
static const PatchPage _patches_page[] = {
 
	{_patches_ui,						lengthof(_patches_ui) },
 
	{_patches_construction, lengthof(_patches_construction) },
 
	{_patches_vehicles,			lengthof(_patches_vehicles) },
 
	{_patches_stations,			lengthof(_patches_stations) },
 
	{_patches_economy,			lengthof(_patches_economy) },
 
	{_patches_ai,						lengthof(_patches_ai) },
 
};
 

	
 

	
 
static int32 ReadPE(const PatchEntry*pe)
 
{
 
	switch (pe->type) {
 
	case PE_BOOL:   return *(bool*)pe->variable;
 
	case PE_UINT8:  return *(uint8*)pe->variable;
 
	case PE_INT16:  return *(int16*)pe->variable;
 
	case PE_UINT16: return *(uint16*)pe->variable;
 
	case PE_INT32:  return *(int32*)pe->variable;
 
	case PE_CURRENCY:  return (*(int32*)pe->variable) * GetCurrentCurrencyRate();
 
	default: NOT_REACHED();
 
	}
 

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

	
 
static void WritePE(const PatchEntry *pe, int32 val)
 
{
 
	if ((pe->flags & PF_0ISDIS) && val <= 0) {
 
		switch (pe->type) {
 
			case PE_BOOL: case PE_UINT8:
 
				*(bool*)pe->variable = 0;
 
				break;
 
			case PE_INT16: case PE_UINT16:
 
				*(int16*)pe->variable = 0;
 
				break;
 
			case PE_CURRENCY: case PE_INT32:
 
				*(int32*)pe->variable = 0;
 
				break;
tunnelbridge_cmd.c
Show inline comments
 
/* $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 "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 "player.h"
 
#include "town.h"
 
#include "sound.h"
 
#include "pbs.h"
 
#include "debug.h"
 
#include "variables.h"
 
#include "bridge.h"
 

	
 
#include "table/bridge_land.h"
 
#include "table/tunnel_land.h"
 

	
 
extern const byte _track_sloped_sprites[14];
 
extern const SpriteID _water_shore_sprites[15];
 

	
 
extern void DrawCanalWater(TileIndex tile);
 

	
 
const Bridge _bridge[] = {
 
/*
 
	   year of availablity 
 
	   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              },
 
	{  0, 0,  2, 112,  48, 0xA26 | PALETTE_TO_STRUCT_RED     , STR_5013_CONCRETE            },
 
	{ 10, 0,  5, 144,  64, 0xA25                             , STR_500F_GIRDER_STEEL        },
 
	{  0, 2, 10, 168,  80, 0xA22 | PALETTE_TO_STRUCT_CONCRETE, STR_5011_SUSPENSION_CONCRETE },
 
	{ 10, 3, 16, 185,  96, 0xA22                             , STR_500E_SUSPENSION_STEEL    },
 
	{ 10, 3, 16, 192, 112, 0xA22 | PALETTE_TO_STRUCT_YELLOW  , STR_500E_SUSPENSION_STEEL	},
 
	{ 10, 3,  7, 224, 160, 0xA23                             , STR_5010_CANTILEVER_STEEL    },
 
	{ 10, 3,  8, 232, 208, 0xA23 | PALETTE_TO_STRUCT_BROWN   , STR_5010_CANTILEVER_STEEL    },
 
	{ 10, 3,  9, 248, 240, 0xA23 | PALETTE_TO_STRUCT_RED     , STR_5010_CANTILEVER_STEEL    },
 
	{ 10, 0,  2, 240, 256, 0xA27                             , STR_500F_GIRDER_STEEL        },
 
	{ 75, 2, 16, 255, 320, 0xA28                             , STR_5014_TUBULAR_STEEL       },
 
	{ 85, 2, 32, 380, 512, 0xA28 | PALETTE_TO_STRUCT_YELLOW  , STR_5014_TUBULAR_STEEL       },
 
	{ 90, 2, 32, 510, 608, 0xA28 | PALETTE_TO_STRUCT_GREY    , STR_BRIDGE_TUBULAR_SILICON   }
 
};
 

	
 
// 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,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;
 
	}
 
}
 

	
 
enum {
 
	// foundation, whole tile is leveled up (tileh's 7, 11, 13, 14) --> 3 corners raised
 
	BRIDGE_FULL_LEVELED_FOUNDATION = 1 << 7 | 1 << 11 | 1 << 13 | 1 << 14,
 
	// foundation, tile is partly leveled up (tileh's 1, 2, 4, 8) --> 1 corner raised
 
	BRIDGE_PARTLY_LEVELED_FOUNDATION = 1 << 1 | 1 << 2 | 1 << 4 | 1 << 8,
 
	// no foundations (X,Y direction) (tileh's 0, 3, 6, 9, 12)
 
	BRIDGE_NO_FOUNDATION = 1 << 0 | 1 << 3 | 1 << 6 | 1 << 9 | 1 << 12,
 
};
 

	
 
/**
 
 * Determines which piece of a bridge is contained in the current tile
 
 * @param tile The tile to analyze
 
 * @return the piece
 
 */
 
static inline int GetBridgePiece(TileIndex tile)
 
{
 
	return GB(_m[tile].m2, 0, 4);
 
}
 

	
 
/**
 
 * Determines the type of bridge on a tile
 
 * @param tile The tile to analyze
 
 * @return The bridge type
 
 */
 
static inline int GetBridgeType(TileIndex tile)
 
{
 
	return GB(_m[tile].m2, 4, 4);
 
}
 

	
 
/**	check if bridge can be built on slope
 
 *	direction 0 = X-axis, direction 1 = Y-axis
 
 *	is_start_tile = false		<-- end tile
 
 *	is_start_tile = true		<-- start tile
 
 */
 
static uint32 CheckBridgeSlope(uint direction, uint tileh, bool is_start_tile)
 
{
 
	if (!IsSteepTileh(tileh)) {	// disable building on very steep slopes
 

	
 
		if (is_start_tile) {
 
			/* check slope at start tile
 
					- no extra cost
 
					- direction X: tiles 0,12
 
					- direction Y: tiles 0, 9
 
			*/
 
			if ((direction?0x201:0x1001) & (1 << tileh))
 
				return 0;
 

	
 
			// disallow certain start tiles to avoid certain crooked bridges
 
			if (tileh == 2)
 
				return CMD_ERROR;
 

	
 
		}
 
		else {
 
			/*	check slope at end tile
 
					- no extra cost
 
					- direction X: tiles 0, 3
 
					- direction Y: tiles 0, 6
 
			*/
 
			if ((direction?0x41:0x9) & (1 << tileh))
 
				return 0;
 

	
variables.h
Show inline comments
 
@@ -341,103 +341,100 @@ VARDEF int _num_resolutions;
 
VARDEF uint16 _resolutions[32][2];
 
VARDEF uint16 _cur_resolution[2];
 

	
 
VARDEF char _savegame_format[8];
 

	
 
VARDEF char *_config_file;
 
VARDEF char *_highscore_file;
 
VARDEF char *_log_file;
 
VARDEF FILE *_log_file_fd;
 

	
 
// NOSAVE: These can be recalculated from InitializeLandscapeVariables
 
typedef struct {
 
	StringID names_s[NUM_CARGO];
 
	StringID names_long[NUM_CARGO];
 
	StringID names_short[NUM_CARGO];
 
	byte weights[NUM_CARGO];
 
	SpriteID sprites[NUM_CARGO];
 
	byte transit_days_1[NUM_CARGO];
 
	byte transit_days_2[NUM_CARGO];
 
	byte ai_railwagon[3][NUM_CARGO];
 
	byte ai_roadveh_start[NUM_CARGO];
 
	byte ai_roadveh_count[NUM_CARGO];
 
} CargoConst;
 

	
 
VARDEF CargoConst _cargoc;
 

	
 

	
 
static inline void SetDParamX(uint32 *s, uint n, uint32 v)
 
{
 
	s[n] = v;
 
}
 

	
 
static inline uint32 GetDParamX(const uint32 *s, uint n)
 
{
 
	return s[n];
 
}
 

	
 
static inline void SetDParam(uint n, uint32 v)
 
{
 
	assert(n < lengthof(_decode_parameters));
 
	_decode_parameters[n] = v;
 
}
 

	
 
static inline void SetDParam64(uint n, uint64 v)
 
{
 
	assert(n + 1 < lengthof(_decode_parameters));
 
	_decode_parameters[n + 0] = v & 0xffffffff;
 
	_decode_parameters[n + 1] = v >> 32;
 
}
 

	
 
static inline uint32 GetDParam(uint n)
 
{
 
	assert(n < lengthof(_decode_parameters));
 
	return _decode_parameters[n];
 
}
 

	
 
// Used to bind a C string name to a dparam number.
 
// NOTE: This has a short lifetime. You can't
 
//       use this string much later or it will be gone.
 
void SetDParamStr(uint n, const char *str);
 

	
 
// This function takes a C-string and allocates a temporary string ID.
 
// The duration of the bound string is valid only until the next acll to GetString,
 
// so be careful.
 
StringID BindCString(const char *str);
 

	
 

	
 
#define COPY_IN_DPARAM(offs,src,num) memcpy(_decode_parameters + offs, src, sizeof(uint32) * (num))
 
#define COPY_OUT_DPARAM(dst,offs,num) memcpy(dst,_decode_parameters + offs, sizeof(uint32) * (num))
 

	
 

	
 
#define SET_EXPENSES_TYPE(x) _yearly_expenses_type = x;
 

	
 
/* landscape.c */
 
extern const byte _tileh_to_sprite[32];
 
extern const byte _inclined_tileh[8];
 

	
 
extern const TileTypeProcs * const _tile_type_procs[16];
 

	
 
/* station_cmd.c */
 
// there are 5 types of airport (Country (3x4) , City(6x6), Metropolitan(6x6), International(7x7), Heliport(1x1)
 
// will become obsolete once airports are loaded from seperate file
 
extern const byte _airport_size_x[];
 
extern const byte _airport_size_y[];
 

	
 
/* misc */
 
VARDEF char _screenshot_name[128];
 
VARDEF byte _vehicle_design_names;
 

	
 
/* tunnelbridge */
 
#define MAX_BRIDGES 13
 

	
 
/* For new pathfinding. Define here so it is globally available without having
 
 * to include npf.h */
 
#define NPF_TILE_LENGTH 100
 

	
 
/* Autoreplace vehicle stuff*/
 
VARDEF byte _autoreplace_array[256];
 

	
 
/* Forking stuff */
 
VARDEF bool _dedicated_forks;
 

	
 
#endif /* VARIABLES_H */
vehicle.c
Show inline comments
 
@@ -1229,534 +1229,520 @@ Vehicle *CheckClickOnVehicle(const ViewP
 

	
 
	x = (x << vp->zoom) + vp->virtual_left;
 
	y = (y << vp->zoom) + vp->virtual_top;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type != 0 && (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) != 0 ||
 
			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 (_patches.autorenew)
 
	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);
 
	}
 
}
 

	
 
extern int32 EstimateTrainCost(const RailVehicleInfo *rvi);
 
extern int32 EstimateRoadVehCost(EngineID engine_type);
 
extern int32 EstimateShipCost(EngineID engine_type);
 
extern int32 EstimateAircraftCost(EngineID engine_type);
 
extern int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2);
 
extern int32 CmdRefitShip(int x, int y, uint32 flags, uint32 p1, uint32 p2);
 
extern int32 CmdRefitAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2);
 

	
 

	
 

	
 
/** Replaces a vehicle (used to be called autorenew).
 
 * @param x,y unused
 
 * @param p1 index of vehicle being replaced
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit  0-15) - new engine type for the vehicle (p2 & 0xFFFF)
 
 * - p2 = (bit 16-31) - money the player wants to have left after replacement counted in 100.000 (100K) (p2 >> 16)
 
 * Must be called with _current_player set to the owner of the vehicle
 
 * @param v Vehicle to replace
 
 */
 
int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
int32 ReplaceVehicle(Vehicle *v)
 
{
 
	/* makesvariables to inform about how much money the player wants to have left after replacing
 
	 and which engine to replace with out of p2.
 
	 the first 16 bit is the money. The last 5 digits (all 0) were removed when sent, so we add them again.
 
	 This way the max is 6553 millions and it is more than the 32 bit that is stored in _patches
 
	 This is a nice way to send 32 bit and only use 16 bit
 
	 the last 8 bit is the engine. The 8 bits in front of the engine is free so it have room for 16 bit engine entries */
 
	EngineID new_engine_type = (p2 & 0xFFFF);
 
	uint32 autorefit_money = (p2 >> 16) * 100000;
 
	Vehicle *v, *u, *first;
 
	Player *p = GetPlayer(v->owner);
 
	EngineID old_engine_type = v->engine_type;
 
	EngineID new_engine_type = p->engine_replacement[old_engine_type];
 
	Vehicle *u, *first;
 
	int cost, build_cost, rear_engine_cost = 0;
 
	EngineID old_engine_type;
 

	
 
	if (!IsVehicleIndex(p1)) return CMD_ERROR;
 

	
 
	v = u = GetVehicle(p1);
 
	// If replacing due to age only, use the same type :-)
 
	if (new_engine_type == INVALID_ENGINE)
 
		new_engine_type = old_engine_type;
 

	
 
	old_engine_type = v->engine_type;
 
	u = v;
 

	
 
	/* 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 (v->type != GetEngine(new_engine_type)->type) return CMD_ERROR;
 

	
 
	// check that it's the vehicle's owner that requested the replace
 
	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	// makes sure that we do not replace a plane with a helicopter or vise versa
 
	if (v->type == VEH_Aircraft) {
 
		if (HASBIT(AircraftVehInfo(old_engine_type)->subtype, 0) != HASBIT(AircraftVehInfo(new_engine_type)->subtype, 0)) return CMD_ERROR;
 
	}
 

	
 
	// makes sure that the player can actually buy the new engine. Renewing is still allowed to outdated engines
 
	if (!HASBIT(GetEngine(new_engine_type)->player_avail, v->owner) && old_engine_type != new_engine_type) return CMD_ERROR;
 

	
 
	switch (v->type) {
 
		case VEH_Train:    build_cost = EstimateTrainCost(RailVehInfo(new_engine_type)); break;
 
		case VEH_Road:     build_cost = EstimateRoadVehCost(new_engine_type);            break;
 
		case VEH_Ship:     build_cost = EstimateShipCost(new_engine_type);               break;
 
		case VEH_Aircraft: build_cost = EstimateAircraftCost(new_engine_type);           break;
 
		default: return CMD_ERROR;
 
	}
 

	
 
	/* In a rare situation, when 2 clients are connected to 1 company and have the same
 
	    settings, a vehicle can be replaced twice.. check if this is the situation here */
 
	if (old_engine_type == new_engine_type && v->age == 0) return CMD_ERROR;
 

	
 
	if ( v->type == VEH_Train ) {
 
		first = GetFirstVehicleInChain(v);
 
		u = GetLastVehicleInChain(v);
 
		if ( RailVehInfo(new_engine_type)->flags & RVI_MULTIHEAD )
 
			build_cost = build_cost >> 1;   //multiheaded engines have EstimateTrainCost() for both engines
 

	
 
		if ( old_engine_type != new_engine_type ) {
 

	
 
			// prevent that the rear engine can get replaced to something else than the front engine
 
			if ( v->u.rail.first_engine != INVALID_VEHICLE && RailVehInfo(old_engine_type)->flags & RVI_MULTIHEAD && RailVehInfo(old_engine_type)->flags ) {
 
				if ( first->engine_type != new_engine_type ) return CMD_ERROR;
 
			}
 

	
 
			// checks if the engine is the first one
 
			if ( v->u.rail.first_engine == INVALID_VEHICLE ) {
 
				if ( RailVehInfo(new_engine_type)->flags & RVI_MULTIHEAD ) {
 
					if ( u->engine_type == old_engine_type && v->next != NULL) {
 
						rear_engine_cost = build_cost - u->value;
 
					} else {
 
						rear_engine_cost = build_cost;
 
					}
 
				} else {
 
					if ( u->engine_type == old_engine_type && RailVehInfo(old_engine_type)->flags & RVI_MULTIHEAD) {
 
						if (v->next != NULL) rear_engine_cost = -(int32)u->value;
 
					}
 
				}
 
			 }
 
		}
 
	}
 

	
 
	/* Check if there is money for the upgrade.. if not, give a nice news-item
 
	    (that is needed, because this CMD is called automaticly) */
 
	if ( GetPlayer(v->owner)->money64 < (int32)(autorefit_money + build_cost + rear_engine_cost - v->value)) {
 
	if ( p->money64 < (p->engine_renew_money + build_cost + rear_engine_cost - v->value)) {
 
		if (( _local_player == v->owner ) && ( v->unitnumber != 0 )) {  //v->unitnumber = 0 for train cars
 
			int 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: message = 0; break;
 
			}
 

	
 
			AddNewsItem(message, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0);
 
		}
 

	
 
		return CMD_ERROR;
 
	}
 
	cost = build_cost - v->value + rear_engine_cost;
 

	
 

	
 
	if (flags & DC_EXEC) {
 
	if (old_engine_type != new_engine_type) {
 
		/* We do not really buy a new vehicle, we upgrade the old one */
 
		const Engine* e = GetEngine(new_engine_type);
 

	
 
		v->reliability = e->reliability;
 
		v->reliability_spd_dec = e->reliability_spd_dec;
 
		v->age = 0;
 

	
 
		v->date_of_last_service = _date;
 
		v->build_year = _cur_year;
 

	
 
		v->value = build_cost;
 

	
 
		if (v->engine_type != new_engine_type) {
 
			byte sprite = v->spritenum;
 
			byte cargo_type = v->cargo_type;
 
			v->engine_type = new_engine_type;
 
			v->max_age = e->lifelength * 366;
 

	
 
			/* Update limits of the vehicle (for when upgraded) */
 
			switch (v->type) {
 
			case VEH_Train:
 
				{
 
				const RailVehicleInfo *rvi = RailVehInfo(new_engine_type);
 
				const RailVehicleInfo *rvi2 = RailVehInfo(old_engine_type);
 
				byte capacity = rvi2->capacity;
 
				Vehicle *first = GetFirstVehicleInChain(v);
 

	
 
				//if (v->owner == _local_player) InvalidateWindowClasses(WC_TRAINS_LIST);
 
				/* rvi->image_index is the new sprite for the engine. Adding +1 makes the engine head the other way
 
				if it is a multiheaded engine (rear engine)
 
				(rvi->flags & RVI_MULTIHEAD && sprite - rvi2->image_index) is true if the engine is heading the other way, otherwise 0*/
 
				v->spritenum = rvi->image_index + (( rvi->flags & RVI_MULTIHEAD && sprite - rvi2->image_index) ? 1 : 0);
 

	
 
				// turn the last engine in a multiheaded train if needed
 
				if ( v->next == NULL && v->u.rail.first_engine != INVALID_VEHICLE && rvi->flags & RVI_MULTIHEAD && v->spritenum == rvi->image_index )
 
					v->spritenum++;
 

	
 
				v->cargo_type = rvi->cargo_type;
 
				v->cargo_cap = rvi->capacity;
 
				v->max_speed = rvi->max_speed;
 

	
 
				v->u.rail.railtype = e->railtype;
 

	
 
				// 0x0100 means that we skip the check for being stopped inside the depot
 
				// since we do not stop it for autorefitting
 
				if (v->cargo_type != cargo_type && capacity) {
 
					// BUG: somehow v->index is not transfered properly
 
					//CmdRefitRailVehicle(v->x_pos, v->y_pos, DC_EXEC, v->index , cargo_type + 0x0100 );
 
					v->cargo_type = cargo_type; // workaround, but it do not check the refit table
 
				} else {
 
					v->cargo_type = rvi->cargo_type;
 
				}
 
#if 0
 
// we disable this because they can crash the game. They will be fixed at a later date
 
				if ( rvi2->flags & RVI_MULTIHEAD && !(rvi->flags & RVI_MULTIHEAD) &&  v->index == first->index) {
 
					if (old_engine_type == u->engine_type ) {
 
						Vehicle *w;
 

	
 
						u = GetLastVehicleInChain(v);
 
						w = GetPrevVehicleInChain(u);
 
						w->next = NULL;
 
						DeleteVehicle(u);
 
					}
 
				}
 

	
 
				if ( rvi->flags & RVI_MULTIHEAD && rvi2->flags & RVI_MULTIHEAD &&  v->index == first->index ) {
 
					CmdReplaceVehicle(x, y, flags, u->index, p2);
 
				}
 

	
 
				if ( rvi->flags & RVI_MULTIHEAD && !(rvi2->flags & RVI_MULTIHEAD) &&  v->index == first->index ) {
 
					if ( old_engine_type != u->engine_type ) {
 
						Vehicle *w;
 
						if ( (w=AllocateVehicle()) != NULL ) {
 
							AddRearEngineToMultiheadedTrain(v,w, false);
 
							u->next = w;
 
						}
 
					}
 
				}
 
#endif
 

	
 
				// recalculate changed train values
 
				TrainConsistChanged(first);
 
				InvalidateWindowClasses(WC_TRAINS_LIST);
 
				UpdateTrainAcceleration(first);
 
				break;
 
				}
 
			case VEH_Road:
 
				{
 
				const RoadVehicleInfo *rvi = RoadVehInfo(new_engine_type);
 

	
 
				v->spritenum = rvi->image_index;
 
				v->cargo_type = rvi->cargo_type;
 
				v->cargo_cap = rvi->capacity;
 
				v->max_speed = rvi->max_speed;
 
				InvalidateWindowClasses(WC_ROADVEH_LIST);
 
				break;
 
				}
 
			case VEH_Ship:
 
				{
 
				const ShipVehicleInfo *svi = ShipVehInfo(new_engine_type);
 

	
 
				v->spritenum = svi->image_index;
 
				v->cargo_type = svi->cargo_type;
 
				v->cargo_cap = svi->capacity;
 
				v->max_speed = svi->max_speed;
 

	
 
				// 0x0100 means that we skip the check for being stopped inside the depot
 
				// since we do not stop it for autorefitting
 
				if (v->cargo_type != cargo_type)
 
					CmdRefitShip(v->x_pos, v->y_pos, DC_EXEC, v->index , cargo_type + 0x0100 );
 
				InvalidateWindowClasses(WC_SHIPS_LIST);
 
				break;
 
				}
 
			case VEH_Aircraft:
 
				{
 
				const AircraftVehicleInfo *avi = AircraftVehInfo(new_engine_type);
 
				Vehicle *u;
 

	
 
				v->max_speed = avi->max_speed;
 
				v->acceleration = avi->acceleration;
 
				v->spritenum = avi->image_index;
 

	
 
					if ( cargo_type == CT_PASSENGERS ) {
 
						v->cargo_cap = avi->passenger_capacity;
 
						u = v->next;
 
						u->cargo_cap = avi->mail_capacity;
 
					} else {
 
						// 0x0100 means that we skip the check for being stopped inside the hangar
 
						// since we do not stop it for autorefitting
 
						CmdRefitAircraft(v->x_pos, v->y_pos, DC_EXEC, v->index , cargo_type + 0x0100 );
 
					}
 
				InvalidateWindowClasses(WC_AIRCRAFT_LIST);
 
				break;
 
				}
 
			default: return CMD_ERROR;
 
			}
 
			// makes sure that the cargo is still valid compared to new capacity
 
			if (v->cargo_count != 0) {
 
				if ( v->cargo_type != cargo_type )
 
					v->cargo_count = 0;
 
				else if ( v->cargo_count > v->cargo_cap )
 
					v->cargo_count = v->cargo_cap;
 
			}
 
		}
 
		InvalidateWindow(WC_REPLACE_VEHICLE, v->type);
 
		ResortVehicleLists();
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	}
 

	
 
	// A replaced vehicle should be classed as new
 
	v->age = 0;
 

	
 
	InvalidateWindow(WC_REPLACE_VEHICLE, v->type);
 
	ResortVehicleLists();
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
	//needs to be down here because refitting will change SET_EXPENSES_TYPE if called
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 
	SubtractMoneyFromPlayer(cost);
 
	if (_current_player == _local_player)
 
		ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost);
 

	
 
	return cost;
 
}
 

	
 
void MaybeReplaceVehicle(Vehicle *v)
 
{
 
	uint32 new_engine_and_autoreplace_money;
 

	
 
	if (v->owner != _local_player)
 
		return;
 
	Player *p = GetPlayer(v->owner);
 
	// uncomment next line if you want to see what engine type just entered a depot
 
	//printf("engine type: %d\n", v->engine_type);
 

	
 
	// A vehicle is autorenewed when it it gets the amount of months
 
	//  give by _patches.autorenew_months away for his max age.
 
	//  Standard is -6, meaning 6 months before his max age
 
	//  It can be any value between -12 and 12.
 
	//  Here it also checks if the vehicles is listed for replacement
 
	if (!_patches.autorenew || v->age - v->max_age < (_patches.autorenew_months * 30)) {  //replace if engine is too old
 
		if (_autoreplace_array[v->engine_type] == v->engine_type && v->type != VEH_Train) //updates to a new model
 
	if (!p->engine_renew || v->age - v->max_age < (p->engine_renew_months * 30)) {  //replace if engine is too old
 
		if (p->engine_replacement[v->engine_type] == INVALID_ENGINE && v->type != VEH_Train) // updates to a new model
 
			return;
 
	}
 
	/* Now replace the vehicle */
 
	_current_player = v->owner;
 

	
 
	/* makes the variable to inform about how much money the player wants to have left after replacing
 
	 and which engine to replace with
 
	 the first 16 bit is the money. Since we know the last 5 digits is 0, they are thrown away.
 
	 This way the max is 6553 millions and it is more than the 32 bit that is stored in _patches
 
	 This is a nice way to send 32 bit and only use 16 bit
 
	 the last 8 bit is the engine. The 8 bits in front of the engine is free so it have room for 16 bit engine entries */
 
	new_engine_and_autoreplace_money = ((_patches.autorenew_money / 100000) << 16) + _autoreplace_array[v->engine_type];
 

	
 
	assert(v->type == GetEngine(_autoreplace_array[v->engine_type])->type);
 

	
 
	if ( v->type != VEH_Train ) {
 
		DoCommandP(v->tile, v->index, new_engine_and_autoreplace_money, NULL, CMD_REPLACE_VEHICLE | CMD_SHOW_NO_ERROR);
 
		ReplaceVehicle(v);
 
	} else {
 
	// checks if any of the engines in the train are either old or listed for replacement
 
		do {
 
			if ( v->engine_type != _autoreplace_array[v->engine_type] || (_patches.autorenew && (v->age - v->max_age) > (_patches.autorenew_months * 30))) {
 
				new_engine_and_autoreplace_money = (new_engine_and_autoreplace_money & 0xFFFF0000) + _autoreplace_array[v->engine_type]; // sets the new engine replacement type
 
				DoCommandP(v->tile, v->index, new_engine_and_autoreplace_money, NULL, CMD_REPLACE_VEHICLE | CMD_SHOW_NO_ERROR);
 
			if (p->engine_replacement[v->engine_type] != INVALID_ENGINE || (p->engine_renew && (v->age - v->max_age) > (p->engine_renew_months * 30))) {
 
				ReplaceVehicle(v);
 
			}
 
		} while ((v=v->next) != NULL);
 
	}
 
	_current_player = OWNER_NONE;
 
}
 

	
 
int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 veh1_veh2, uint32 mode);
 
int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2);
 
int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2);
 
int32 CmdBuildRoadVeh(int x, int y, uint32 flags, uint32 p1, uint32 p2);
 
int32 CmdBuildShip(int x, int y, uint32 flags, uint32 p1, uint32 p2);
 
int32 CmdBuildAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2);
 

	
 

	
 
typedef int32 VehBuildProc(int x, int y, uint32 flags, uint32 p1, uint32 p2);
 

	
 
static VehBuildProc * const _veh_build_proc_table[] = {
 
	CmdBuildRailVehicle,
 
	CmdBuildRoadVeh,
 
	CmdBuildShip,
 
	CmdBuildAircraft,
 
};
 

	
 
static VehicleID * _new_vehicle_id_proc_table[] = {
 
	&_new_train_id,
 
	&_new_roadveh_id,
 
	&_new_ship_id,
 
	&_new_aircraft_id,
 
};
 

	
 
/** Clone a vehicle. If it is a train, it will clone all the cars too
 
  * @param x,y unused
 
  * @param p1 the original vehicle's index
 
  * @param p2 1 = shared orders, else copied orders
 
  */
 
int32 CmdCloneVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *vfront, *v;
 
	Vehicle *wfront, *w1, *w2;
 
	int cost, total_cost;
 
	VehBuildProc *proc;
 
	VehicleID *new_id;
 
	uint refit_command = 0;
 
	byte needs_refitting = 255;
 

	
 
	if (!IsVehicleIndex(p1))
 
		return CMD_ERROR;
 
	v = GetVehicle(p1);
 
	wfront = v;
 
	w1 = v;
 
	vfront = v;
 

	
 
	if (!CheckOwnership(v->owner))
 
		return CMD_ERROR;
 

	
 
	if (v->type == VEH_Train && v->subtype != TS_Front_Engine) return CMD_ERROR;
 

	
 
	//no need to check if it is a depot since the build command do that
 
	switch (v->type) {
 
		case VEH_Train:		refit_command = CMD_REFIT_RAIL_VEHICLE; break;
 
		case VEH_Road:		break;
 
		case VEH_Ship:		refit_command = CMD_REFIT_SHIP; break;
 
		case VEH_Aircraft:	refit_command = CMD_REFIT_AIRCRAFT; break;
 
		default: return CMD_ERROR;
 
	}
 

	
 
	proc = _veh_build_proc_table[v->type - VEH_Train];
 
	new_id = _new_vehicle_id_proc_table[v->type - VEH_Train];
 
	total_cost = proc(x, y, flags, v->engine_type, 1);
 
	if (total_cost == CMD_ERROR)
 
		return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		wfront = GetVehicle(*new_id);
 
		w1 = wfront;
 
		CmdCloneOrder(x, y, flags, (v->index << 16) | w1->index, p2 & 1 ? CO_SHARE : CO_COPY);
 

	
 
		if (wfront->cargo_type != v->cargo_type) {
 
			//a refit is needed
 
			needs_refitting = v->cargo_type;
 
		}
 
	}
 
	if (v->type == VEH_Train) {
 
		// now we handle the cars
 
		v = v->next;
 
		while (v != NULL) {
 
			cost = proc(x, y, flags, v->engine_type, 1);
 
			if (cost == CMD_ERROR)
 
				return CMD_ERROR;
 
			total_cost += cost;
 

	
 
			if (flags & DC_EXEC) {
 
				// add this unit to the end of the train
 
				w2 = GetVehicle(RailVehInfo(v->engine_type)->flags & RVI_WAGON ? _new_wagon_id : _new_train_id);
 
				CmdMoveRailVehicle(x, y, flags, (w1->index << 16) | w2->index, 0);
 
				w1 = w2;
vehicle.h
Show inline comments
 
@@ -360,102 +360,103 @@ byte GetDirectionTowards(Vehicle *v, int
 
VARDEF SortStruct *_vehicle_sort;
 

	
 
extern MemoryPool _vehicle_pool;
 

	
 
/**
 
 * Get the pointer to the vehicle with index 'index'
 
 */
 
static inline Vehicle *GetVehicle(VehicleID index)
 
{
 
	return (Vehicle*)GetItemFromPool(&_vehicle_pool, index);
 
}
 

	
 
/**
 
 * Get the current size of the VehiclePool
 
 */
 
static inline uint16 GetVehiclePoolSize(void)
 
{
 
	return _vehicle_pool.total_items;
 
}
 

	
 
#define FOR_ALL_VEHICLES_FROM(v, start) for (v = GetVehicle(start); v != NULL; v = (v->index + 1 < GetVehiclePoolSize()) ? GetVehicle(v->index + 1) : NULL)
 
#define FOR_ALL_VEHICLES(v) FOR_ALL_VEHICLES_FROM(v, 0)
 

	
 
/**
 
 * Check if a Vehicle really exists.
 
 */
 
static inline bool IsValidVehicle(const Vehicle *v)
 
{
 
	return v->type != 0;
 
}
 

	
 
/**
 
 * Check if an index is a vehicle-index (so between 0 and max-vehicles)
 
 *
 
 * @return Returns true if the vehicle-id is in range
 
 */
 
static inline bool IsVehicleIndex(uint index)
 
{
 
	if (index < GetVehiclePoolSize()) return true;
 

	
 
	return false;
 
}
 

	
 
/* Returns order 'index' of a vehicle or NULL when it doesn't exists */
 
static inline Order *GetVehicleOrder(const Vehicle *v, int index)
 
{
 
	Order *order = v->orders;
 

	
 
	if (index < 0) return NULL;
 

	
 
	while (order != NULL && index-- > 0)
 
		order = order->next;
 

	
 
	return order;
 
}
 

	
 
/* Returns the last order of a vehicle, or NULL if it doesn't exists */
 
static inline Order *GetLastVehicleOrder(const Vehicle *v)
 
{
 
	Order *order = v->orders;
 

	
 
	if (order == NULL) return NULL;
 

	
 
	while (order->next != NULL)
 
		order = order->next;
 

	
 
	return order;
 
}
 

	
 
/* Get the first vehicle of a shared-list, so we only have to walk forwards */
 
static inline Vehicle *GetFirstVehicleFromSharedList(Vehicle *v)
 
{
 
	Vehicle *u = v;
 
	while (u->prev_shared != NULL)
 
		u = u->prev_shared;
 

	
 
	return u;
 
}
 

	
 
// NOSAVE: Can be regenerated by inspecting the vehicles.
 
VARDEF VehicleID _vehicle_position_hash[0x1000];
 

	
 
// NOSAVE: Return values from various commands.
 
VARDEF VehicleID _new_train_id;
 
VARDEF VehicleID _new_wagon_id;
 
VARDEF VehicleID _new_aircraft_id;
 
VARDEF VehicleID _new_ship_id;
 
VARDEF VehicleID _new_roadveh_id;
 
VARDEF uint16 _aircraft_refit_capacity;
 
VARDEF byte _cmd_build_rail_veh_score;
 
VARDEF byte _cmd_build_rail_veh_var1;
 

	
 
// for each player, for each vehicle type, keep a list of the vehicles.
 
//VARDEF Vehicle *_vehicle_arr[8][4];
 

	
 
#define INVALID_VEHICLE 0xFFFF
 
#define INVALID_ENGINE 0xFFFF
 

	
 
/* A lot of code calls for the invalidation of the status bar, which is widget 5.
 
 * Best is to have a virtual value for it when it needs to change again */
 
#define STATUS_BAR 5
 

	
 
#endif /* VEHICLE_H */
vehicle_gui.c
Show inline comments
 
@@ -651,364 +651,368 @@ static void DrawEngineArrayInReplaceWind
 

	
 
					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, SPRITE_PALETTE(PLAYER_SPRITE_COLOR(_local_player)));
 
							y2 += 14;
 
						}
 
						sel[1]--;
 
					}
 
				} while (++engine_id, ++e,--num);
 
			}
 
			break;
 
		}
 

	
 
		case VEH_Ship: {
 
			int num = NUM_SHIP_ENGINES;
 
			Engine *e = GetEngine(SHIP_ENGINES_INDEX);
 
			int engine_id = SHIP_ENGINES_INDEX;
 
			byte cargo, refittable;
 
			EngineInfo *info;
 

	
 
			if ( selected_id[0] != -1 ) {
 
				cargo = ShipVehInfo(selected_id[0])->cargo_type;
 
				refittable = ShipVehInfo(selected_id[0])->refittable;
 

	
 
				do {
 
					info = &_engine_info[engine_id];
 
					if (_player_num_engines[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, SPRITE_PALETTE(PLAYER_SPRITE_COLOR(_local_player)));
 
							SetDParam(0, _player_num_engines[engine_id]);
 
							DrawStringRightAligned(213, y+15, STR_TINY_BLACK, 0);
 
							y += 24;
 
						}
 
						sel[0]--;
 
					}
 
					if ( selected_id[0] != -1 ) {
 
						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, SPRITE_PALETTE(PLAYER_SPRITE_COLOR(_local_player)));
 
								y2 += 24;
 
							}
 
							sel[1]--;
 
						}
 
					}
 
				} while (++engine_id, ++e,--num);
 
			}
 
			break;
 
		}   //end of ship
 

	
 
		case VEH_Aircraft: {
 
			if ( selected_id[0] != -1 ) {
 
				int num = NUM_AIRCRAFT_ENGINES;
 
				Engine *e = GetEngine(AIRCRAFT_ENGINES_INDEX);
 
				int engine_id = AIRCRAFT_ENGINES_INDEX;
 
				byte subtype = AircraftVehInfo(selected_id[0])->subtype;
 
				EngineInfo *info;
 

	
 
				do {
 
					info = &_engine_info[engine_id];
 
					if (_player_num_engines[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, SPRITE_PALETTE(PLAYER_SPRITE_COLOR(_local_player)));
 
							SetDParam(0, _player_num_engines[engine_id]);
 
							DrawStringRightAligned(213, y+15, STR_TINY_BLACK, 0);
 
							y += 24;
 
						}
 
						sel[0]--;
 
					}
 
					if ( (HASBIT(subtype, 0) == HASBIT(AircraftVehInfo(engine_id)->subtype, 0))
 
						&& 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, SPRITE_PALETTE(PLAYER_SPRITE_COLOR(_local_player)));
 
							y2 += 24;
 
						}
 
						sel[1]--;
 
					}
 
				} while (++engine_id, ++e,--num);
 
			}
 
			break;
 
		}   // end of aircraft
 
	}
 

	
 
}
 
static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
 
{
 
	// 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;
 
	Player *p = GetPlayer(_local_player);
 

	
 
	switch(e->event) {
 
		case WE_PAINT:
 
			{
 
				int pos = w->vscroll.pos;
 
				int selected_id[2] = {-1,-1};
 
				int x = 1;
 
				int y = 15;
 
				int pos2 = w->vscroll2.pos;
 
				int x2 = 1 + 228;
 
				int y2 = 15;
 
				int sel[2];
 
				sel[0] = WP(w,replaceveh_d).sel_index[0];
 
				sel[1] = WP(w,replaceveh_d).sel_index[1];
 

	
 
				{
 
					uint i;
 
					const Vehicle *vehicle;
 

	
 
					for (i = 0; i < lengthof(_player_num_engines); i++) {
 
						_player_num_engines[i] = 0;
 
					}
 
					FOR_ALL_VEHICLES(vehicle) {
 
						if ( vehicle->owner == _local_player ) {
 
							if (vehicle->type == VEH_Aircraft && vehicle->subtype > 2) continue;
 

	
 
							// do not count the vehicles, that contains only 0 in all var
 
							if (vehicle->engine_type == 0 && vehicle->spritenum == 0 ) continue;
 

	
 
							if (vehicle->type != GetEngine(vehicle->engine_type)->type) continue;
 

	
 
							_player_num_engines[vehicle->engine_type]++;
 
						}
 
					}
 
				}
 

	
 
				SetupScrollStuffForReplaceWindow(w);
 

	
 
				selected_id[0] = WP(w,replaceveh_d).sel_engine[0];
 
				selected_id[1] = WP(w,replaceveh_d).sel_engine[1];
 

	
 
			// sets the selected left item to the top one if it's greater than the number of vehicles in the left side
 

	
 
				if ( WP(w,replaceveh_d).count[0] <= sel[0] ) {
 
					if (WP(w,replaceveh_d).count[0]) {
 
						sel[0] = 0;
 
						WP(w,replaceveh_d).sel_index[0] = 0;
 
						w->vscroll.pos = 0;
 
						// now we go back to set selected_id[1] properly
 
						SetWindowDirty(w);
 
						return;
 
					} else { //there are no vehicles in the left window
 
						selected_id[1] = -1;
 
					}
 
				}
 

	
 
				if ( WP(w,replaceveh_d).count[1] <= sel[1] ) {
 
					if (WP(w,replaceveh_d).count[1]) {
 
						sel[1] = 0;
 
						WP(w,replaceveh_d).sel_index[1] = 0;
 
						w->vscroll2.pos = 0;
 
						// now we go back to set selected_id[1] properly
 
						SetWindowDirty(w);
 
						return;
 
					} else { //there are no vehicles in the right window
 
						selected_id[1] = -1;
 
					}
 
				}
 

	
 
				if ( selected_id[0] == selected_id[1] || _autoreplace_array[selected_id[0]] == selected_id[1]
 
				if ( selected_id[0] == selected_id[1] || p->engine_replacement[selected_id[0]] == selected_id[1]
 
					|| selected_id[0] == -1 || selected_id[1] == -1 )
 
					SETBIT(w->disabled_state, 4);
 
				else
 
					CLRBIT(w->disabled_state, 4);
 

	
 
				if ( _autoreplace_array[selected_id[0]] == selected_id[0] || selected_id[0] == -1 )
 
				if (p->engine_replacement[selected_id[0]] == INVALID_ENGINE || selected_id[0] == -1)
 
					SETBIT(w->disabled_state, 6);
 
				else
 
					CLRBIT(w->disabled_state, 6);
 

	
 
				// now the actual drawing of the window itself takes place
 
				DrawWindowWidgets(w);
 

	
 

	
 

	
 
				// sets up the string for the vehicle that is being replaced to
 
				if ( selected_id[0] != -1 ) {
 
					if ( selected_id[0] == _autoreplace_array[selected_id[0]] )
 
					if (p->engine_replacement[selected_id[0]] == INVALID_ENGINE)
 
						SetDParam(0, STR_NOT_REPLACING);
 
					else
 
						SetDParam(0, GetCustomEngineName(_autoreplace_array[selected_id[0]]));
 
						SetDParam(0, GetCustomEngineName(p->engine_replacement[selected_id[0]]));
 
				} else {
 
					SetDParam(0, STR_NOT_REPLACING_VEHICLE_SELECTED);
 
				}
 

	
 

	
 
				DrawString(145, (w->resize.step_height == 24 ? 77 : 87 ) + ( w->resize.step_height * w->vscroll.cap), 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 */
 
				switch (WP(w,replaceveh_d).vehicletype) {
 
					case VEH_Train: {
 
						byte i = 0;
 
						int offset = 0;
 

	
 
						for ( i = 0 ; i < 2 ; i++) {
 
							if ( i )
 
							offset = 228;
 
							if (selected_id[i] != -1) {
 
								if (!(RailVehInfo(selected_id[i])->flags & RVI_WAGON)) {
 
									/* it's an engine */
 
									DrawTrainEnginePurchaseInfo(2 + offset, 15 + (14 * w->vscroll.cap), selected_id[i]);
 
								} else {
 
									/* it's a wagon. Train cars are not replaced with the current GUI, but this code is ready for newgrf if anybody adds that*/
 
									DrawTrainWagonPurchaseInfo(2 + offset, 15 + (14 * w->vscroll.cap), selected_id[i]);
 
								}
 
							}
 
						}
 
						break;
 
					}   //end if case  VEH_Train
 

	
 
					case VEH_Road: {
 
						if (selected_id[0] != -1) {
 
							DrawRoadVehPurchaseInfo(2, 15 + (14 * w->vscroll.cap), selected_id[0]);
 
							if (selected_id[1] != -1) {
 
								DrawRoadVehPurchaseInfo(2 + 228, 15 + (14 * w->vscroll.cap), selected_id[1]);
 
							}
 
						}
 
						break;
 
					}   // end of VEH_Road
 

	
 
					case VEH_Ship: {
 
						if (selected_id[0] != -1) {
 
							DrawShipPurchaseInfo(2, 15 + (24 * w->vscroll.cap), selected_id[0]);
 
							if (selected_id[1] != -1) {
 
								DrawShipPurchaseInfo(2 + 228, 15 + (24 * w->vscroll.cap), selected_id[1]);
 
							}
 
						}
 
						break;
 
					}   // end of VEH_Ship
 

	
 
					case VEH_Aircraft: {
 
						if (selected_id[0] != -1) {
 
							DrawAircraftPurchaseInfo(2, 15 + (24 * w->vscroll.cap), selected_id[0]);
 
							if (selected_id[1] != -1) {
 
								DrawAircraftPurchaseInfo(2 + 228, 15 + (24 * w->vscroll.cap), selected_id[1]);
 
							}
 
						}
 
						break;
 
					}   // end of VEH_Aircraft
 
				}
 
			} break;   // end of paint
 

	
 
		case WE_CLICK: {
 
			switch(e->click.widget) {
 
				case 14: case 15: { /* Select sorting criteria dropdown menu */
 
					ShowDropDownMenu(w, _rail_types_list, _railtype_selected_in_replace_gui, 15, ~GetPlayer(_local_player)->avail_railtypes, 1);
 
					break;
 
				}
 
				case 4: {
 
					_autoreplace_array[WP(w,replaceveh_d).sel_engine[0]] = WP(w,replaceveh_d).sel_engine[1];
 
				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_REPLACE_VEHICLE);
 
					SetWindowDirty(w);
 
					break;
 
				}
 

	
 
				case 6: {
 
					_autoreplace_array[WP(w,replaceveh_d).sel_engine[0]] = WP(w,replaceveh_d).sel_engine[0];
 
				case 6: { /* Stop replacing */
 
					EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
 
					DoCommandP(0, 3, veh_from + (INVALID_ENGINE << 16), NULL, CMD_REPLACE_VEHICLE);
 
					SetWindowDirty(w);
 
					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;
 
				case 9: {
 
					uint i = (e->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->dropdown.index;
 
			SetWindowDirty(w);
 
		} break;
 

	
 
		case WE_RESIZE: {
 
			w->vscroll.cap  += e->sizing.diff.y / (int)w->resize.step_height;
 
			w->vscroll2.cap += e->sizing.diff.y / (int)w->resize.step_height;
 

	
 
			w->widget[7].unkA = (w->vscroll.cap  << 8) + 1;
 
			w->widget[9].unkA = (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, 0x0,            STR_STICKY_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,     0,   227,   126,   197, 0x0,            STR_NULL},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   138,   210,   221, STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,   139,   316,   198,   209, 0x0,            STR_REPLACE_HELP_REPLACE_INFO_TAB},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   306,   443,   210,   221, 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, 0x0,            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, 0x0,            STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,     RESIZE_TB,    14,   228,   455,   126,   197, 0x0,            STR_NULL},
 
// train specific stuff
 
{      WWT_PANEL,     RESIZE_TB,    14,     0,   138,   198,   209, 0x0,            STR_NULL},
 
{      WWT_PANEL,     RESIZE_TB,    14,   139,   153,   210,   221, 0x0,            STR_NULL},
 
{      WWT_PANEL,     RESIZE_TB,    14,   154,   277,   210,   221, 0x0,            STR_REPLACE_HELP_RAILTYPE},
 
{   WWT_CLOSEBOX,     RESIZE_TB,    14,   278,   289,   210,   221, STR_0225,       STR_REPLACE_HELP_RAILTYPE},
 
{      WWT_PANEL,     RESIZE_TB,    14,   290,   305,   210,   221, 0x0,            STR_NULL},
 
{      WWT_PANEL,     RESIZE_TB,    14,   317,   455,   198,   209, 0x0,            STR_NULL},
 
// end of train specific stuff
 
{  WWT_RESIZEBOX,     RESIZE_TB,    14,   444,   455,   210,   221, 0x0,            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, 0x0,            STR_STICKY_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,     0,   227,   126,   187, 0x0,            STR_NULL},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   138,   188,   199, STR_REPLACE_VEHICLES_START,  STR_REPLACE_HELP_START_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,   139,   305,   188,   199, 0x0,            STR_REPLACE_HELP_REPLACE_INFO_TAB},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   306,   443,   188,   199, 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, 0x0,            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, 0x0,            STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,     RESIZE_TB,    14,   228,   455,   126,   187, 0x0,            STR_NULL},
 
{  WWT_RESIZEBOX,     RESIZE_TB,    14,   444,   455,   188,   199, 0x0,            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, 0x0,            STR_STICKY_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,     0,   227,   110,   161, 0x0,            STR_NULL},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,     0,   138,   162,   173, STR_REPLACE_VEHICLES_START,  STR_REPLACE_HELP_START_BUTTON},
 
{      WWT_PANEL,     RESIZE_TB,    14,   139,   305,   162,   173, 0x0,            STR_REPLACE_HELP_REPLACE_INFO_TAB},
 
{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   306,   443,   162,   173, 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, 0x0,            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, 0x0,            STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_PANEL,     RESIZE_TB,    14,   228,   455,   110,   161, 0x0,            STR_NULL},
 
{  WWT_RESIZEBOX,     RESIZE_TB,    14,   444,   455,   162,   173, 0x0,            STR_RESIZE_BUTTON},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _replace_rail_vehicle_desc = {
0 comments (0 inline, 0 general)