Changeset - r4000:702cb45b8eab
[Not reviewed]
master
0 44 0
tron - 18 years ago 2006-06-10 08:37:41
tron@openttd.org
(svn r5210) Many small changes which piled up: const, unsigned, variable scope, CSE for readability, DeMorgan, if cascades -> switch, whitespace, parentheses, bracing, misc.
30 files changed:
0 comments (0 inline, 0 general)
ai/ai.c
Show inline comments
 
@@ -52,211 +52,213 @@ static void AI_PutCommandInQueue(PlayerI
 
	if (_ai_player[player].queue_tail == NULL) {
 
		/* There is no item in the queue yet, create the queue */
 
		_ai_player[player].queue = malloc(sizeof(AICommand));
 
		_ai_player[player].queue_tail = _ai_player[player].queue;
 
	} else {
 
		/* Add an item at the end */
 
		_ai_player[player].queue_tail->next = malloc(sizeof(AICommand));
 
		_ai_player[player].queue_tail = _ai_player[player].queue_tail->next;
 
	}
 

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

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

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

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

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

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

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

	
 
	/* Restore _cmd_text */
 
	_cmd_text = tmp_cmdtext;
 

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

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

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

	
 
	/* Set _local_player back */
 
	_local_player = old_lp;
 

	
 
	return res;
 
}
 

	
 

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

	
 

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

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

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

	
 

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

	
 
	/* Don't do anything if we are a network-client
 
	 *  (too bad when a client joins, he thinks the AIs are real, so it wants to control
 
	 *   them.. this avoids that, while loading a network game in singleplayer, does make
 
	 *   the AIs to continue ;))
 
	 */
 
	if (_networking && !_network_server && !_ai.network_client)
 
		return;
 
	if (_networking && !_network_server && !_ai.network_client) return;
 

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

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

	
 
	/* Check for AI-client (so joining a network with an AI) */
 
	if (_ai.network_client && _ai_player[_ai.network_playas].active) {
 
		/* Run the script */
 
		AI_DequeueCommands(_ai.network_playas);
 
		AI_RunTick(_ai.network_playas);
 
	} else if (!_networking || _network_server) {
 
		/* Check if we want to run AIs (server or SP only) */
 
		Player *p;
 
		const Player* p;
 

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

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

	
 
	_current_player = OWNER_NONE;
 
}
 

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

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

	
 
/**
 
 * This AI player died. Give it some chance to make a final puf.
 
 */
 
void AI_PlayerDied(PlayerID player)
 
{
 
	if (_ai.network_client && _ai.network_playas == player)
 
	if (_ai.network_client && _ai.network_playas == player) {
 
		_ai.network_playas = OWNER_SPECTATOR;
 
	}
 

	
 
	/* Called if this AI died */
 
	_ai_player[player].active = false;
 
}
 

	
 
/**
 
 * Initialize some AI-related stuff.
 
 */
 
void AI_Initialize(void)
 
{
 
	bool ai_network_client = _ai.network_client;
 

	
 
	/* First, make sure all AIs are DEAD! */
 
	AI_Uninitialize();
 

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

	
 
	_ai.network_client = ai_network_client;
 
	_ai.network_playas = OWNER_SPECTATOR;
 
	_ai.enabled = true;
 
}
 

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

	
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active && p->is_ai) AI_PlayerDied(p->index);
 
	}
 
}
ai/default/default.c
Show inline comments
 
@@ -305,99 +305,99 @@ static void AiHandleReplaceTrain(Player 
 
	BackuppedOrders orderbak[1];
 
	EngineID veh;
 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	
 
@@ -1563,98 +1563,97 @@ static int32 AiDoBuildDefaultRailTrack(T
 
			}
 

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

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

	
 
			j = p->attr;
 
			k = 0;
 

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

	
 
			/* signals too? */
 
			if (j & 3) {
 
				/* XXX - we need to check manually whether we can build a signal if DC_EXEC is
 
				   not set because the rail has not actually been built */
 
				if (!IsTileType(c, MP_RAILWAY)) return CMD_ERROR;
 

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

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

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

	
 
		p++;
 
	}
 

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

	
 
	return total_cost;
 
}
 

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

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

	
 
	return -1;
 
}
 

	
 
static const byte _terraform_up_flags[] = {
 
	14, 13, 12, 11,
 
	10, 9, 8, 7,
 
	6, 5, 4, 3,
 
@@ -1882,291 +1881,289 @@ static void AiBanTile(Player* p, TileInd
 
	for (i = lengthof(p->ai.banned_tiles) - 1; i != 0; i--) {
 
		p->ai.banned_tiles[i] = p->ai.banned_tiles[i - 1];
 
		p->ai.banned_val[i] = p->ai.banned_val[i - 1];
 
	}
 

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

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

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

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

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

	
 
	return better;
 
}
 

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

	
 
	int dir2 = p[0] & 3;
 

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

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

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

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

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

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

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

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

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

	
 

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

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

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

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

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

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

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

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

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

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

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

	
 
	arf->depth--;
 
}
 

	
 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	
 
	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
		if (IsTunnel(tile)) {
 
			// Clear the tunnel and continue at the other side of it.
 
			if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR)))
 
				return false;
 
			p->ai.cur_tile_a = TILE_MASK(_build_tunnel_endtile - TileOffsByDir(p->ai.cur_dir_a));
 
			return true;
 
		}
 

	
 
		if (IsBridgeRamp(tile)) {
 
			// Check if the bridge points in the right direction.
 
			// This is not really needed the first place AiRemoveTileAndGoForward is called.
 
@@ -2303,184 +2300,180 @@ static void AiStateBuildRail(Player *p)
 
	p->ai.start_dir_a = dir;
 
	p->ai.cur_dir_a = dir;
 
	DoCommand(TILE_MASK(tile + TileOffsByDir(dir)), 0, (dir&1)?1:0, DC_EXEC, CMD_REMOVE_SINGLE_RAIL);
 

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

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

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

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

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

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

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

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

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

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

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

	
 
	return best_veh_index;
 
}
 

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

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

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

	
 

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

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

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

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

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

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

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

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

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

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

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

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

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

	
 
static void AiStateDeleteRailBlocks(Player *p)
 
{
 
	int num;
 
	AiBuildRec *aib;
 
	const AiDefaultBlockData *b;
 

	
 
	num = p->ai.num_build_rec;
 
	aib = &p->ai.src;
 
	do {
 
		if (aib->cur_building_rule != 255) {
 
			b = _default_rail_track_data[aib->cur_building_rule]->data;
 
			while (b->mode != 4) {
 
				DoCommand(TILE_ADD(aib->use_tile, ToTileIndexDiff(b->tileoffs)), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				b++;
 
			}
 
		}
 
@@ -2570,101 +2563,98 @@ static int32 AiDoBuildDefaultRoadBlock(T
 
		}
 

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

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

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

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

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

	
 
		}
 
	}
 

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

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

	
 
// Make sure the blocks are not too close to each other
 
static bool AiCheckBlockDistances(Player *p, TileIndex tile)
 
{
 
	AiBuildRec *aib;
 
	int num;
 

	
 
	num = p->ai.num_build_rec;
 
	aib = &p->ai.src;
 
	const AiBuildRec* aib = &p->ai.src;
 
	uint num = p->ai.num_build_rec;
 

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

	
 
	return true;
 
}
 

	
 

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

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

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

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

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

	
 
			if (rule == -1) {
 
				// cannot build, terraform after a while
 
				if (p->ai.state_counter >= 600) {
 
					AiDoTerraformLand(aib->use_tile, Random()&3, 3, (int8)p->ai.state_mode);
 
				}
 
@@ -2790,149 +2780,147 @@ static const uint16 _ai_road_table_and[4
 
	0x2A00,
 
};
 

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

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

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

	
 
	are.best_dist = (uint)-1;
 

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

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

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

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

	
 

	
 
static bool AiBuildRoadHelper(TileIndex tile, int flags, int type)
 
{
 
	static const byte _road_bits[] = {
 
		8+2,
 
		1+4,
 
		1+8,
 
		4+2,
 
		1+2,
 
		8+4,
 
	};
 
	return !CmdFailed(DoCommand(tile, _road_bits[type], 0, flags, CMD_BUILD_ROAD));
 
}
 

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

	
 
	int dir2 = p[0] & 3;
 

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

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

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

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

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

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

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

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

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

	
 

	
 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	
 
		// road
 
		if (!AiBuildRoadHelper(tile, DC_EXEC | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, arf.best_ptr[0]))
 
			goto do_some_terraform;
 

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

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

	
 

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

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

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

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

	
 

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

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

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

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

	
 
		return;
 
	}
 

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

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

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

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

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

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

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

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

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

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

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

	
 
	loco_id = _new_vehicle_id;
 

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

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

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

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

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

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

	
 
static void AiStateDeleteRoadBlocks(Player *p)
 
{
 
	int num;
 
	AiBuildRec *aib;
 
	const AiDefaultBlockData *b;
 

	
 
	num = p->ai.num_build_rec;
 
	aib = &p->ai.src;
 
	do {
 
		if (aib->cur_building_rule != 255) {
 
			b = _road_default_block_data[aib->cur_building_rule]->data;
 
			while (b->mode != 4) {
 
				if (b->mode <= 1) {
 
					DoCommand(TILE_ADD(aib->use_tile, ToTileIndexDiff(b->tileoffs)), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				}
 
				b++;
 
			}
 
		}
 
	} while (++aib,--num);
 

	
 
	p->ai.state = AIS_0;
 
}
 

	
 

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

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

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

	
 
		FOR_ALL_STATIONS(st) {
 
			// Dismiss ghost stations.
 
			if (st->xy == 0) continue;
 

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

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

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

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

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

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

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

	
 
			/* XXX: This part is utterly broken - rule should
 
			 * contain number of the rule appropriate for the
 
			 * airport type (country, town, ...), see
 
			 * _airport_default_block_data (rule is just an index
 
@@ -3404,97 +3392,97 @@ static void AiStateBuildDefaultAirportBl
 
				if (p->ai.state_counter >= 600) {
 
					AiDoTerraformLand(aib->use_tile, Random()&3, 3, (int8)p->ai.state_mode);
 
				}
 
				// also try the other terraform direction
 
				if (++p->ai.state_counter >= 1000) {
 
					p->ai.state_counter = 0;
 
					p->ai.state_mode = -p->ai.state_mode;
 
				}
 
			} else if (CheckPlayerHasMoney(cost) && AiCheckBlockDistances(p,aib->use_tile)) {
 
				// player has money, build it.
 
				int32 r;
 

	
 
				aib->cur_building_rule = rule;
 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	
 
static void AiStateBuildDefaultShipBlocks(Player *p)
 
{
 
@@ -3619,117 +3607,110 @@ pos_0:
 
			}
 

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

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

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

	
 
			DiagDirection dir = GetRailDepotDirection(tile);
 

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

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

	
 
		if (IsLevelCrossing(tile)) goto is_rail_crossing;
 

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

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

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

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

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

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

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

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

	
 
			dir = GetRoadDepotDirection(tile);
 

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

	
 
		rails = 0;
 

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

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

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

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

	
 
		// Remove player stuff in that tile
 
		AiRemovePlayerRailOrRoad(p, tile);
 
		if (p->ai.state != AIS_REMOVE_TRACK) return;
 
@@ -3836,98 +3817,97 @@ static void AiHandleTakeover(Player *p)
 
		}
 
	}
 
}
 

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

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

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

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

	
 

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

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

	
 
	if (IS_HUMAN_PLAYER(_current_player))
 
		return;
 
	if (IS_HUMAN_PLAYER(_current_player)) return;
 

	
 
	AiAdjustLoan(p);
 
	AiBuildCompanyHQ(p);
 

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

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

	
 
	_ai_actions[p->ai.state](p);
ai/trolly/build.c
Show inline comments
 
@@ -23,152 +23,156 @@ bool AiNew_Build_CompanyHQ(Player *p, Ti
 
{
 
	if (CmdFailed(AI_DoCommand(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ)))
 
		return false;
 
	AI_DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ);
 
	return true;
 
}
 

	
 

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

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

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

	
 

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

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

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

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

	
 

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

	
 

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

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

	
 
#define TEST_STATION_NO_DIR 0xFF
 

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

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

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

	
 

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

	
 

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

	
 

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

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

	
 
	return AYSTAR_DONE;
 
}
 

	
 

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

	
 

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

	
 

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

	
 

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

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

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

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

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

	
 
	return result;
 
}
 

	
 

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

	
 
	aystar->clear(aystar);
 

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

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

	
 
	// Now we add all the starting tiles
 
	for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) {
 
		for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) {
 
			if (!(IsTileType(TileXY(x, y), MP_CLEAR) || IsTileType(TileXY(x, y), MP_TREES))) continue;
 
			if (!TestCanBuildStationHere(TileXY(x, y), TEST_STATION_NO_DIR)) continue;
 
			start_node.node.tile = TileXY(x, y);
 
			TileIndex tile = TileXY(x, y);
 

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

	
 

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

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

	
 

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

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

	
 

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

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

	
 
	aystar->num_neighbours = 0;
 
@@ -402,101 +406,104 @@ static int32 AyStar_AiPathFinder_Calcula
 
			res -= AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS;
 
		}
 
	}
 

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

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

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

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

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

	
 
	return (res < 0) ? 0 : res;
 
}
ai/trolly/trolly.c
Show inline comments
 
@@ -83,240 +83,243 @@ static void AiNew_State_Nothing(Player *
 
	assert(p->ainew.state == AI_STATE_NOTHING);
 
	// If we are done idling, start over again
 
	if (p->ainew.idle == 0) p->ainew.idle = AI_RandomRange(DAY_TICKS * 2) + DAY_TICKS;
 
	if (--p->ainew.idle == 0) {
 
		// We are done idling.. what you say? Let's do something!
 
		// I mean.. the next tick ;)
 
		p->ainew.state = AI_STATE_WAKE_UP;
 
	}
 
}
 

	
 

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

	
 
	money = p->player_money - AI_MINIMUM_MONEY;
 

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

	
 
		p->ainew.counter = 0;
 
	}
 

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

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

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

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

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

	
 
	p->ainew.state = AI_STATE_NOTHING;
 
}
 

	
 

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

	
 

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

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

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

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

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

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

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

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

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

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

	
 
		// Check if we have build a station in this town the last 6 months
 
		//  else we don't do it. This is done, because stat updates can be slow
 
		//  and sometimes it takes up to 4 months before the stats are corectly.
 
		FOR_ALL_STATIONS(st) {
 
			// Is it an active station
 
			if (st->xy == 0) continue;
 

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

	
 

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

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

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

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

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

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

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

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

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

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

	
 
			return;
 
		}
 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	
 
					return;
 
@@ -627,204 +633,202 @@ static void AiNew_State_FindStation(Play
 
							}
 
						}
 
						count++;
 
					}
 
				}
 
			}
 
		}
 
	}
 
	// We are going to add a new station...
 
	if (new_tile == 0) count++;
 
	// No more than 2 stations allowed in a city
 
	//  This is because only the best 2 stations of one cargo do get any cargo
 
	if (count > 2) {
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 

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

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

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

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

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

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

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

	
 

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

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

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

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

	
 
			p->ainew.path_info.end_tile_tl = i->xy - TileDiffXY(1, 1);
 
			p->ainew.path_info.end_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1);
 
			p->ainew.path_info.end_direction = p->ainew.to_direction;
 
		} else {
 
			p->ainew.path_info.end_tile_tl = p->ainew.to_tile;
 
			p->ainew.path_info.end_tile_br = p->ainew.to_tile;
 
			p->ainew.path_info.end_direction = p->ainew.to_direction;
 
		}
 

	
 
		if (p->ainew.tbt == AI_TRAIN)
 
			p->ainew.path_info.rail_or_road = true;
 
		else
 
			p->ainew.path_info.rail_or_road = false;
 
		p->ainew.path_info.rail_or_road = (p->ainew.tbt == AI_TRAIN);
 

	
 
		// First, clean the pathfinder with our new begin and endpoints
 
		clean_AyStar_AiPathFinder(p->ainew.pathfinder, &p->ainew.path_info);
 

	
 
		p->ainew.temp = 0;
 
	}
 

	
 
	// Start the pathfinder
 
	r = p->ainew.pathfinder->main(p->ainew.pathfinder);
 
	// If it return: no match, stop it...
 
	if (r == AYSTAR_NO_PATH) {
 
	switch (r) {
 
		case AYSTAR_NO_PATH:
 
		DEBUG(ai,1)("[AiNew] PathFinder found no route!");
 
		// Start all over again...
 
			// Start all over again
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 
	if (r == AYSTAR_FOUND_END_NODE) {
 
		// We found the end-point
 
			break;
 

	
 
		case AYSTAR_FOUND_END_NODE: // We found the end-point
 
		p->ainew.temp = -1;
 
		p->ainew.state = AI_STATE_FIND_DEPOT;
 
		return;
 
			break;
 

	
 
		// In any other case, we are still busy finding the route
 
		default: break;
 
	}
 
	// In any other case, we are still busy finding the route...
 
}
 

	
 

	
 
// This function tries to locate a good place for a depot!
 
static void AiNew_State_FindDepot(Player *p)
 
{
 
	// To place the depot, we walk through the route, and if we find a lovely spot (MP_CLEAR, MP_TREES), we place it there..
 
	// Simple, easy, works!
 
	// To make the depot stand in the middle of the route, we start from the center..
 
	// But first we walk through the route see if we can find a depot that is ours
 
	//  this keeps things nice ;)
 
	int g, i, r;
 
	DiagDirection j;
 
	TileIndex tile;
 
	assert(p->ainew.state == AI_STATE_FIND_DEPOT);
 

	
 
	p->ainew.depot_tile = 0;
 

	
 
	for (i=2;i<p->ainew.path_info.route_length-2;i++) {
 
		tile = p->ainew.path_info.route[i];
 
		for (j = 0; j < 4; j++) {
 
			TileIndex t = tile + TileOffsByDir(j);
 

	
 
			if (IsTileType(t, MP_STREET) &&
 
					GetRoadTileType(t) == ROAD_TILE_DEPOT &&
 
					IsTileOwner(t, _current_player) &&
 
					GetRoadDepotDirection(t) == ReverseDiagDir(j)) {
 
				p->ainew.depot_tile = t;
 
				p->ainew.depot_direction = ReverseDiagDir(j);
 
				p->ainew.state = AI_STATE_VERIFY_ROUTE;
 
				return;
 
			}
 
		}
 
	}
 

	
 
	// This routine let depot finding start in the middle, and work his way to the stations
 
	// It makes depot placing nicer :)
 
	i = p->ainew.path_info.route_length / 2;
 
	g = 1;
 
	while (i > 1 && i < p->ainew.path_info.route_length - 2) {
 
		i += g;
 
		g *= -1;
 
		(g < 0?g--:g++);
 

	
 
		if (p->ainew.path_info.route_extra[i] != 0 || p->ainew.path_info.route_extra[i+1] != 0) {
 
			// Bridge or tunnel.. we can't place a depot there
 
			continue;
 
		}
 
@@ -855,100 +859,101 @@ static void AiNew_State_FindDepot(Player
 
				p->ainew.new_cost += r;
 
				p->ainew.depot_tile = t;
 
				p->ainew.depot_direction = ReverseDiagDir(j); // Reverse direction
 
				p->ainew.state = AI_STATE_VERIFY_ROUTE;
 
				return;
 
			}
 
		}
 
	}
 

	
 
	// Failed to find a depot?
 
	p->ainew.state = AI_STATE_NOTHING;
 
}
 

	
 

	
 
// This function calculates how many vehicles there are needed on this
 
//  traject.
 
// It works pretty simple: get the length, see how much we move around
 
//  and hussle that, and you know how many vehicles there are needed.
 
// It returns the cost for the vehicles
 
static int AiNew_HowManyVehicles(Player *p)
 
{
 
	if (p->ainew.tbt == AI_BUS) {
 
		// For bus-routes we look at the time before we are back in the station
 
		EngineID i;
 
		int length, tiles_a_day;
 
		int amount;
 
		i = AiNew_PickVehicle(p);
 
		if (i == INVALID_ENGINE) return 0;
 
		// Passenger run.. how long is the route?
 
		length = p->ainew.path_info.route_length;
 
		// Calculating tiles a day a vehicle moves is not easy.. this is how it must be done!
 
		tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16;
 
		// We want a vehicle in a station once a month at least, so, calculate it!
 
		// (the * 2 is because we have 2 stations ;))
 
		amount = length * 2 * 2 / tiles_a_day / 30;
 
		if (amount == 0) amount = 1;
 
		return amount;
 
	} else if (p->ainew.tbt == AI_TRUCK) {
 
		// For truck-routes we look at the cargo
 
		EngineID i;
 
		int length, amount, tiles_a_day;
 
		int max_cargo;
 
		i = AiNew_PickVehicle(p);
 
		if (i == INVALID_ENGINE) return 0;
 
		// Passenger run.. how long is the route?
 
		length = p->ainew.path_info.route_length;
 
		// Calculating tiles a day a vehicle moves is not easy.. this is how it must be done!
 
		tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16;
 
		if (p->ainew.from_deliver)
 
		if (p->ainew.from_deliver) {
 
			max_cargo = GetIndustry(p->ainew.from_ic)->total_production[0];
 
		else
 
		} else {
 
			max_cargo = GetIndustry(p->ainew.to_ic)->total_production[0];
 
		}
 

	
 
		// This is because moving 60% is more than we can dream of!
 
		max_cargo *= 0.6;
 
		// We want all the cargo to be gone in a month.. so, we know the cargo it delivers
 
		//  we know what the vehicle takes with him, and we know the time it takes him
 
		//  to get back here.. now let's do some math!
 
		amount = 2 * length * max_cargo / tiles_a_day / 30 / RoadVehInfo(i)->capacity;
 
		amount += 1;
 
		return amount;
 
	} else {
 
		// Currently not supported
 
		return 0;
 
	}
 
}
 

	
 

	
 
// This function checks:
 
//   - If the route went okay
 
//   - Calculates the amount of money needed to build the route
 
//   - Calculates how much vehicles needed for the route
 
static void AiNew_State_VerifyRoute(Player *p)
 
{
 
	int res, i;
 
	assert(p->ainew.state == AI_STATE_VERIFY_ROUTE);
 

	
 
	// Let's calculate the cost of the path..
 
	//  new_cost already contains the cost of the stations
 
	p->ainew.path_info.position = -1;
 

	
 
	do {
 
		p->ainew.path_info.position++;
 
		p->ainew.new_cost += AiNew_Build_RoutePart(p, &p->ainew.path_info, DC_QUERY_COST);
 
	} while (p->ainew.path_info.position != -2);
 

	
 
	// Now we know the price of build station + path. Now check how many vehicles
 
	//  we need and what the price for that will be
 
	res = AiNew_HowManyVehicles(p);
 
	// If res == 0, no vehicle was found, or an other problem did occour
 
	if (res == 0) {
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 
	p->ainew.amount_veh = res;
 
	p->ainew.cur_veh = 0;
 

	
 
	// Check how much it it going to cost us..
 
	for (i=0;i<res;i++) {
 
		p->ainew.new_cost += AiNew_Build_Vehicle(p, 0, DC_QUERY_COST);
 
@@ -1064,97 +1069,97 @@ static void AiNew_State_BuildPath(Player
 
						if (!CmdFailed(ret)) {
 
							if (IsTileType(tile + dir1 + dir1, MP_CLEAR) || IsTileType(tile + dir1 + dir1, MP_TREES))
 
								AI_DoCommand(tile+dir1+dir1, AiNew_GetRoadDirection(tile+dir1, tile+dir1+dir1, tile+dir1+dir1+dir1), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						}
 
					}
 
				}
 

	
 
				ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir2)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
				if (!CmdFailed(ret)) {
 
					dir2 = TileOffsByDir(dir2);
 
					if (IsTileType(tile + dir2, MP_CLEAR) || IsTileType(tile + dir2, MP_TREES)) {
 
						ret = AI_DoCommand(tile+dir2, AiNew_GetRoadDirection(tile, tile+dir2, tile+dir2+dir2), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						if (!CmdFailed(ret)) {
 
							if (IsTileType(tile + dir2 + dir2, MP_CLEAR) || IsTileType(tile + dir2 + dir2, MP_TREES))
 
								AI_DoCommand(tile+dir2+dir2, AiNew_GetRoadDirection(tile+dir2, tile+dir2+dir2, tile+dir2+dir2+dir2), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						}
 
					}
 
				}
 

	
 
				ret = AI_DoCommand(tile, DiagDirToRoadBits(dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
				if (!CmdFailed(ret)) {
 
					dir3 = TileOffsByDir(dir3);
 
					if (IsTileType(tile + dir3, MP_CLEAR) || IsTileType(tile + dir3, MP_TREES)) {
 
						ret = AI_DoCommand(tile+dir3, AiNew_GetRoadDirection(tile, tile+dir3, tile+dir3+dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						if (!CmdFailed(ret)) {
 
							if (IsTileType(tile + dir3 + dir3, MP_CLEAR) || IsTileType(tile + dir3 + dir3, MP_TREES))
 
								AI_DoCommand(tile+dir3+dir3, AiNew_GetRoadDirection(tile+dir3, tile+dir3+dir3, tile+dir3+dir3+dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
 
						}
 
					}
 
				}
 
			}
 
		}
 

	
 

	
 
		DEBUG(ai,1)("[AiNew] Done building the path (cost: %d)", p->ainew.new_cost);
 
		p->ainew.state = AI_STATE_BUILD_DEPOT;
 
	}
 
}
 

	
 

	
 
// Builds the depot
 
static void AiNew_State_BuildDepot(Player *p)
 
{
 
	int res = 0;
 
	assert(p->ainew.state == AI_STATE_BUILD_DEPOT);
 

	
 
	if (IsTileType(p->ainew.depot_tile, MP_STREET) && GetRoadTileType(p->ainew.depot_tile) == ROAD_TILE_DEPOT) {
 
		if (IsTileOwner(p->ainew.depot_tile, _current_player)) {
 
			// The depot is already builded!
 
			// The depot is already built
 
			p->ainew.state = AI_STATE_BUILD_VEHICLE;
 
			return;
 
		} else {
 
			// There is a depot, but not of our team! :(
 
			p->ainew.state = AI_STATE_NOTHING;
 
			return;
 
		}
 
	}
 

	
 
	// There is a bus on the tile we want to build road on... idle till he is gone! (BAD PERSON! :p)
 
	if (!EnsureNoVehicle(p->ainew.depot_tile + TileOffsByDir(p->ainew.depot_direction)))
 
		return;
 

	
 
	res = AiNew_Build_Depot(p, p->ainew.depot_tile, p->ainew.depot_direction, DC_EXEC);
 
	if (CmdFailed(res)) {
 
		DEBUG(ai,0)("[AiNew - BuildDepot] Strange but true... depot can not be build!");
 
		p->ainew.state = AI_STATE_NOTHING;
 
		return;
 
	}
 

	
 
	p->ainew.state = AI_STATE_BUILD_VEHICLE;
 
	p->ainew.idle = 10;
 
	p->ainew.veh_main_id = INVALID_VEHICLE;
 
}
 

	
 

	
 
// Build vehicles
 
static void AiNew_State_BuildVehicle(Player *p)
 
{
 
	int res;
 
	assert(p->ainew.state == AI_STATE_BUILD_VEHICLE);
 

	
 
	// Check if we need to build a vehicle
 
	if (p->ainew.amount_veh == 0) {
 
		// Nope, we are done!
 
		// This means: we are all done! The route is open.. go back to NOTHING
 
		//  He will idle some time and it will all start over again.. :)
 
		p->ainew.state = AI_STATE_ACTION_DONE;
 
		return;
 
	}
 
	if (--p->ainew.idle != 0) return;
 
	// It is realistic that the AI can only build 1 vehicle a day..
 
	// This makes sure of that!
 
	p->ainew.idle = AI_BUILD_VEHICLE_TIME_BETWEEN;
 

	
 
	// Build the vehicle
 
	res = AiNew_Build_Vehicle(p, p->ainew.depot_tile, DC_EXEC);
 
	if (CmdFailed(res)) {
aystar.c
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file has the core function for AyStar
 
 *  AyStar is a fast pathfinding routine and is used for things like
 
 *  AI_pathfinding and Train_pathfinding.
 
 *  For more information about AyStar (A* Algorithm), you can look at
 
 *    http://en.wikipedia.org/wiki/A-star_search_algorithm
 
 */
 

	
 
/*
 
 * Friendly reminder:
 
 *  Call (AyStar).free() when you are done with Aystar. It reserves a lot of memory
 
 *  And when not free'd, it can cause system-crashes.
 
 * Also remember that when you stop an algorithm before it is finished, your
 
 * should call clear() yourself!
 
 */
 

	
 
#include "stdafx.h"
 
#include "openttd.h"
 
#include "aystar.h"
 

	
 
int _aystar_stats_open_size;
 
int _aystar_stats_closed_size;
 

	
 
// This looks in the Hash if a node exists in ClosedList
 
//  If so, it returns the PathNode, else NULL
 
static PathNode *AyStarMain_ClosedList_IsInList(AyStar *aystar, AyStarNode *node)
 
static PathNode* AyStarMain_ClosedList_IsInList(AyStar* aystar, const AyStarNode* node)
 
{
 
	return (PathNode*)Hash_Get(&aystar->ClosedListHash, node->tile, node->direction);
 
}
 

	
 
// This adds a node to the ClosedList
 
//  It makes a copy of the data
 
static void AyStarMain_ClosedList_Add(AyStar *aystar, PathNode *node)
 
static void AyStarMain_ClosedList_Add(AyStar* aystar, const PathNode* node)
 
{
 
	// Add a node to the ClosedList
 
	PathNode *new_node = malloc(sizeof(PathNode));
 
	PathNode* new_node = malloc(sizeof(*new_node));
 
	*new_node = *node;
 
	Hash_Set(&aystar->ClosedListHash, node->node.tile, node->node.direction, new_node);
 
}
 

	
 
// Checks if a node is in the OpenList
 
//   If so, it returns the OpenListNode, else NULL
 
static OpenListNode *AyStarMain_OpenList_IsInList(AyStar *aystar, AyStarNode *node)
 
static OpenListNode* AyStarMain_OpenList_IsInList(AyStar* aystar, const AyStarNode* node)
 
{
 
	return (OpenListNode*)Hash_Get(&aystar->OpenListHash, node->tile, node->direction);
 
}
 

	
 
// Gets the best node from OpenList
 
//  returns the best node, or NULL of none is found
 
// Also it deletes the node from the OpenList
 
static OpenListNode *AyStarMain_OpenList_Pop(AyStar *aystar)
 
{
 
	// Return the item the Queue returns.. the best next OpenList item.
 
	OpenListNode* res = (OpenListNode*)aystar->OpenListQueue.pop(&aystar->OpenListQueue);
 
	if (res != NULL)
 
	if (res != NULL) {
 
		Hash_Delete(&aystar->OpenListHash, res->path.node.tile, res->path.node.direction);
 
	}
 

	
 
	return res;
 
}
 

	
 
// Adds a node to the OpenList
 
//  It makes a copy of node, and puts the pointer of parent in the struct
 
static void AyStarMain_OpenList_Add(AyStar *aystar, PathNode *parent, AyStarNode *node, int f, int g)
 
static void AyStarMain_OpenList_Add(AyStar* aystar, PathNode* parent, const AyStarNode* node, int f, int g)
 
{
 
	// Add a new Node to the OpenList
 
	OpenListNode* new_node = malloc(sizeof(OpenListNode));
 
	OpenListNode* new_node = malloc(sizeof(*new_node));
 
	new_node->g = g;
 
	new_node->path.parent = parent;
 
	new_node->path.node = *node;
 
	Hash_Set(&aystar->OpenListHash, node->tile, node->direction, new_node);
 

	
 
	// Add it to the queue
 
	aystar->OpenListQueue.push(&aystar->OpenListQueue, new_node, f);
 
}
 

	
 
/*
 
 * Checks one tile and calculate his f-value
 
 *  return values:
 
 *	AYSTAR_DONE : indicates we are done
 
 */
 
int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent) {
 
int AyStarMain_CheckTile(AyStar* aystar, AyStarNode* current, OpenListNode* parent)
 
{
 
	int new_f, new_g, new_h;
 
	PathNode *closedlist_parent;
 
	OpenListNode *check;
 

	
 
	// Check the new node against the ClosedList
 
	if (AyStarMain_ClosedList_IsInList(aystar, current) != NULL) return AYSTAR_DONE;
 

	
 
	// Calculate the G-value for this node
 
	new_g = aystar->CalculateG(aystar, current, parent);
 
	// If the value was INVALID_NODE, we don't do anything with this node
 
	if (new_g == AYSTAR_INVALID_NODE) return AYSTAR_DONE;
 

	
 
	// There should not be given any other error-code..
 
	assert(new_g >= 0);
 
	// Add the parent g-value to the new g-value
 
	new_g += parent->g;
 
	if (aystar->max_path_cost != 0 && (uint)new_g > aystar->max_path_cost) return AYSTAR_DONE;
 

	
 
	// Calculate the h-value
 
	new_h = aystar->CalculateH(aystar, current, parent);
 
	// There should not be given any error-code..
 
	assert(new_h >= 0);
 

	
 
	// The f-value if g + h
 
	new_f = new_g + new_h;
 

	
 
	// Get the pointer to the parent in the ClosedList (the currentone is to a copy of the one in the OpenList)
 
	closedlist_parent = AyStarMain_ClosedList_IsInList(aystar, &parent->path.node);
 

	
 
	// Check if this item is already in the OpenList
 
	if ((check = AyStarMain_OpenList_IsInList(aystar, current)) != NULL) {
 
	check = AyStarMain_OpenList_IsInList(aystar, current);
 
	if (check != NULL) {
 
		uint i;
 
		// Yes, check if this g value is lower..
 
		if (new_g > check->g) return AYSTAR_DONE;
 
		aystar->OpenListQueue.del(&aystar->OpenListQueue, check, 0);
 
		// It is lower, so change it to this item
 
		check->g = new_g;
 
		check->path.parent = closedlist_parent;
 
		/* Copy user data, will probably have changed */
 
		for (i=0;i<lengthof(current->user_data);i++)
 
		for (i = 0; i < lengthof(current->user_data); i++) {
 
			check->path.node.user_data[i] = current->user_data[i];
 
		}
 
		// Readd him in the OpenListQueue
 
		aystar->OpenListQueue.push(&aystar->OpenListQueue, check, new_f);
 
	} else {
 
		// A new node, add him to the OpenList
 
		AyStarMain_OpenList_Add(aystar, closedlist_parent, current, new_f, new_g);
 
	}
 

	
 
	return AYSTAR_DONE;
 
}
 

	
 
/*
 
 * This function is the core of AyStar. It handles one item and checks
 
 *  his neighbour items. If they are valid, they are added to be checked too.
 
 *  return values:
 
 *	AYSTAR_EMPTY_OPENLIST : indicates all items are tested, and no path
 
 *	has been found.
 
 *	AYSTAR_LIMIT_REACHED : Indicates that the max_nodes limit has been
 
 *	reached.
 
 *	AYSTAR_FOUND_END_NODE : indicates we found the end. Path_found now is true, and in path is the path found.
 
 *	AYSTAR_STILL_BUSY : indicates we have done this tile, did not found the path yet, and have items left to try.
 
 */
 
int AyStarMain_Loop(AyStar *aystar) {
 
int AyStarMain_Loop(AyStar* aystar)
 
{
 
	int i, r;
 

	
 
	// Get the best node from OpenList
 
	OpenListNode *current = AyStarMain_OpenList_Pop(aystar);
 
	// If empty, drop an error
 
	if (current == NULL) {
 
		return AYSTAR_EMPTY_OPENLIST;
 
	}
 
	if (current == NULL) return AYSTAR_EMPTY_OPENLIST;
 

	
 
	// Check for end node and if found, return that code
 
	if (aystar->EndNodeCheck(aystar, current) == AYSTAR_FOUND_END_NODE) {
 
		if (aystar->FoundEndNode != NULL)
 
			aystar->FoundEndNode(aystar, current);
 
		free(current);
 
		return AYSTAR_FOUND_END_NODE;
 
	}
 

	
 
	// Add the node to the ClosedList
 
	AyStarMain_ClosedList_Add(aystar, &current->path);
 

	
 
	// Load the neighbours
 
	aystar->GetNeighbours(aystar, current);
 

	
 
	// Go through all neighbours
 
	for (i=0;i<aystar->num_neighbours;i++) {
 
		// Check and add them to the OpenList if needed
 
		r = aystar->checktile(aystar, &aystar->neighbours[i], current);
 
	}
 

	
 
	// Free the node
 
	free(current);
 

	
 
	if (aystar->max_search_nodes != 0 && Hash_Size(&aystar->ClosedListHash) >= aystar->max_search_nodes) {
 
		/* We've expanded enough nodes */
 
		return AYSTAR_LIMIT_REACHED;
 
	} else {
 
		// Return that we are still busy
 
		return AYSTAR_STILL_BUSY;
 
	}
 
}
 

	
 
/*
 
 * This function frees the memory it allocated
 
 */
 
void AyStarMain_Free(AyStar *aystar) {
 
void AyStarMain_Free(AyStar* aystar)
 
{
 
	aystar->OpenListQueue.free(&aystar->OpenListQueue, false);
 
	/* 2nd argument above is false, below is true, to free the values only
 
	 * once */
 
	delete_Hash(&aystar->OpenListHash, true);
 
	delete_Hash(&aystar->ClosedListHash, true);
 
#ifdef AYSTAR_DEBUG
 
	printf("[AyStar] Memory free'd\n");
 
#endif
 
}
 

	
 
/*
 
 * This function make the memory go back to zero
 
 *  This function should be called when you are using the same instance again.
 
 */
 
void AyStarMain_Clear(AyStar *aystar) {
 
void AyStarMain_Clear(AyStar* aystar)
 
{
 
	// Clean the Queue, but not the elements within. That will be done by
 
	// the hash.
 
	aystar->OpenListQueue.clear(&aystar->OpenListQueue, false);
 
	// Clean the hashes
 
	clear_Hash(&aystar->OpenListHash, true);
 
	clear_Hash(&aystar->ClosedListHash, true);
 

	
 
#ifdef AYSTAR_DEBUG
 
	printf("[AyStar] Cleared AyStar\n");
 
#endif
 
}
 

	
 
/*
 
 * This is the function you call to run AyStar.
 
 *  return values:
 
 *	AYSTAR_FOUND_END_NODE : indicates we found an end node.
 
 *	AYSTAR_NO_PATH : indicates that there was no path found.
 
 *	AYSTAR_STILL_BUSY : indicates we have done some checked, that we did not found the path yet, and that we still have items left to try.
 
 * When the algorithm is done (when the return value is not AYSTAR_STILL_BUSY)
 
 * aystar->clear() is called. Note that when you stop the algorithm halfway,
 
 * you should still call clear() yourself!
 
 */
 
int AyStarMain_Main(AyStar *aystar) {
 
	int r, i = 0;
 
	// Loop through the OpenList
 
	//  Quit if result is no AYSTAR_STILL_BUSY or is more than loops_per_tick
 
	while ((r = aystar->loop(aystar)) == AYSTAR_STILL_BUSY && (aystar->loops_per_tick == 0 || ++i < aystar->loops_per_tick)) { }
 
#ifdef AYSTAR_DEBUG
 
	if (r == AYSTAR_FOUND_END_NODE)
 
		printf("[AyStar] Found path!\n");
 
	else if (r == AYSTAR_EMPTY_OPENLIST)
 
		printf("[AyStar] OpenList run dry, no path found\n");
 
	else if (r == AYSTAR_LIMIT_REACHED)
 
		printf("[AyStar] Exceeded search_nodes, no path found\n");
 
	switch (r) {
 
		case AYSTAR_FOUND_END_NODE: printf("[AyStar] Found path!\n"); break;
 
		case AYSTAR_EMPTY_OPENLIST: printf("[AyStar] OpenList run dry, no path found\n"); break;
 
		case AYSTAR_LIMIT_REACHED:  printf("[AyStar] Exceeded search_nodes, no path found\n"); break;
 
		default: break;
 
	}
 
#endif
 
	if (r != AYSTAR_STILL_BUSY) {
 
		/* We're done, clean up */
 
		_aystar_stats_open_size = aystar->OpenListHash.size;
 
		_aystar_stats_closed_size = aystar->ClosedListHash.size;
 
		aystar->clear(aystar);
 
	}
 

	
 
	// Check result-value
 
	if (r == AYSTAR_FOUND_END_NODE) return AYSTAR_FOUND_END_NODE;
 
	// Check if we have some left in the OpenList
 
	if (r == AYSTAR_EMPTY_OPENLIST || r == AYSTAR_LIMIT_REACHED) return AYSTAR_NO_PATH;
 

	
 
	// Return we are still busy
 
	return AYSTAR_STILL_BUSY;
 
	switch (r) {
 
		case AYSTAR_FOUND_END_NODE: return AYSTAR_FOUND_END_NODE;
 
		case AYSTAR_EMPTY_OPENLIST:
 
		case AYSTAR_LIMIT_REACHED:  return AYSTAR_NO_PATH;
 
		default:                    return AYSTAR_STILL_BUSY;
 
	}
 
}
 

	
 
/*
 
 * Adds a node from where to start an algorithm. Multiple nodes can be added
 
 * if wanted. You should make sure that clear() is called before adding nodes
 
 * if the AyStar has been used before (though the normal main loop calls
 
 * clear() automatically when the algorithm finishes
 
 * g is the cost for starting with this node.
 
 */
 
void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g) {
 
void AyStarMain_AddStartNode(AyStar* aystar, AyStarNode* start_node, uint g)
 
{
 
#ifdef AYSTAR_DEBUG
 
	printf("[AyStar] Starting A* Algorithm from node (%d, %d, %d)\n",
 
		TileX(start_node->tile), TileY(start_node->tile), start_node->direction);
 
#endif
 
	AyStarMain_OpenList_Add(aystar, NULL, start_node, 0, g);
 
}
 

	
 
void init_AyStar(AyStar* aystar, Hash_HashProc hash, uint num_buckets) {
 
void init_AyStar(AyStar* aystar, Hash_HashProc hash, uint num_buckets)
 
{
 
	// Allocated the Hash for the OpenList and ClosedList
 
	init_Hash(&aystar->OpenListHash, hash, num_buckets);
 
	init_Hash(&aystar->ClosedListHash, hash, num_buckets);
 

	
 
	// Set up our sorting queue
 
	//  BinaryHeap allocates a block of 1024 nodes
 
	//  When thatone gets full it reserves an otherone, till this number
 
	//  That is why it can stay this high
 
	init_BinaryHeap(&aystar->OpenListQueue, 102400);
 

	
 
	aystar->addstart	= AyStarMain_AddStartNode;
 
	aystar->main		= AyStarMain_Main;
 
	aystar->loop		= AyStarMain_Loop;
 
	aystar->free		= AyStarMain_Free;
 
	aystar->clear		= AyStarMain_Clear;
 
	aystar->checktile	= AyStarMain_CheckTile;
 
}
clear_cmd.c
Show inline comments
 
@@ -97,186 +97,190 @@ static int TerraformProc(TerraformerStat
 

	
 
	if (!IsTileType(tile, MP_RAILWAY)) {
 
		int32 ret = DoCommand(tile, 0,0, ts->flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR);
 

	
 
		if (CmdFailed(ret)) {
 
			_terraform_err_tile = tile;
 
			return -1;
 
		}
 

	
 
		ts->cost += ret;
 
	}
 

	
 
	if (ts->tile_table_count >= 625) return -1;
 
	ts->tile_table[ts->tile_table_count++] = tile;
 

	
 
	return 0;
 
}
 

	
 
static bool TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height)
 
{
 
	int nh;
 
	TerraformerHeightMod *mod;
 
	int count;
 

	
 
	assert(tile < MapSize());
 

	
 
	if (height < 0) {
 
		_error_message = STR_1003_ALREADY_AT_SEA_LEVEL;
 
		return false;
 
	}
 

	
 
	_error_message = STR_1004_TOO_HIGH;
 

	
 
	if (height > 15) return false;
 

	
 
	nh = TerraformGetHeightOfTile(ts, tile);
 
	if (nh < 0 || height == nh) return false;
 

	
 
	if (TerraformProc(ts, tile, 0) < 0) return false;
 
	if (TerraformProc(ts, tile + TileDiffXY( 0, -1), 1) < 0) return false;
 
	if (TerraformProc(ts, tile + TileDiffXY(-1, -1), 2) < 0) return false;
 
	if (TerraformProc(ts, tile + TileDiffXY(-1,  0), 3) < 0) return false;
 

	
 
	mod = ts->modheight;
 
	count = ts->modheight_count;
 

	
 
	for (;;) {
 
		if (count == 0) {
 
			if (ts->modheight_count >= 576)
 
				return false;
 
			if (ts->modheight_count >= 576) return false;
 
			ts->modheight_count++;
 
			break;
 
		}
 
		if (mod->tile == tile) break;
 
		mod++;
 
		count--;
 
	}
 

	
 
	mod->tile = tile;
 
	mod->height = (byte)height;
 

	
 
	ts->cost += _price.terraform;
 

	
 
	{
 
		int direction = ts->direction, r;
 
		const TileIndexDiffC *ttm;
 

	
 
		static const TileIndexDiffC _terraform_tilepos[] = {
 
			{ 1,  0},
 
			{-2,  0},
 
			{ 1,  1},
 
			{ 0, -2}
 
		};
 

	
 
		for (ttm = _terraform_tilepos; ttm != endof(_terraform_tilepos); ttm++) {
 
			tile += ToTileIndexDiff(*ttm);
 

	
 
			r = TerraformGetHeightOfTile(ts, tile);
 
			if (r != height && r-direction != height && r+direction != height) {
 
				if (!TerraformTileHeight(ts, tile, r+direction))
 
					return false;
 
			}
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
/** Terraform land
 
 * @param tile tile to terraform
 
 * @param p1 corners to terraform.
 
 * @param p2 direction; eg up or down
 
 */
 
int32 CmdTerraformLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	TerraformerState ts;
 
	TileIndex t;
 
	int direction;
 

	
 
	TerraformerHeightMod modheight_data[576];
 
	TileIndex tile_table_data[625];
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	_terraform_err_tile = 0;
 

	
 
	ts.direction = direction = p2 ? 1 : -1;
 
	ts.flags = flags;
 
	ts.modheight_count = ts.tile_table_count = 0;
 
	ts.cost = 0;
 
	ts.modheight = modheight_data;
 
	ts.tile_table = tile_table_data;
 

	
 
	/* Make an extra check for map-bounds cause we add tiles to the originating tile */
 
	if (tile + TileDiffXY(1, 1) >= MapSize()) return CMD_ERROR;
 

	
 
	if (p1 & 1) {
 
		if (!TerraformTileHeight(&ts, tile + TileDiffXY(1, 0),
 
				TileHeight(tile + TileDiffXY(1, 0)) + direction))
 
		t = tile + TileDiffXY(1, 0);
 
		if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
 
					return CMD_ERROR;
 
	}
 
	}
 

	
 
	if (p1 & 2) {
 
		if (!TerraformTileHeight(&ts, tile + TileDiffXY(1, 1),
 
				TileHeight(tile + TileDiffXY(1, 1)) + direction))
 
		t = tile + TileDiffXY(1, 1);
 
		if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
 
					return CMD_ERROR;
 
	}
 
	}
 

	
 
	if (p1 & 4) {
 
		if (!TerraformTileHeight(&ts, tile + TileDiffXY(0, 1),
 
				TileHeight(tile + TileDiffXY(0, 1)) + direction))
 
		t = tile + TileDiffXY(0, 1);
 
		if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
 
					return CMD_ERROR;
 
	}
 
	}
 

	
 
	if (p1 & 8) {
 
		if (!TerraformTileHeight(&ts, tile + TileDiffXY(0, 0),
 
				TileHeight(tile + TileDiffXY(0, 0)) + direction))
 
		t = tile + TileDiffXY(0, 0);
 
		if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
 
					return CMD_ERROR;
 
	}
 
	}
 

	
 
	{ /* Check if tunnel or track would take damage */
 
		int count;
 
		TileIndex *ti = ts.tile_table;
 

	
 
		for (count = ts.tile_table_count; count != 0; count--, ti++) {
 
			uint a, b, c, d, r, min;
 
			TileIndex tile = *ti;
 

	
 
			_terraform_err_tile = tile;
 

	
 
			a = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0));
 
			b = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0));
 
			c = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 1));
 
			d = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1));
 

	
 
			r = GetTileh(a, b, c, d, &min);
 

	
 
			if (IsTileType(tile, MP_RAILWAY)) {
 
				if (IsSteepSlope(r)) return_cmd_error(STR_1008_MUST_REMOVE_RAILROAD_TRACK);
 

	
 
				if (IsPlainRailTile(tile)) {
 
					extern const TrackBits _valid_tileh_slopes[2][15];
 
					if (GetTrackBits(tile) & ~_valid_tileh_slopes[0][r]) return_cmd_error(STR_1008_MUST_REMOVE_RAILROAD_TRACK);
 
				} else return_cmd_error(STR_5800_OBJECT_IN_THE_WAY);
 
			}
 

	
 
			if (direction == -1 && IsTunnelInWay(tile, min)) return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE);
 

	
 
			_terraform_err_tile = 0;
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		/* Clear the landscape at the tiles */
 
		{
 
			int count;
 
			TileIndex *ti = ts.tile_table;
 
			for (count = ts.tile_table_count; count != 0; count--, ti++) {
 
				DoCommand(*ti, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 
			}
 
		}
 

	
 
		/* change the height */
 
		{
 
			int count;
 
			TerraformerHeightMod *mod;
 

	
 
@@ -385,98 +389,97 @@ int32 CmdPurchaseLandArea(TileIndex tile
 
		MakeOwnedLand(tile, _current_player);
 
		MarkTileDirtyByTile(tile);
 
	}
 

	
 
	return cost + _price.purchase_land * 10;
 
}
 

	
 

	
 
static int32 ClearTile_Clear(TileIndex tile, byte flags)
 
{
 
	static const int32* clear_price_table[] = {
 
		&_price.clear_1,
 
		&_price.purchase_land,
 
		&_price.clear_2,
 
		&_price.clear_3,
 
		&_price.purchase_land,
 
		&_price.purchase_land,
 
		&_price.clear_2, // XXX unused?
 
	};
 
	int32 price;
 

	
 
	if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) {
 
		price = 0;
 
	} else {
 
		price = *clear_price_table[GetClearGround(tile)];
 
	}
 

	
 
	if (flags & DC_EXEC) DoClearSquare(tile);
 

	
 
	return price;
 
}
 

	
 
/** Sell a land area. Actually you only sell one tile, so
 
 * the name is a bit confusing ;p
 
 * @param tile the tile the player is selling
 
 * @param p1 unused
 
 * @param p2 unused
 
 */
 
int32 CmdSellLandArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	if (!IsOwnedLandTile(tile)) return CMD_ERROR;
 
	if (!CheckTileOwnership(tile) && _current_player != OWNER_WATER) return CMD_ERROR;
 

	
 

	
 
	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC)
 
		DoClearSquare(tile);
 
	if (flags & DC_EXEC) DoClearSquare(tile);
 

	
 
	return - _price.purchase_land * 2;
 
}
 

	
 

	
 
#include "table/clear_land.h"
 

	
 

	
 
void DrawClearLandTile(const TileInfo *ti, byte set)
 
{
 
	DrawGroundSprite(SPR_FLAT_BARE_LAND + _tileh_to_sprite[ti->tileh] + set * 19);
 
}
 

	
 
void DrawHillyLandTile(const TileInfo *ti)
 
{
 
	if (ti->tileh != SLOPE_FLAT) {
 
		DrawGroundSprite(SPR_FLAT_ROUGH_LAND + _tileh_to_sprite[ti->tileh]);
 
	} else {
 
		DrawGroundSprite(_landscape_clear_sprites[GB(ti->x ^ ti->y, 4, 3)]);
 
	}
 
}
 

	
 
void DrawClearLandFence(const TileInfo *ti)
 
{
 
	byte z = ti->z;
 

	
 
	if (ti->tileh & SLOPE_S) {
 
		z += TILE_HEIGHT;
 
		if (ti->tileh == SLOPE_STEEP_S) z += TILE_HEIGHT;
 
	}
 

	
 
	if (GetFenceSW(ti->tile) != 0) {
 
		DrawGroundSpriteAt(_clear_land_fence_sprites_1[GetFenceSW(ti->tile) - 1] + _fence_mod_by_tileh[ti->tileh], ti->x, ti->y, z);
 
	}
 

	
 
	if (GetFenceSE(ti->tile) != 0) {
 
		DrawGroundSpriteAt(_clear_land_fence_sprites_1[GetFenceSE(ti->tile) - 1] + _fence_mod_by_tileh_2[ti->tileh], ti->x, ti->y, z);
 
	}
 
}
 

	
 
static void DrawTile_Clear(TileInfo *ti)
 
{
 
	switch (GetClearGround(ti->tile)) {
 
		case CLEAR_GRASS:
 
			DrawClearLandTile(ti, GetClearDensity(ti->tile));
 
			break;
 

	
 
		case CLEAR_ROUGH:
command.c
Show inline comments
 
@@ -269,128 +269,133 @@ static const Command _command_proc_table
 
	{CmdSellShip,                            0}, /*  87 */
 
	{CmdBuildShip,                           0}, /*  88 */
 
	{CmdSendShipToDepot,                     0}, /*  89 */
 
	{NULL,                                   0}, /*  90 */
 
	{CmdRefitShip,                           0}, /*  91 */
 

	
 
	{NULL,                                   0}, /*  92 */
 
	{NULL,                                   0}, /*  93 */
 
	{NULL,                                   0}, /*  94 */
 
	{NULL,                                   0}, /*  95 */
 
	{NULL,                                   0}, /*  96 */
 
	{NULL,                                   0}, /*  97 */
 
	{NULL,                                   0}, /*  98 */
 

	
 
	{CmdCloneOrder,                          0}, /*  99 */
 

	
 
	{CmdClearArea,                           0}, /* 100 */
 
	{NULL,                                   0}, /* 101 */
 

	
 
	{CmdMoneyCheat,                CMD_OFFLINE}, /* 102 */
 
	{CmdBuildCanal,                          0}, /* 103 */
 
	{CmdPlayerCtrl,                          0}, /* 104 */
 

	
 
	{CmdLevelLand,                           0}, /* 105 */
 

	
 
	{CmdRefitRailVehicle,                    0}, /* 106 */
 
	{CmdRestoreOrderIndex,                   0}, /* 107 */
 
	{CmdBuildLock,                           0}, /* 108 */
 
	{NULL,                                   0}, /* 109 */
 
	{CmdBuildSignalTrack,                    0}, /* 110 */
 
	{CmdRemoveSignalTrack,                   0}, /* 111 */
 
	{NULL,                                   0}, /* 112 */
 
	{CmdGiveMoney,                           0}, /* 113 */
 
	{CmdChangePatchSetting,         CMD_SERVER}, /* 114 */
 
	{CmdReplaceVehicle,                      0}, /* 115 */
 
	{CmdCloneVehicle,                        0}, /* 116 */
 
};
 

	
 
/* This function range-checks a cmd, and checks if the cmd is not NULL */
 
bool IsValidCommand(uint cmd)
 
{
 
	cmd &= 0xFF;
 

	
 
	return
 
		cmd < lengthof(_command_proc_table) &&
 
		_command_proc_table[cmd].proc != NULL;
 
}
 

	
 
byte GetCommandFlags(uint cmd) {return _command_proc_table[cmd & 0xFF].flags;}
 
byte GetCommandFlags(uint cmd)
 
{
 
	return _command_proc_table[cmd & 0xFF].flags;
 
}
 

	
 

	
 
static int _docommand_recursive;
 

	
 
int32 DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
 
{
 
	int32 res;
 
	CommandProc *proc;
 

	
 
	/* Do not even think about executing out-of-bounds tile-commands */
 
	if (tile >= MapSize()) {
 
		_cmd_text = NULL;
 
		return CMD_ERROR;
 
	}
 

	
 
	proc = _command_proc_table[procc].proc;
 

	
 
	if (_docommand_recursive == 0) _error_message = INVALID_STRING_ID;
 

	
 
	_docommand_recursive++;
 

	
 
	// only execute the test call if it's toplevel, or we're not execing.
 
	if (_docommand_recursive == 1 || !(flags & DC_EXEC) || (flags & DC_FORCETEST) ) {
 
		res = proc(tile, flags & ~DC_EXEC, p1, p2);
 
		if (CmdFailed(res)) {
 
			if (res & 0xFFFF) _error_message = res & 0xFFFF;
 
			goto error;
 
		}
 

	
 
		if (_docommand_recursive == 1) {
 
			if (!(flags&DC_QUERY_COST) && res != 0 && !CheckPlayerHasMoney(res))
 
		if (_docommand_recursive == 1 &&
 
				!(flags & DC_QUERY_COST) &&
 
				res != 0 &&
 
				!CheckPlayerHasMoney(res)) {
 
				goto error;
 
		}
 

	
 
		if (!(flags & DC_EXEC)) {
 
			_docommand_recursive--;
 
			_cmd_text = NULL;
 
			return res;
 
		}
 
	}
 

	
 
	/* Execute the command here. All cost-relevant functions set the expenses type
 
	 * themselves with "SET_EXPENSES_TYPE(...);" at the beginning of the function */
 
	res = proc(tile, flags, p1, p2);
 
	if (CmdFailed(res)) {
 
		if (res & 0xFFFF) _error_message = res & 0xFFFF;
 
error:
 
		_docommand_recursive--;
 
		_cmd_text = NULL;
 
		return CMD_ERROR;
 
	}
 

	
 
	// if toplevel, subtract the money.
 
	if (--_docommand_recursive == 0) {
 
		SubtractMoneyFromPlayer(res);
 
		// XXX - Old AI hack which doesn't use DoCommandDP; update last build coord of player
 
		if (tile != 0 && _current_player < MAX_PLAYERS) {
 
			GetPlayer(_current_player)->last_build_coordinate = tile;
 
		}
 
	}
 

	
 
	_cmd_text = NULL;
 
	return res;
 
}
 

	
 
int32 GetAvailableMoneyForCommand(void)
 
{
 
	PlayerID pid = _current_player;
 
	if (pid >= MAX_PLAYERS) return 0x7FFFFFFF; // max int
 
	return GetPlayer(pid)->player_money;
 
}
 

	
 
// toplevel network safe docommand function for the current player. must not be called recursively.
 
// the callback is called when the command succeeded or failed.
 
bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd)
 
{
 
	int32 res = 0,res2;
 
	CommandProc *proc;
 
	uint32 flags;
 
@@ -451,97 +456,98 @@ bool DoCommandP(TileIndex tile, uint32 p
 
	_docommand_recursive = 1;
 

	
 
	// cost estimation only?
 
	if (_shift_pressed && IsLocalPlayer() && !(cmd & (CMD_NETWORK_COMMAND | CMD_SHOW_NO_ERROR))) {
 
		// estimate the cost.
 
		res = proc(tile, flags, p1, p2);
 
		if (CmdFailed(res)) {
 
			if (res & 0xFFFF) _error_message = res & 0xFFFF;
 
			ShowErrorMessage(_error_message, error_part1, x, y);
 
		} else {
 
			ShowEstimatedCostOrIncome(res, x, y);
 
		}
 

	
 
		_docommand_recursive = 0;
 
		_cmd_text = NULL;
 
		return false;
 
	}
 

	
 

	
 
	if (!((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) {
 
		// first test if the command can be executed.
 
		res = proc(tile, flags, p1, p2);
 
		if (CmdFailed(res)) {
 
			if (res & 0xFFFF) _error_message = res & 0xFFFF;
 
			goto show_error;
 
		}
 
		// no money? Only check if notest is off
 
		if (!notest && res != 0 && !CheckPlayerHasMoney(res)) goto show_error;
 
	}
 

	
 
#ifdef ENABLE_NETWORK
 
	/** If we are in network, and the command is not from the network
 
	 * send it to the command-queue and abort execution
 
	 * If we are a dedicated server temporarily switch local player, otherwise
 
	 * the other parties won't be able to execute our command and will desync.
 
	 * @todo Rewrite dedicated server to something more than a dirty hack!
 
	 */
 
	if (_networking && !(cmd & CMD_NETWORK_COMMAND)) {
 
		if (_network_dedicated) _local_player = 0;
 
		NetworkSend_Command(tile, p1, p2, cmd, callback);
 
		if (_network_dedicated) _local_player = OWNER_SPECTATOR;
 
		_docommand_recursive = 0;
 
		_cmd_text = NULL;
 
		return true;
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	// update last build coordinate of player.
 
	if ( tile != 0 && _current_player < MAX_PLAYERS) GetPlayer(_current_player)->last_build_coordinate = tile;
 
	if (tile != 0 && _current_player < MAX_PLAYERS) {
 
		GetPlayer(_current_player)->last_build_coordinate = tile;
 
	}
 

	
 
	/* Actually try and execute the command. If no cost-type is given
 
	 * use the construction one */
 
	_yearly_expenses_type = EXPENSES_CONSTRUCTION;
 
	res2 = proc(tile, flags|DC_EXEC, p1, p2);
 

	
 
	// If notest is on, it means the result of the test can be different than
 
	//   the real command.. so ignore the test
 
	if (!notest && !((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) {
 
		assert(res == res2); // sanity check
 
	} else {
 
		if (CmdFailed(res2)) {
 
			if (res2 & 0xFFFF) _error_message = res2 & 0xFFFF;
 
			goto show_error;
 
		}
 
	}
 

	
 
	SubtractMoneyFromPlayer(res2);
 

	
 
	if (IsLocalPlayer() && _game_mode != GM_EDITOR) {
 
		if (res2 != 0)
 
			ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2);
 
		if (res2 != 0) ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2);
 
		if (_additional_cash_required) {
 
			SetDParam(0, _additional_cash_required);
 
			ShowErrorMessage(STR_0003_NOT_ENOUGH_CASH_REQUIRES, error_part1, x,y);
 
			if (res2 == 0) goto callb_err;
 
		}
 
	}
 

	
 
	_docommand_recursive = 0;
 

	
 
	if (callback) callback(true, tile, p1, p2);
 
	_cmd_text = NULL;
 
	return true;
 

	
 
show_error:
 
	// show error message if the command fails?
 
	if (IsLocalPlayer() && error_part1 != 0) {
 
		ShowErrorMessage(_error_message, error_part1, x,y);
 
	}
 

	
 
callb_err:
 
	_docommand_recursive = 0;
 

	
 
	if (callback) callback(false, tile, p1, p2);
 
	_cmd_text = NULL;
 
	return false;
 
}
depot.h
Show inline comments
 
@@ -27,94 +27,93 @@ static inline Depot *GetDepot(uint index
 
	return (Depot*)GetItemFromPool(&_depot_pool, index);
 
}
 

	
 
/**
 
 * Get the current size of the DepotPool
 
 */
 
static inline uint16 GetDepotPoolSize(void)
 
{
 
	return _depot_pool.total_items;
 
}
 

	
 
static inline bool IsDepotIndex(uint index)
 
{
 
	return index < GetDepotPoolSize();
 
}
 

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

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

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

	
 

	
 
/**
 
 * Check if a depot really exists.
 
 */
 
static inline bool IsValidDepot(const Depot* depot)
 
{
 
	return depot->xy != 0; /* XXX: Replace by INVALID_TILE someday */
 
}
 

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

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

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

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

	
 

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

	
 

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

	
 

	
 
Depot *GetDepotByTile(TileIndex tile);
 
void InitializeDepot(void);
 
Depot *AllocateDepot(void);
 
void DoDeleteDepot(TileIndex tile);
 

	
 
#endif /* DEPOT_H */
elrail.c
Show inline comments
 
@@ -149,98 +149,99 @@ static void DrawCatenaryRailway(const Ti
 
	   the track configuration of 2 adjacent tiles. trackconfig[0] stores the
 
	   current tile (home tile) while [1] holds the neighbour */
 
	TrackBits trackconfig[TS_END];
 
	bool isflat[TS_END];
 
	/* Note that ti->tileh has already been adjusted for Foundations */
 
	Slope tileh[TS_END] = { ti->tileh, SLOPE_FLAT };
 

	
 
	TLG tlg = GetTLG(ti->tile);
 
	byte PCPstatus = 0;
 
	byte OverridePCP = 0;
 
	byte PPPpreferred[DIAGDIR_END];
 
	byte PPPallowed[DIAGDIR_END];
 
	DiagDirection i;
 
	Track t;
 

	
 
	/* Find which rail bits are present, and select the override points.
 
	   We don't draw a pylon:
 
	   1) INSIDE a tunnel (we wouldn't see it anyway)
 
	   2) on the "far" end of a bridge head (the one that connects to bridge middle),
 
	      because that one is drawn on the bridge. Exception is for length 0 bridges
 
	      which have no middle tiles */
 
	trackconfig[TS_HOME] = GetRailTrackBitsUniversal(ti->tile, &OverridePCP);
 
	/* If a track bit is present that is not in the main direction, the track is level */
 
	isflat[TS_HOME] = trackconfig[TS_HOME] & (TRACK_BIT_HORZ | TRACK_BIT_VERT);
 

	
 
	AdjustTileh(ti->tile, &tileh[TS_HOME]);
 

	
 
	for (i = DIAGDIR_NE; i < DIAGDIR_END; i++) {
 
		TileIndex neighbour = ti->tile + TileOffsByDir(i);
 
		uint foundation = 0;
 
		int k;
 

	
 
		/* Here's one of the main headaches. GetTileSlope does not correct for possibly
 
		   existing foundataions, so we do have to do that manually later on.*/
 
		tileh[TS_NEIGHBOUR] = GetTileSlope(neighbour, NULL);
 
		trackconfig[TS_NEIGHBOUR] = GetRailTrackBitsUniversal(neighbour, NULL);
 
		if (IsTunnelTile(neighbour) && i != GetTunnelDirection(neighbour)) trackconfig[TS_NEIGHBOUR] = 0;
 
		isflat[TS_NEIGHBOUR] = trackconfig[TS_NEIGHBOUR] & (TRACK_BIT_HORZ | TRACK_BIT_VERT);
 

	
 
		PPPpreferred[i] = 0xFF; /* We start with preferring everything (end-of-line in any direction) */
 
		PPPallowed[i] = AllowedPPPonPCP[i];
 

	
 
		/* We cycle through all the existing tracks at a PCP and see what
 
		   PPPs we want to have, or may not have at all */
 
		for (k = 0; k < NUM_TRACKS_AT_PCP; k++) {
 
			/* Next to us, we have a bridge head, don't worry about that one, if it shows away from us */
 
			if (TrackSourceTile[i][k] == TS_NEIGHBOUR &&
 
			    IsBridgeTile(neighbour) && IsBridgeRamp(neighbour) &&
 
			    GetBridgeRampDirection(neighbour) == ReverseDiagDir(i)
 
			   ) continue;
 
			    GetBridgeRampDirection(neighbour) == ReverseDiagDir(i)) {
 
				continue;
 
			}
 

	
 
			/* We check whether the track in question (k) is present in the tile
 
			   (TrackSourceTile) */
 
			if (HASBIT(trackconfig[TrackSourceTile[i][k]], TracksAtPCP[i][k])) {
 
				/* track found, if track is in the neighbour tile, adjust the number
 
				   of the PCP for preferred/allowed determination*/
 
				DiagDirection PCPpos = (TrackSourceTile[i][k] == TS_HOME) ? i : ReverseDiagDir(i);
 
				SETBIT(PCPstatus, i); /* This PCP is in use */
 

	
 
				PPPpreferred[i] &= PreferredPPPofTrackAtPCP[TracksAtPCP[i][k]][PCPpos];
 
				PPPallowed[i] &= ~DisallowedPPPofTrackAtPCP[TracksAtPCP[i][k]][PCPpos];
 
			}
 
		}
 

	
 
		/* Deactivate all PPPs if PCP is not used */
 
		PPPpreferred[i] *= HASBIT(PCPstatus, i);
 
		PPPallowed[i] *= HASBIT(PCPstatus, i);
 

	
 
		/* A station is always "flat", so adjust the tileh accordingly */
 
		if (IsTileType(neighbour, MP_STATION)) tileh[TS_NEIGHBOUR] = SLOPE_FLAT;
 

	
 
		/* Read the foundataions if they are present, and adjust the tileh */
 
		if (IsTileType(neighbour, MP_RAILWAY)) foundation = GetRailFoundation(tileh[TS_NEIGHBOUR], trackconfig[TS_NEIGHBOUR]);
 
		if (IsBridgeTile(neighbour) && IsBridgeRamp(neighbour)) {
 
			foundation = GetBridgeFoundation(tileh[TS_NEIGHBOUR], DiagDirToAxis(GetBridgeRampDirection(neighbour)));
 
		}
 

	
 
		if (foundation != 0) {
 
			if (foundation < 15) {
 
				tileh[TS_NEIGHBOUR] = SLOPE_FLAT;
 
			} else {
 
				tileh[TS_NEIGHBOUR] = _inclined_tileh[foundation - 15];
 
			}
 
		}
 

	
 
		AdjustTileh(neighbour, &tileh[TS_NEIGHBOUR]);
 

	
 
		/* If we have a straight (and level) track, we want a pylon only every 2 tiles
 
		   Delete the PCP if this is the case. */
 
		/* Level means that the slope is the same, or the track is flat */
 
		if (tileh[TS_HOME] == tileh[TS_NEIGHBOUR] || (isflat[TS_HOME] && isflat[TS_NEIGHBOUR])) {
 
			for (k = 0; k < NUM_IGNORE_GROUPS; k++)
 
				if (PPPpreferred[i] == IgnoredPCP[k][tlg][i]) CLRBIT(PCPstatus, i);
 
		}
 

	
 
		/* Now decide where we draw our pylons. First try the preferred PPPs, but they may not exist.
 
		   In that case, we try the any of the allowed ones. if they don't exist either, don't draw
 
		   anything. Note that the preferred PPPs still contain the end-of-line markers.
functions.h
Show inline comments
 
@@ -160,97 +160,97 @@ void NetworkSend_Command(TileIndex tile,
 
void PlaceTreesRandomly(void);
 

	
 
void InitializeLandscapeVariables(bool only_constants);
 

	
 
/* misc.c */
 
void DeleteName(StringID id);
 
char *GetName(int id, char *buff);
 

	
 
// AllocateNameUnique also tests if the name used is not used anywere else
 
//  and if it is used, it returns an error.
 
#define AllocateNameUnique(name, skip) RealAllocateName(name, skip, true)
 
#define AllocateName(name, skip) RealAllocateName(name, skip, false)
 
StringID RealAllocateName(const char *name, byte skip, bool check_double);
 
void ConvertDayToYMD(YearMonthDay *ymd, uint16 date);
 
uint ConvertYMDToDay(uint year, uint month, uint day);
 
uint ConvertIntDate(uint date);
 

	
 
/* misc functions */
 
void MarkTileDirty(int x, int y);
 
void MarkTileDirtyByTile(TileIndex tile);
 
void InvalidateWindow(WindowClass cls, WindowNumber number);
 
void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index);
 
void InvalidateWindowClasses(WindowClass cls);
 
void DeleteWindowById(WindowClass cls, WindowNumber number);
 
void DeleteWindowByClass(WindowClass cls);
 

	
 
void SetObjectToPlaceWnd(CursorID icon, byte mode, Window *w);
 
void SetObjectToPlace(CursorID icon, byte mode, WindowClass window_class, WindowNumber window_num);
 

	
 
void ResetObjectToPlace(void);
 

	
 
bool ScrollWindowTo(int x, int y, Window * w);
 

	
 
bool ScrollMainWindowToTile(TileIndex tile);
 
bool ScrollMainWindowTo(int x, int y);
 
void DrawSprite(uint32 img, int x, int y);
 
bool EnsureNoVehicle(TileIndex tile);
 
bool EnsureNoVehicleOnGround(TileIndex tile);
 
void MarkAllViewportsDirty(int left, int top, int right, int bottom);
 
void ShowCostOrIncomeAnimation(int x, int y, int z, int32 cost);
 
void ShowFeederIncomeAnimation(int x, int y, int z, int32 cost);
 

	
 
void DrawFoundation(TileInfo *ti, uint f);
 

	
 
bool CheckIfAuthorityAllows(TileIndex tile);
 
Town *ClosestTownFromTile(TileIndex tile, uint threshold);
 
void ChangeTownRating(Town *t, int add, int max);
 

	
 
int GetTownRadiusGroup(const Town *t, TileIndex tile);
 
uint GetTownRadiusGroup(const Town* t, TileIndex tile);
 
void ShowNetworkChatQueryWindow(byte desttype, byte dest);
 
void ShowNetworkGiveMoneyWindow(byte player);
 
void ShowNetworkNeedGamePassword(void);
 
void ShowNetworkNeedCompanyPassword(void);
 
int FindFirstBit(uint32 x);
 
void ShowHighscoreTable(int difficulty, int8 rank);
 
void ShowEndGameChart(void);
 
TileIndex AdjustTileCoordRandomly(TileIndex a, byte rng);
 

	
 
void AfterLoadTown(void);
 
void UpdatePatches(void);
 
void GenRandomNewGame(uint32 rnd1, uint32 rnd2);
 
void StartScenarioEditor(uint32 rnd1, uint32 rnd2);
 
void AskExitGame(void);
 
void AskExitToGameMenu(void);
 

	
 
void RedrawAutosave(void);
 

	
 
StringID RemapOldStringID(StringID s);
 

	
 
void UpdateViewportSignPos(ViewportSign *sign, int left, int top, StringID str);
 

	
 
enum {
 
	SLD_LOAD_GAME = 0,
 
	SLD_LOAD_SCENARIO = 1,
 
	SLD_SAVE_GAME = 2,
 
	SLD_SAVE_SCENARIO = 3,
 
	SLD_NEW_GAME = 4,
 
};
 
void ShowSaveLoadDialog(int mode);
 

	
 
// callback from drivers that is called if the game size changes dynamically
 
void GameSizeChanged(void);
 
bool FileExists(const char *filename);
 
bool ReadLanguagePack(int index);
 
void InitializeLanguagePacks(void);
 
const char *GetCurrentLocale(const char *param);
 
void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize);
 
int GetLanguageList(char **languages, int max);
 

	
 
void LoadFromConfig(void);
 
void SaveToConfig(void);
 
void CheckConfig(void);
 
int ttd_main(int argc, char* argv[]);
 

	
 
void DeterminePaths(void);
 

	
 
void CSleep(int milliseconds);
landscape.c
Show inline comments
 
@@ -350,97 +350,97 @@ int32 CmdClearArea(TileIndex tile, uint3
 
#define TILELOOP_CHKMASK (((1 << (MapLogX() - TILELOOP_BITS))-1) << TILELOOP_BITS)
 

	
 
void RunTileLoop(void)
 
{
 
	TileIndex tile;
 
	uint count;
 

	
 
	tile = _cur_tileloop_tile;
 

	
 
	assert( (tile & ~TILELOOP_ASSERTMASK) == 0);
 
	count = (MapSizeX() / TILELOOP_SIZE) * (MapSizeY() / TILELOOP_SIZE);
 
	do {
 
		_tile_type_procs[GetTileType(tile)]->tile_loop_proc(tile);
 

	
 
		if (TileX(tile) < MapSizeX() - TILELOOP_SIZE) {
 
			tile += TILELOOP_SIZE; /* no overflow */
 
		} else {
 
			tile = TILE_MASK(tile - TILELOOP_SIZE * (MapSizeX() / TILELOOP_SIZE - 1) + TileDiffXY(0, TILELOOP_SIZE)); /* x would overflow, also increase y */
 
		}
 
	} while (--count);
 
	assert( (tile & ~TILELOOP_ASSERTMASK) == 0);
 

	
 
	tile += 9;
 
	if (tile & TILELOOP_CHKMASK)
 
		tile = (tile + MapSizeX()) & TILELOOP_ASSERTMASK;
 
	_cur_tileloop_tile = tile;
 
}
 

	
 
void InitializeLandscape(void)
 
{
 
	uint maxx = MapMaxX();
 
	uint maxy = MapMaxY();
 
	uint sizex = MapSizeX();
 
	uint x;
 
	uint y;
 

	
 
	for (y = 0; y < maxy; y++) {
 
		for (x = 0; x < maxx; x++) {
 
			MakeClear(sizex * y + x, CLEAR_GRASS, 3);
 
			SetTileHeight(sizex * y + x, 0);
 
		}
 
		MakeVoid(sizex * y + x);
 
	}
 
	for (x = 0; x < sizex; x++) MakeVoid(sizex * y + x);
 
}
 

	
 
void ConvertGroundTilesIntoWaterTiles(void)
 
{
 
	TileIndex tile = 0;
 
	TileIndex tile;
 

	
 
	for (tile = 0; tile < MapSize(); ++tile) {
 
		if (IsTileType(tile, MP_CLEAR) && GetTileMaxZ(tile) == 0) {
 
			MakeWater(tile);
 
		}
 
	}
 
}
 

	
 
static const byte _genterrain_tbl_1[5] = { 10, 22, 33, 37, 4 };
 
static const byte _genterrain_tbl_2[5] = { 0, 0, 0, 0, 33 };
 

	
 
static void GenerateTerrain(int type, int flag)
 
{
 
	uint32 r;
 
	uint x;
 
	uint y;
 
	uint w;
 
	uint h;
 
	const Sprite* template;
 
	const byte *p;
 
	Tile* tile;
 
	byte direction;
 

	
 
	r = Random();
 
	template = GetSprite((((r >> 24) * _genterrain_tbl_1[type]) >> 8) + _genterrain_tbl_2[type] + 4845);
 

	
 
	x = r & MapMaxX();
 
	y = (r >> MapLogX()) & MapMaxY();
 

	
 

	
 
	if (x < 2 || y < 2) return;
 

	
 
	direction = GB(r, 22, 2);
 
	if (direction & 1) {
 
		w = template->height;
 
		h = template->width;
 
	} else {
 
		w = template->width;
 
		h = template->height;
 
	}
 
	p = template->data;
 

	
 
	if (flag & 4) {
 
		uint xw = x * MapSizeY();
 
		uint yw = y * MapSizeX();
 
		uint bias = (MapSizeX() + MapSizeY()) * 16;
 

	
 
		switch (flag & 3) {
map.h
Show inline comments
 
@@ -22,101 +22,101 @@ typedef struct Tile {
 
	uint16 m2;
 
	byte m3;
 
	byte m4;
 
	byte m5;
 
	byte extra;
 
} Tile;
 

	
 
extern Tile* _m;
 

	
 
void AllocateMap(uint size_x, uint size_y);
 

	
 
// binary logarithm of the map size, try to avoid using this one
 
static inline uint MapLogX(void)  { return _map_log_x; }
 
/* The size of the map */
 
static inline uint MapSizeX(void) { return _map_size_x; }
 
static inline uint MapSizeY(void) { return _map_size_y; }
 
/* The maximum coordinates */
 
static inline uint MapMaxX(void) { return _map_size_x - 1; }
 
static inline uint MapMaxY(void) { return _map_size_y - 1; }
 
/* The number of tiles in the map */
 
static inline uint MapSize(void) { return _map_size; }
 

	
 
// Scale a number relative to the map size
 
uint ScaleByMapSize(uint); // Scale relative to the number of tiles
 
uint ScaleByMapSize1D(uint); // Scale relative to the circumference of the map
 

	
 
typedef uint32 TileIndex;
 
typedef int32 TileIndexDiff;
 

	
 
static inline TileIndex TileXY(uint x, uint y)
 
{
 
	return (y * MapSizeX()) + x;
 
}
 

	
 
static inline TileIndexDiff TileDiffXY(int x, int y)
 
{
 
	// Multiplication gives much better optimization on MSVC than shifting.
 
	// 0 << shift isn't optimized to 0 properly.
 
	// Typically x and y are constants, and then this doesn't result
 
	// in any actual multiplication in the assembly code..
 
	return (y * MapSizeX()) + x;
 
}
 

	
 
static inline TileIndex TileVirtXY(uint x, uint y)
 
{
 
	return (y >> 4 << MapLogX()) + (x >> 4);
 
}
 

	
 
typedef enum {
 
	OWNER_TOWN			= 0xf,	// a town owns the tile
 
typedef enum Owner {
 
	OWNER_TOWN      = 0x0F, // a town owns the tile
 
	OWNER_NONE			= 0x10,	// nobody owns the tile
 
	OWNER_WATER			= 0x11,	// "water" owns the tile
 
	OWNER_SPECTATOR	= 0xff,	// spectator in MP or in scenario editor
 
	OWNER_SPECTATOR = 0xFF, // spectator in MP or in scenario editor
 
} Owner;
 

	
 
enum {
 
	INVALID_TILE = (TileIndex)-1
 
};
 

	
 
enum {
 
	TILE_SIZE   = 16,   /* Tiles are 16x16 "units" in size */
 
	TILE_PIXELS = 32,   /* a tile is 32x32 pixels */
 
	TILE_HEIGHT = 8,    /* The standard height-difference between tiles on two levels is 8 (z-diff 8) */
 
};
 

	
 

	
 
static inline uint TileX(TileIndex tile)
 
{
 
	return tile & MapMaxX();
 
}
 

	
 
static inline uint TileY(TileIndex tile)
 
{
 
	return tile >> MapLogX();
 
}
 

	
 

	
 
typedef struct TileIndexDiffC {
 
	int16 x;
 
	int16 y;
 
} TileIndexDiffC;
 

	
 
static inline TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
 
{
 
	return (tidc.y << MapLogX()) + tidc.x;
 
}
 

	
 

	
 
#ifndef _DEBUG
 
	#define TILE_ADD(x,y) ((x) + (y))
 
#else
 
	extern TileIndex TileAdd(TileIndex tile, TileIndexDiff add,
 
		const char *exp, const char *file, int line);
 
	#define TILE_ADD(x, y) (TileAdd((x), (y), #x " + " #y, __FILE__, __LINE__))
 
#endif
 

	
 
#define TILE_ADDXY(tile, x, y) TILE_ADD(tile, TileDiffXY(x, y))
 

	
 
uint TileAddWrap(TileIndex tile, int addx, int addy);
 

	
 
static inline TileIndexDiffC TileIndexDiffCByDir(uint dir) {
misc_gui.c
Show inline comments
 
@@ -685,97 +685,98 @@ void GuiShowTooltips(StringID string_id)
 

	
 
static void DrawStationCoverageText(const AcceptedCargo accepts,
 
	int str_x, int str_y, uint mask)
 
{
 
	char *b = _userstring;
 
	int i;
 

	
 
	b = InlineString(b, STR_000D_ACCEPTS);
 

	
 
	for (i = 0; i != NUM_CARGO; i++, mask >>= 1) {
 
		if (accepts[i] >= 8 && mask & 1) {
 
			b = InlineString(b, _cargoc.names_s[i]);
 
			*b++ = ',';
 
			*b++ = ' ';
 
		}
 
	}
 

	
 
	if (b == &_userstring[3]) {
 
		b = InlineString(b, STR_00D0_NOTHING);
 
		*b++ = '\0';
 
	} else {
 
		b[-2] = '\0';
 
	}
 

	
 
	DrawStringMultiLine(str_x, str_y, STR_SPEC_USERSTRING, 144);
 
}
 

	
 
void DrawStationCoverageAreaText(int sx, int sy, uint mask, int rad) {
 
	TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
 
	AcceptedCargo accepts;
 
	if (tile < MapSize()) {
 
		GetAcceptanceAroundTiles(accepts, tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE , rad);
 
		DrawStationCoverageText(accepts, sx, sy, mask);
 
	}
 
}
 

	
 
void CheckRedrawStationCoverage(const Window* w)
 
{
 
	if (_thd.dirty & 1) {
 
		_thd.dirty &= ~1;
 
		SetWindowDirty(w);
 
	}
 
}
 

	
 

	
 
void UnclickSomeWindowButtons(Window *w, uint32 mask)
 
{
 
	uint32 x = w->click_state & mask;
 
	int i = 0;
 
	uint i = 0;
 

	
 
	w->click_state ^= x;
 
	do {
 
		if (x & 1) InvalidateWidget(w, i);
 
	} while (i++, x >>= 1);
 
}
 

	
 

	
 
void UnclickWindowButtons(Window *w)
 
{
 
	UnclickSomeWindowButtons(w, (uint32)-1);
 
}
 

	
 

	
 
void SetVScrollCount(Window *w, int num)
 
{
 
	w->vscroll.count = num;
 
	num -= w->vscroll.cap;
 
	if (num < 0) num = 0;
 
	if (num < w->vscroll.pos) w->vscroll.pos = num;
 
}
 

	
 
void SetVScroll2Count(Window *w, int num)
 
{
 
	w->vscroll2.count = num;
 
	num -= w->vscroll2.cap;
 
	if (num < 0) num = 0;
 
	if (num < w->vscroll2.pos) w->vscroll2.pos = num;
 
}
 

	
 
void SetHScrollCount(Window *w, int num)
 
{
 
	w->hscroll.count = num;
 
	num -= w->hscroll.cap;
 
	if (num < 0) num = 0;
 
	if (num < w->hscroll.pos) w->hscroll.pos = num;
 
}
 

	
 
static void DelChar(Textbuf *tb)
 
{
 
	tb->width -= GetCharacterWidth(FS_NORMAL, (byte)tb->buf[tb->caretpos]);
 
	memmove(tb->buf + tb->caretpos, tb->buf + tb->caretpos + 1, tb->length - tb->caretpos);
 
	tb->length--;
 
}
 

	
 
/**
 
 * Delete a character from a textbuffer, either with 'Delete' or 'Backspace'
 
 * The character is delete from the position the caret is at
 
 * @param tb @Textbuf type to be changed
 
@@ -1206,130 +1207,130 @@ static void MakeSortedSaveGameList(void)
 
			case FIOS_TYPE_DRIVE:  sort_end++;   break;
 
		}
 
	}
 

	
 
	s_amount = _fios_num - sort_start - sort_end;
 
	if (s_amount > 0)
 
		qsort(_fios_list + sort_start, s_amount, sizeof(FiosItem), compare_FiosItems);
 
}
 

	
 
static void GenerateFileName(void)
 
{
 
	/* Check if we are not a specatator who wants to generate a name..
 
	    Let's use the name of player #0 for now. */
 
	const Player* p = GetPlayer(_local_player < MAX_PLAYERS ? _local_player : 0);
 

	
 
	SetDParam(0, p->name_1);
 
	SetDParam(1, p->name_2);
 
	SetDParam(2, _date);
 
	GetString(_edit_str_buf, STR_4004);
 
}
 

	
 
extern void StartupEngines(void);
 

	
 
static void SaveLoadDlgWndProc(Window *w, WindowEvent *e)
 
{
 
	static FiosItem o_dir;
 

	
 
	switch (e->event) {
 
	case WE_CREATE: { /* Set up OPENTTD button */
 
		o_dir.type = FIOS_TYPE_DIRECT;
 
		switch (_saveload_mode) {
 
			case SLD_SAVE_GAME:
 
			case SLD_LOAD_GAME:
 
				ttd_strlcpy(&o_dir.name[0], _path.save_dir, sizeof(o_dir.name));
 
				break;
 

	
 
			case SLD_SAVE_SCENARIO:
 
			case SLD_LOAD_SCENARIO:
 
				ttd_strlcpy(&o_dir.name[0], _path.scenario_dir, sizeof(o_dir.name));
 
				break;
 

	
 
			default:
 
				ttd_strlcpy(&o_dir.name[0], _path.personal_dir, sizeof(o_dir.name));
 
		}
 
		break;
 
		}
 

	
 
	case WE_PAINT: {
 
		int y,pos;
 
		const FiosItem *item;
 
		int pos;
 
		int y;
 

	
 
		SetVScrollCount(w, _fios_num);
 
		DrawWindowWidgets(w);
 
		DrawFiosTexts(w->width);
 

	
 
		if (_savegame_sort_dirty) {
 
			_savegame_sort_dirty = false;
 
			MakeSortedSaveGameList();
 
		}
 

	
 
		GfxFillRect(w->widget[7].left + 1, w->widget[7].top + 1, w->widget[7].right, w->widget[7].bottom, 0xD7);
 
		DoDrawString(
 
			_savegame_sort_order & SORT_DESCENDING ? DOWNARROW : UPARROW,
 
			_savegame_sort_order & SORT_BY_NAME ? w->widget[2].right - 9 : w->widget[3].right - 9,
 
			15, 16
 
		);
 

	
 
		y = w->widget[7].top + 1;
 
		pos = w->vscroll.pos;
 
		while (pos < _fios_num) {
 
			item = _fios_list + pos;
 
		for (pos = w->vscroll.pos; pos < _fios_num; pos++) {
 
			const FiosItem* item = _fios_list + pos;
 

	
 
			DoDrawStringTruncated(item->title, 4, y, _fios_colors[item->type], w->width - 18);
 
			pos++;
 
			y += 10;
 
			if (y >= w->vscroll.cap * 10 + w->widget[7].top + 1) break;
 
		}
 

	
 
		if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
 
			DrawEditBox(w, &WP(w,querystr_d), 10);
 
		}
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		switch (e->click.widget) {
 
		case 2: /* Sort save names by name */
 
			_savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ?
 
				SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME;
 
			_savegame_sort_dirty = true;
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 3: /* Sort save names by date */
 
			_savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ?
 
				SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE;
 
			_savegame_sort_dirty = true;
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 6: /* OpenTTD 'button', jumps to OpenTTD directory */
 
			FiosBrowseTo(&o_dir);
 
			SetWindowDirty(w);
 
			BuildFileList();
 
			break;
 

	
 
		case 7: { /* Click the listbox */
 
			int y = (e->click.pt.y - w->widget[e->click.widget].top - 1) / 10;
 
			char *name;
 
			const FiosItem *file;
 

	
 
			if (y < 0 || (y += w->vscroll.pos) >= w->vscroll.count) return;
 

	
 
			file = _fios_list + y;
 

	
 
			name = FiosBrowseTo(file);
 
			if (name != NULL) {
 
				if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) {
 
					_switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD;
 

	
 
					SetFiosType(file->type);
 
					ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
 
					ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
 

	
 
					DeleteWindow(w);
 
				} else {
 
					// SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox
 
					ttd_strlcpy(WP(w, querystr_d).text.buf, file->title, WP(w, querystr_d).text.maxlength);
 
					UpdateTextBufferSize(&WP(w, querystr_d).text);
 
					InvalidateWidget(w, 10);
 
				}
 
			} else {
music/qtmidi.c
Show inline comments
 
@@ -21,278 +21,273 @@
 

	
 
/*
 
 * OpenTTD includes.
 
 */
 
#define  WindowClass OSX_WindowClass
 
#include <QuickTime/QuickTime.h>
 
#undef   WindowClass
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "qtmidi.h"
 

	
 
/*
 
 * System includes. We need to workaround with some defines because there's
 
 * stuff already defined in QuickTime headers.
 
 */
 
#define  OTTD_Random OSX_OTTD_Random
 
#undef   OTTD_Random
 
#undef   WindowClass
 
#undef   SL_ERROR
 
#undef   bool
 

	
 
#include <assert.h>
 
#include <unistd.h>
 
#include <fcntl.h>
 

	
 
// we need to include debug.h after CoreServices because defining DEBUG will break CoreServices in OSX 10.2
 
#include "../debug.h"
 

	
 

	
 
enum {
 
	midiType = 'Midi' /**< OSType code for MIDI songs. */
 
};
 

	
 

	
 
/**
 
 * Converts a Unix-like pathname to a @c FSSpec structure which may be
 
 * used with functions from several MacOS X frameworks (Carbon, QuickTime,
 
 * etc). The pointed file or directory must exist.
 
 *
 
 * @param *path A string containing a Unix-like path.
 
 * @param *spec Pointer to a @c FSSpec structure where the result will be
 
 *              stored.
 
 * @return Wether the conversion was successful.
 
 */
 
static bool PathToFSSpec(const char *path, FSSpec *spec)
 
{
 
	FSRef ref;
 
	assert(spec);
 
	assert(path);
 
	assert(spec != NULL);
 
	assert(path != NULL);
 

	
 
	if (noErr != FSPathMakeRef((UInt8*) path, &ref, NULL))
 
		return false;
 

	
 
	return (noErr ==
 
			FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL));
 
	return
 
		FSPathMakeRef((UInt8*)path, &ref, NULL) == noErr &&
 
		FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL) == noErr;
 
}
 

	
 

	
 
/**
 
 * Sets the @c OSType of a given file to @c 'Midi', but only if it's not
 
 * already set.
 
 *
 
 * @param *spec A @c FSSpec structure referencing a file.
 
 */
 
static void SetMIDITypeIfNeeded(const FSSpec *spec)
 
{
 
	FInfo info;
 
	assert(spec);
 

	
 
	if (noErr != FSpGetFInfo(spec, &info)) return;
 

	
 
	/* Set file type to 'Midi' if the file is _not_ an alias. */
 
	if ((info.fdType != midiType) && !(info.fdFlags & kIsAlias)) {
 
	if (info.fdType != midiType && !(info.fdFlags & kIsAlias)) {
 
		info.fdType = midiType;
 
		FSpSetFInfo(spec, &info);
 
		DEBUG(driver, 3) ("qtmidi: changed filetype to 'Midi'");
 
	}
 
}
 

	
 

	
 
/**
 
 * Loads a MIDI file and returns it as a QuickTime Movie structure.
 
 *
 
 * @param *path String with the path of an existing MIDI file.
 
 * @param *moov Pointer to a @c Movie where the result will be stored.
 
 * @return Wether the file was loaded and the @c Movie successfully created.
 
 */
 
static bool LoadMovieForMIDIFile(const char *path, Movie *moov)
 
{
 
	int fd;
 
	int ret;
 
	char magic[4];
 
	FSSpec fsspec;
 
	short refnum = 0;
 
	short resid  = 0;
 

	
 
	assert(path);
 
	assert(moov);
 
	assert(path != NULL);
 
	assert(moov != NULL);
 

	
 
	DEBUG(driver, 2) ("qtmidi: begin loading '%s'...", path);
 

	
 
	/*
 
	 * XXX Manual check for MIDI header ('MThd'), as I don't know how to make
 
	 * QuickTime load MIDI files without a .mid suffix without knowing it's
 
	 * a MIDI file and setting the OSType of the file to the 'Midi' value.
 
	 * Perhahaps ugly, but it seems that it does the Right Thing(tm).
 
	 */
 
	if ((fd = open(path, O_RDONLY, 0)) == -1)
 
		return false;
 
	fd = open(path, O_RDONLY, 0);
 
	if (fd == -1) return false;
 
	ret = read(fd, magic, 4);
 
	close(fd);
 
	if (ret < 4) return false;
 

	
 
	DEBUG(driver, 3) ("qtmidi: header is '%c%c%c%c'",
 
			magic[0], magic[1], magic[2], magic[3]);
 
	DEBUG(driver, 3) ("qtmidi: header is '%.4s'", magic);
 
	if (magic[0] != 'M' || magic[1] != 'T' || magic[2] != 'h' || magic[3] != 'd')
 
		return false;
 

	
 
	if (!PathToFSSpec(path, &fsspec))
 
		return false;
 
	if (!PathToFSSpec(path, &fsspec)) return false;
 
	SetMIDITypeIfNeeded(&fsspec);
 

	
 
	if (noErr != OpenMovieFile(&fsspec, &refnum, fsRdPerm))
 
		return false;
 
	if (OpenMovieFile(&fsspec, &refnum, fsRdPerm) != noErr) return false;
 
	DEBUG(driver, 1) ("qtmidi: '%s' successfully opened", path);
 

	
 
	if (noErr != NewMovieFromFile(moov, refnum, &resid, NULL,
 
				newMovieActive | newMovieDontAskUnresolvedDataRefs, NULL))
 
	{
 
		CloseMovieFile(refnum);
 
		return false;
 
	}
 
	DEBUG(driver, 2) ("qtmidi: movie container created");
 

	
 
	CloseMovieFile(refnum);
 
	return true;
 
}
 

	
 

	
 
/**
 
 * Flag which has the @c true value when QuickTime is available and
 
 * initialized.
 
 */
 
static bool _quicktime_started = false;
 

	
 

	
 
/**
 
 * Initialize QuickTime if needed. This function sets the
 
 * #_quicktime_started flag to @c true if QuickTime is present in the system
 
 * and it was initialized properly.
 
 */
 
static void InitQuickTimeIfNeeded(void)
 
{
 
	OSStatus dummy;
 

	
 
	if (_quicktime_started) return;
 

	
 
	DEBUG(driver, 2) ("qtmidi: trying to initialize Quicktime");
 
	/* Be polite: check wether QuickTime is available and initialize it. */
 
	_quicktime_started =
 
		(noErr == Gestalt(gestaltQuickTime, &dummy)) &&
 
		(noErr == EnterMovies());
 
	DEBUG(driver, 1) ("qtmidi: Quicktime was %s initialized",
 
			_quicktime_started ? "successfully" : "NOT");
 
		_quicktime_started ? "successfully" : "NOT"
 
	);
 
}
 

	
 

	
 
/** Possible states of the QuickTime music driver. */
 
enum
 
{
 
enum {
 
	QT_STATE_IDLE, /**< No file loaded. */
 
	QT_STATE_PLAY, /**< File loaded, playing. */
 
	QT_STATE_STOP, /**< File loaded, stopped. */
 
};
 

	
 

	
 
static Movie _quicktime_movie;                  /**< Current QuickTime @c Movie. */
 
static byte  _quicktime_volume = 127;           /**< Current volume. */
 
static int   _quicktime_state  = QT_STATE_IDLE; /**< Current player state. */
 

	
 

	
 
/**
 
 * Maps OpenTTD volume to QuickTime notion of volume.
 
 */
 
#define VOLUME  ((short)((0x00FF & _quicktime_volume) << 1))
 

	
 

	
 
static void StopSong(void);
 

	
 

	
 
/**
 
 * Initialized the MIDI player, including QuickTime initialization.
 
 *
 
 * @todo Give better error messages by inspecting error codes returned by
 
 * @c Gestalt() and @c EnterMovies(). Needs changes in
 
 * #InitQuickTimeIfNeeded.
 
 */
 
static const char* StartDriver(const char * const *parm)
 
{
 
	InitQuickTimeIfNeeded();
 
	return (_quicktime_started) ? NULL : "can't initialize QuickTime";
 
}
 

	
 

	
 
/**
 
 * Checks wether the player is active.
 
 *
 
 * This function is called at regular intervals from OpenTTD's main loop, so
 
 * we call @c MoviesTask() from here to let QuickTime do its work.
 
 */
 
static bool SongIsPlaying(void)
 
{
 
	if (!_quicktime_started) return true;
 

	
 
	switch (_quicktime_state) {
 
		case QT_STATE_IDLE:
 
		case QT_STATE_STOP:
 
			/* Do nothing. */
 
			break;
 
		case QT_STATE_PLAY:
 
			MoviesTask(_quicktime_movie, 0);
 
			/* Check wether movie ended. */
 
			if (IsMovieDone(_quicktime_movie) ||
 
					(GetMovieTime(_quicktime_movie, NULL) >=
 
					 GetMovieDuration(_quicktime_movie)))
 
				_quicktime_state = QT_STATE_STOP;
 
	}
 

	
 
	return (_quicktime_state == QT_STATE_PLAY);
 
	return _quicktime_state == QT_STATE_PLAY;
 
}
 

	
 

	
 
/**
 
 * Stops the MIDI player.
 
 *
 
 * Stops playing and frees any used resources before returning. As it
 
 * deinitilizes QuickTime, the #_quicktime_started flag is set to @c false.
 
 */
 
static void StopDriver(void)
 
{
 
	if (!_quicktime_started) return;
 

	
 
	DEBUG(driver, 2) ("qtmidi: trying to stop driver...");
 
	switch (_quicktime_state) {
 
		case QT_STATE_IDLE:
 
			DEBUG(driver, 3) ("qtmidi: nothing to do (already idle)");
 
			/* Do nothing. */
 
			break;
 
		case QT_STATE_PLAY:
 
			StopSong();
 
		case QT_STATE_STOP:
 
			DisposeMovie(_quicktime_movie);
 
	}
 

	
 
	ExitMovies();
 
	_quicktime_started = false;
 
	DEBUG(driver, 1) ("qtmidi: driver successfully stopped");
 
}
 

	
 

	
 
/**
 
 * Starts playing a new song.
 
 *
 
 * @param filename Path to a MIDI file.
 
 */
 
static void PlaySong(const char *filename)
 
{
 
	if (!_quicktime_started) return;
 

	
 
	DEBUG(driver, 3) ("qtmidi: request playing of '%s'n", filename);
 
	switch (_quicktime_state) {
 
		case QT_STATE_PLAY:
 
			StopSong();
 
			DEBUG(driver, 2) ("qtmidi: previous tune stopped");
 
			/* XXX Fall-through -- no break needed. */
 
		case QT_STATE_STOP:
 
			DisposeMovie(_quicktime_movie);
network.c
Show inline comments
 
@@ -1197,101 +1197,99 @@ static void NetworkHandleLocalQueue(void
 
static bool NetworkDoClientLoop(void)
 
{
 
	_frame_counter++;
 

	
 
	NetworkHandleLocalQueue();
 

	
 
	StateGameLoop();
 

	
 
	// Check if we are in sync!
 
	if (_sync_frame != 0) {
 
		if (_sync_frame == _frame_counter) {
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
			if (_sync_seed_1 != _random_seeds[0][0] || _sync_seed_2 != _random_seeds[0][1]) {
 
#else
 
			if (_sync_seed_1 != _random_seeds[0][0]) {
 
#endif
 
				NetworkError(STR_NETWORK_ERR_DESYNC);
 
				DEBUG(net, 0)("[NET] Sync error detected!");
 
				NetworkClientError(NETWORK_RECV_STATUS_DESYNC, DEREF_CLIENT(0));
 
				return false;
 
			}
 

	
 
			// If this is the first time we have a sync-frame, we
 
			//   need to let the server know that we are ready and at the same
 
			//   frame as he is.. so we can start playing!
 
			if (_network_first_time) {
 
				_network_first_time = false;
 
				SEND_COMMAND(PACKET_CLIENT_ACK)();
 
			}
 

	
 
			_sync_frame = 0;
 
		} else if (_sync_frame < _frame_counter) {
 
			DEBUG(net, 1)("[NET] Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter);
 
			_sync_frame = 0;
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
// We have to do some UDP checking
 
void NetworkUDPGameLoop(void)
 
{
 
	if (_network_udp_server) {
 
		NetworkUDPReceive(_udp_server_socket);
 
		if (_udp_master_socket != INVALID_SOCKET) {
 
			NetworkUDPReceive(_udp_master_socket);
 
		}
 
	}
 
	else if (_udp_client_socket != INVALID_SOCKET) {
 
	} else if (_udp_client_socket != INVALID_SOCKET) {
 
		NetworkUDPReceive(_udp_client_socket);
 
		if (_network_udp_broadcast > 0)
 
			_network_udp_broadcast--;
 
		if (_network_udp_broadcast > 0) _network_udp_broadcast--;
 
	}
 
}
 

	
 
// The main loop called from ttd.c
 
//  Here we also have to do StateGameLoop if needed!
 
void NetworkGameLoop(void)
 
{
 
	if (!_networking) return;
 

	
 
	if (!NetworkReceive()) return;
 

	
 
	if (_network_server) {
 
		bool send_frame = false;
 

	
 
		// We first increase the _frame_counter
 
		_frame_counter++;
 
		// Update max-frame-counter
 
		if (_frame_counter > _frame_counter_max) {
 
			_frame_counter_max = _frame_counter + _network_frame_freq;
 
			send_frame = true;
 
		}
 

	
 
		NetworkHandleLocalQueue();
 

	
 
		// Then we make the frame
 
		StateGameLoop();
 

	
 
		_sync_seed_1 = _random_seeds[0][0];
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
		_sync_seed_2 = _random_seeds[0][1];
 
#endif
 

	
 
		NetworkServer_Tick(send_frame);
 
	} else {
 
		// Client
 

	
 
		// Make sure we are at the frame were the server is (quick-frames)
 
		if (_frame_counter_server > _frame_counter) {
 
			while (_frame_counter_server > _frame_counter) {
 
				if (!NetworkDoClientLoop()) break;
 
			}
 
		} else {
 
			// Else, keep on going till _frame_counter_max
 
			if (_frame_counter_max > _frame_counter) {
 
				NetworkDoClientLoop();
 
			}
 
		}
 
	}
news_gui.c
Show inline comments
 
@@ -176,111 +176,108 @@ static void NewsWindowProc(Window *w, Wi
 
		case 0: {
 
			NewsItem *ni = WP(w, news_d).ni;
 
			if (ni->flags & NF_VEHICLE) {
 
				Vehicle *v = GetVehicle(ni->data_a);
 
				ScrollMainWindowTo(v->x_pos, v->y_pos);
 
			} else if (ni->flags & NF_TILE) {
 
				if (!ScrollMainWindowToTile(ni->data_a) && ni->data_b != 0)
 
					ScrollMainWindowToTile(ni->data_b);
 
			}
 
		} break;
 
		}
 
	} break;
 

	
 
	case WE_KEYPRESS:
 
		if (e->keypress.keycode == WKC_SPACE) {
 
			// Don't continue.
 
			e->keypress.cont = false;
 
			DeleteWindow(w);
 
		}
 
		break;
 

	
 
	case WE_MESSAGE: /* The chatbar has notified us that is was either created or closed */
 
		switch (e->message.msg) {
 
			case WE_CREATE: w->message.msg = e->message.wparam; break;
 
			case WE_DESTROY: w->message.msg = 0; break;
 
		}
 
		break;
 

	
 
	case WE_TICK: { /* Scroll up newsmessages from the bottom in steps of 4 pixels */
 
		int diff;
 
		int y = max(w->top - 4, _screen.height - w->height - 12 - w->message.msg);
 
		if (y == w->top) return;
 

	
 
		if (w->viewport != NULL)
 
			w->viewport->top += y - w->top;
 

	
 
		diff = abs(w->top - y);
 
		w->top = y;
 

	
 
		SetDirtyBlocks(w->left, w->top - diff, w->left + w->width, w->top + w->height);
 
	} break;
 
	}
 
}
 

	
 
// returns the correct index in the array
 
// (to deal with overflows)
 
static byte increaseIndex(byte i)
 
{
 
	if (i == INVALID_NEWS)
 
		return 0;
 
	if (i == INVALID_NEWS) return 0;
 
	i++;
 
	if (i >= MAX_NEWS)
 
		i = i % MAX_NEWS;
 
	if (i >= MAX_NEWS) i = i % MAX_NEWS;
 
	return i;
 
}
 

	
 
void AddNewsItem(StringID string, uint32 flags, uint data_a, uint data_b)
 
{
 
	NewsItem *ni;
 
	Window *w;
 

	
 
	if (_game_mode == GM_MENU)
 
		return;
 
	if (_game_mode == GM_MENU) return;
 

	
 
	// check the rare case that the oldest (to be overwritten) news item is open
 
	if (_total_news==MAX_NEWS && (_oldest_news == _current_news || _oldest_news == _forced_news))
 
		MoveToNexItem();
 

	
 
	_forced_news = INVALID_NEWS;
 
	if (_total_news < MAX_NEWS) _total_news++;
 

	
 
	// make sure our pointer isn't overflowing
 
	_latest_news = increaseIndex(_latest_news);
 

	
 
	// overwrite oldest news entry
 
	if (_oldest_news == _latest_news && _news_items[_oldest_news].string_id != 0)
 
		_oldest_news = increaseIndex(_oldest_news); // but make sure we're not overflowing here
 

	
 
	// add news to _latest_news
 
	ni = &_news_items[_latest_news];
 
	memset(ni, 0, sizeof(*ni));
 

	
 
	ni->string_id = string;
 
	ni->display_mode = (byte)flags;
 
	ni->flags = (byte)(flags >> 8) | NF_NOEXPIRE;
 

	
 
	// show this news message in color?
 
	if (_date >= ConvertIntDate(_patches.colored_news_date))
 
		ni->flags |= NF_INCOLOR;
 

	
 
	ni->type = (byte)(flags >> 16);
 
	ni->callback = (byte)(flags >> 24);
 
	ni->data_a = data_a;
 
	ni->data_b = data_b;
 
	ni->date = _date;
 
	COPY_OUT_DPARAM(ni->params, 0, lengthof(ni->params));
 

	
 
	w = FindWindowById(WC_MESSAGE_HISTORY, 0);
 
	if (w == NULL) return;
 
	SetWindowDirty(w);
 
	w->vscroll.count = _total_news;
 
}
 

	
 

	
 
// don't show item if it's older than x days
 
static const byte _news_items_age[] = {60, 60, 90, 60, 90, 30, 150, 30, 90, 180};
 

	
 
static const Widget _news_type13_widgets[] = {
 
{      WWT_PANEL,   RESIZE_NONE,    15,     0,   429,     0,   169, 0x0, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,    15,     0,    10,     0,    11, 0x0, STR_NULL},
 
{   WIDGETS_END},
npf.c
Show inline comments
 
@@ -232,98 +232,97 @@ static void NPFMarkTile(TileIndex tile)
 
				MarkTileDirtyByTile(tile);
 
			}
 
			break;
 

	
 
		default:
 
			break;
 
	}
 
#endif
 
}
 

	
 
static int32 NPFWaterPathCost(AyStar* as, AyStarNode* current, OpenListNode* parent)
 
{
 
	//TileIndex tile = current->tile;
 
	int32 cost = 0;
 
	Trackdir trackdir = (Trackdir)current->direction;
 

	
 
	cost = _trackdir_length[trackdir]; /* Should be different for diagonal tracks */
 

	
 
	if (IsBuoyTile(current->tile) && IsDiagonalTrackdir(trackdir))
 
		cost += _patches.npf_buoy_penalty; /* A small penalty for going over buoys */
 

	
 
	if (current->direction != NextTrackdir((Trackdir)parent->path.node.direction))
 
		cost += _patches.npf_water_curve_penalty;
 

	
 
	/* TODO More penalties? */
 

	
 
	return cost;
 
}
 

	
 
/* Determine the cost of this node, for road tracks */
 
static int32 NPFRoadPathCost(AyStar* as, AyStarNode* current, OpenListNode* parent)
 
{
 
	TileIndex tile = current->tile;
 
	int32 cost = 0;
 

	
 
	/* Determine base length */
 
	switch (GetTileType(tile)) {
 
		case MP_TUNNELBRIDGE:
 
			if (IsTunnel(tile)) {
 
				cost = NPFTunnelCost(current);
 
			} else {
 
				cost = NPF_TILE_LENGTH;
 
			}
 
			break;
 

	
 
		case MP_STREET:
 
			cost = NPF_TILE_LENGTH;
 
			/* Increase the cost for level crossings */
 
			if (IsLevelCrossing(tile))
 
				cost += _patches.npf_crossing_penalty;
 
			if (IsLevelCrossing(tile)) cost += _patches.npf_crossing_penalty;
 
			break;
 

	
 
		default:
 
			break;
 
	}
 

	
 
	/* Determine extra costs */
 

	
 
	/* Check for slope */
 
	cost += NPFSlopeCost(current);
 

	
 
	/* Check for turns. Road vehicles only really drive diagonal, turns are
 
	 * represented by non-diagonal tracks */
 
	if (!IsDiagonalTrackdir(current->direction))
 
		cost += _patches.npf_road_curve_penalty;
 

	
 
	NPFMarkTile(tile);
 
	DEBUG(npf, 4)("Calculating G for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), cost);
 
	return cost;
 
}
 

	
 

	
 
/* Determine the cost of this node, for railway tracks */
 
static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* parent)
 
{
 
	TileIndex tile = current->tile;
 
	Trackdir trackdir = (Trackdir)current->direction;
 
	int32 cost = 0;
 
	/* HACK: We create a OpenListNode manually, so we can call EndNodeCheck */
 
	OpenListNode new_node;
 

	
 
	/* Determine base length */
 
	switch (GetTileType(tile)) {
 
		case MP_TUNNELBRIDGE:
 
			if (IsTunnel(tile)) {
 
				cost = NPFTunnelCost(current);
 
				break;
 
			}
 
			/* Fall through if above if is false, it is a bridge
 
			 * then. We treat that as ordinary rail */
 

	
 
		case MP_RAILWAY:
 
			cost = _trackdir_length[trackdir]; /* Should be different for diagonal tracks */
 
			break;
 

	
 
		case MP_STREET: /* Railway crossing */
 
			cost = NPF_TILE_LENGTH;
 
			break;
 
@@ -362,105 +361,100 @@ static int32 NPFRailPathCost(AyStar* as,
 
					cost += _patches.npf_rail_firstred_penalty;
 
				}
 
			}
 
			/* Record the state of this signal */
 
			NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true);
 
		} else {
 
			/* Record the state of this signal */
 
			NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false);
 
		}
 
		NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true);
 
	}
 

	
 
	/* Penalise the tile if it is a target tile and the last signal was
 
	 * red */
 
	/* HACK: We create a new_node here so we can call EndNodeCheck. Ugly as hell
 
	 * of course... */
 
	new_node.path.node = *current;
 
	if (as->EndNodeCheck(as, &new_node) == AYSTAR_FOUND_END_NODE && NPFGetFlag(current, NPF_FLAG_LAST_SIGNAL_RED))
 
		cost += _patches.npf_rail_lastred_penalty;
 

	
 
	/* Check for slope */
 
	cost += NPFSlopeCost(current);
 

	
 
	/* Check for turns */
 
	if (current->direction != NextTrackdir((Trackdir)parent->path.node.direction))
 
		cost += _patches.npf_rail_curve_penalty;
 
	//TODO, with realistic acceleration, also the amount of straight track between
 
	//      curves should be taken into account, as this affects the speed limit.
 

	
 
	/* Check for reverse in depot */
 
	if (IsTileDepotType(tile, TRANSPORT_RAIL) && as->EndNodeCheck(as, &new_node) != AYSTAR_FOUND_END_NODE) {
 
		/* Penalise any depot tile that is not the last tile in the path. This
 
		 * _should_ penalise every occurence of reversing in a depot (and only
 
		 * that) */
 
		cost += _patches.npf_rail_depot_reverse_penalty;
 
	}
 

	
 
	/* Check for occupied track */
 
	//TODO
 

	
 
	NPFMarkTile(tile);
 
	DEBUG(npf, 4)("Calculating G for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), cost);
 
	return cost;
 
}
 

	
 
/* Will find any depot */
 
static int32 NPFFindDepot(AyStar* as, OpenListNode *current)
 
{
 
	TileIndex tile = current->path.node.tile;
 

	
 
	/* It's not worth caching the result with NPF_FLAG_IS_TARGET here as below,
 
	 * since checking the cache not that much faster than the actual check */
 
	if (IsTileDepotType(tile, as->user_data[NPF_TYPE])) {
 
		return AYSTAR_FOUND_END_NODE;
 
	} else {
 
		return AYSTAR_DONE;
 
	}
 
	return IsTileDepotType(current->path.node.tile, as->user_data[NPF_TYPE]) ?
 
		AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
 
}
 

	
 
/* Will find a station identified using the NPFFindStationOrTileData */
 
static int32 NPFFindStationOrTile(AyStar* as, OpenListNode *current)
 
{
 
	NPFFindStationOrTileData* fstd = (NPFFindStationOrTileData*)as->user_target;
 
	AyStarNode *node = &current->path.node;
 
	TileIndex tile = node->tile;
 

	
 
	/* If GetNeighbours said we could get here, we assume the station type
 
	 * is correct */
 
	if (
 
		(fstd->station_index == INVALID_STATION && tile == fstd->dest_coords) || /* We've found the tile, or */
 
		(IsTileType(tile, MP_STATION) && GetStationIndex(tile) == fstd->station_index) /* the station */
 
	) {
 
		return AYSTAR_FOUND_END_NODE;
 
	} else {
 
		return AYSTAR_DONE;
 
	}
 
}
 

	
 
/* To be called when current contains the (shortest route to) the target node.
 
 * Will fill the contents of the NPFFoundTargetData using
 
 * AyStarNode[NPF_TRACKDIR_CHOICE].
 
 */
 
static void NPFSaveTargetData(AyStar* as, OpenListNode* current)
 
{
 
	NPFFoundTargetData* ftd = (NPFFoundTargetData*)as->user_path;
 
	ftd->best_trackdir = (Trackdir)current->path.node.user_data[NPF_TRACKDIR_CHOICE];
 
	ftd->best_path_dist = current->g;
 
	ftd->best_bird_dist = 0;
 
	ftd->node = current->path.node;
 
}
 

	
 
/**
 
 * Finds out if a given player's vehicles are allowed to enter a given tile.
 
 * @param owner    The owner of the vehicle.
 
 * @param tile     The tile that is about to be entered.
 
 * @param enterdir The direction from which the vehicle wants to enter the tile.
 
 * @return         true if the vehicle can enter the tile.
 
 * @todo           This function should be used in other places than just NPF,
 
 *                 maybe moved to another file too.
 
 */
 
static bool VehicleMayEnterTile(Owner owner, TileIndex tile, DiagDirection enterdir)
 
{
 
	if (IsTileType(tile, MP_RAILWAY) ||           /* Rail tile (also rail depot) */
 
			IsRailwayStationTile(tile) ||               /* Rail station tile */
 
			IsTileDepotType(tile, TRANSPORT_ROAD) ||  /* Road depot tile */
 
@@ -637,104 +631,102 @@ static void NPFFollowTrack(AyStar* aysta
 
	i = 0;
 
	/* Enumerate possible track */
 
	while (trackdirbits != 0) {
 
		Trackdir dst_trackdir;
 
		dst_trackdir =  FindFirstBit2x64(trackdirbits);
 
		trackdirbits = KillFirstBit2x64(trackdirbits);
 
		DEBUG(npf, 5)("Expanded into trackdir: %d, remaining trackdirs: 0x%X", dst_trackdir, trackdirbits);
 

	
 
		/* Check for oneway signal against us */
 
		if (IsTileType(dst_tile, MP_RAILWAY) && GetRailTileType(dst_tile) == RAIL_TILE_SIGNALS) {
 
			if (HasSignalOnTrackdir(dst_tile, ReverseTrackdir(dst_trackdir)) && !HasSignalOnTrackdir(dst_tile, dst_trackdir))
 
				// if one way signal not pointing towards us, stop going in this direction.
 
				break;
 
		}
 
		{
 
			/* We've found ourselves a neighbour :-) */
 
			AyStarNode* neighbour = &aystar->neighbours[i];
 
			neighbour->tile = dst_tile;
 
			neighbour->direction = dst_trackdir;
 
			/* Save user data */
 
			neighbour->user_data[NPF_NODE_FLAGS] = current->path.node.user_data[NPF_NODE_FLAGS];
 
			NPFFillTrackdirChoice(neighbour, current);
 
		}
 
		i++;
 
	}
 
	aystar->num_neighbours = i;
 
}
 

	
 
/*
 
 * Plan a route to the specified target (which is checked by target_proc),
 
 * from start1 and if not NULL, from start2 as well. The type of transport we
 
 * are checking is in type. reverse_penalty is applied to all routes that
 
 * originate from the second start node.
 
 * When we are looking for one specific target (optionally multiple tiles), we
 
 * should use a good heuristic to perform aystar search. When we search for
 
 * multiple targets that are spread around, we should perform a breadth first
 
 * search by specifiying CalcZero as our heuristic.
 
 */
 
static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start2, NPFFindStationOrTileData* target, AyStar_EndNodeCheck target_proc, AyStar_CalculateH heuristic_proc, TransportType type, Owner owner, RailTypeMask railtypes, uint reverse_penalty)
 
{
 
	int r;
 
	NPFFoundTargetData result;
 

	
 
	/* Initialize procs */
 
	_npf_aystar.CalculateH = heuristic_proc;
 
	_npf_aystar.EndNodeCheck = target_proc;
 
	_npf_aystar.FoundEndNode = NPFSaveTargetData;
 
	_npf_aystar.GetNeighbours = NPFFollowTrack;
 
	if (type == TRANSPORT_RAIL)
 
		_npf_aystar.CalculateG = NPFRailPathCost;
 
	else if (type == TRANSPORT_ROAD)
 
		_npf_aystar.CalculateG = NPFRoadPathCost;
 
	else if (type == TRANSPORT_WATER)
 
		_npf_aystar.CalculateG = NPFWaterPathCost;
 
	else
 
		assert(0);
 
	switch (type) {
 
		default: NOT_REACHED();
 
		case TRANSPORT_RAIL:  _npf_aystar.CalculateG = NPFRailPathCost;  break;
 
		case TRANSPORT_ROAD:  _npf_aystar.CalculateG = NPFRoadPathCost;  break;
 
		case TRANSPORT_WATER: _npf_aystar.CalculateG = NPFWaterPathCost; break;
 
	}
 

	
 
	/* Initialize Start Node(s) */
 
	start1->user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 
	start1->user_data[NPF_NODE_FLAGS] = 0;
 
	_npf_aystar.addstart(&_npf_aystar, start1, 0);
 
	if (start2) {
 
		start2->user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 
		start2->user_data[NPF_NODE_FLAGS] = 0;
 
		NPFSetFlag(start2, NPF_FLAG_REVERSE, true);
 
		_npf_aystar.addstart(&_npf_aystar, start2, reverse_penalty);
 
	}
 

	
 
	/* Initialize result */
 
	result.best_bird_dist = (uint)-1;
 
	result.best_path_dist = (uint)-1;
 
	result.best_trackdir = INVALID_TRACKDIR;
 
	_npf_aystar.user_path = &result;
 

	
 
	/* Initialize target */
 
	_npf_aystar.user_target = target;
 

	
 
	/* Initialize user_data */
 
	_npf_aystar.user_data[NPF_TYPE] = type;
 
	_npf_aystar.user_data[NPF_OWNER] = owner;
 
	_npf_aystar.user_data[NPF_RAILTYPES] = railtypes;
 

	
 
	/* GO! */
 
	r = AyStarMain_Main(&_npf_aystar);
 
	assert(r != AYSTAR_STILL_BUSY);
 

	
 
	if (result.best_bird_dist != 0) {
 
		if (target != NULL) {
 
			DEBUG(npf, 1) ("Could not find route to tile 0x%X from 0x%X.", target->dest_coords, start1->tile);
 
		} else {
 
			/* Assumption: target == NULL, so we are looking for a depot */
 
			DEBUG(npf, 1) ("Could not find route to a depot from tile 0x%X.", start1->tile);
 
		}
 

	
 
	}
 
	return result;
 
}
 

	
 
NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypeMask railtypes)
 
{
 
	AyStarNode start1;
 
	AyStarNode start2;
 

	
 
	start1.tile = tile1;
oldloader.c
Show inline comments
 
@@ -143,102 +143,101 @@ static byte ReadByte(LoadgameState *ls)
 
{
 
	/* Old savegames have a nice compression algorithm (RLE)
 
	which means that we have a chunk, which starts with a length
 
	byte. If that byte is negative, we have to repeat the next byte
 
	that many times (+1). Else, we need to read that amount of bytes.
 
	Works pretty good if you have many zero's behind eachother */
 

	
 
	if (ls->chunk_size == 0) {
 
		/* Read new chunk */
 
		int8 new_byte = ReadByteFromFile(ls);
 

	
 
		if (new_byte < 0) {
 
			/* Repeat next char for new_byte times */
 
			ls->decoding    = true;
 
			ls->decode_char = ReadByteFromFile(ls);
 
			ls->chunk_size  = -new_byte + 1;
 
		} else {
 
			ls->decoding    = false;
 
			ls->chunk_size  = new_byte + 1;
 
		}
 
	}
 

	
 
	ls->total_read++;
 
	ls->chunk_size--;
 

	
 
	return ls->decoding ? ls->decode_char : ReadByteFromFile(ls);
 
}
 

	
 
static inline uint16 ReadUint16(LoadgameState *ls)
 
{
 
	byte x = ReadByte(ls);
 
	return x | ReadByte(ls) << 8;
 
}
 

	
 
static inline uint32 ReadUint32(LoadgameState *ls)
 
{
 
	uint16 x = ReadUint16(ls);
 
	return x | ReadUint16(ls) << 16;
 
}
 

	
 
/**
 
 *
 
 * Loads a chunk from the old savegame
 
 *
 
 */
 
static bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
 
{
 
	const OldChunks *chunk = chunks;
 
	byte *ptr;
 
	byte *base_ptr = base;
 
	uint i;
 

	
 
	while (chunk->type != OC_END) {
 
		ptr = chunk->ptr;
 
		byte* ptr = chunk->ptr;
 
		uint i;
 

	
 
		for (i = 0; i < chunk->amount; i++) {
 
			if (ls->failed) return false;
 

	
 
			/* Handle simple types */
 
			if (GetOldChunkType(chunk->type) != 0) {
 
				switch (GetOldChunkType(chunk->type)) {
 
					/* Just read the byte and forget about it */
 
					case OC_NULL: ReadByte(ls); break;
 

	
 
					case OC_CHUNK:
 
						/* Call function, with 'i' as parameter to tell which item we
 
						 * are going to read */
 
						if (!chunk->proc(ls, i)) return false;
 
						break;
 

	
 
					case OC_ASSERT:
 
						DEBUG(oldloader, 4)("[OldLoader] Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value);
 
						if (ls->total_read != chunk->offset + _bump_assert_value) ls->failed = true;
 
					default: break;
 
				}
 
			} else {
 
				uint32 res = 0;
 

	
 
				/* Reading from the file: bits 16 to 23 have the FILE type */
 
				switch (GetOldChunkFileType(chunk->type)) {
 
					case OC_FILE_I8:  res = (int8)ReadByte(ls); break;
 
					case OC_FILE_U8:  res = ReadByte(ls); break;
 
					case OC_FILE_I16: res = (int16)ReadUint16(ls); break;
 
					case OC_FILE_U16: res = ReadUint16(ls); break;
 
					case OC_FILE_I32: res = (int32)ReadUint32(ls); break;
 
					case OC_FILE_U32: res = ReadUint32(ls); break;
 
					default: NOT_REACHED();
 
				}
 

	
 
				/* Sanity check */
 
				assert(base_ptr != NULL || chunk->ptr != NULL);
 

	
 
				/* Writing to the var: bits 8 to 15 have the VAR type */
 
				if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset;
 

	
 
				/* Write the data */
 
				switch (GetOldChunkVarType(chunk->type)) {
 
					case OC_VAR_I8: *(int8  *)ptr = GB(res, 0, 8); break;
 
					case OC_VAR_U8: *(uint8 *)ptr = GB(res, 0, 8); break;
 
					case OC_VAR_I16:*(int16 *)ptr = GB(res, 0, 16); break;
 
					case OC_VAR_U16:*(uint16*)ptr = GB(res, 0, 16); break;
 
					case OC_VAR_I32:*(int32 *)ptr = res; break;
openttd.c
Show inline comments
 
@@ -968,104 +968,108 @@ void GameLoop(void)
 
	_timer_counter += 8;
 
	CursorTick();
 

	
 
#ifdef ENABLE_NETWORK
 
	// Check for UDP stuff
 
	NetworkUDPGameLoop();
 

	
 
	if (_networking) {
 
		// Multiplayer
 
		NetworkGameLoop();
 
	} else {
 
		if (_network_reconnect > 0 && --_network_reconnect == 0) {
 
			// This means that we want to reconnect to the last host
 
			// We do this here, because it means that the network is really closed
 
			NetworkClientConnectGame(_network_last_host, _network_last_port);
 
		}
 
		// Singleplayer
 
		StateGameLoop();
 
	}
 
#else
 
	StateGameLoop();
 
#endif /* ENABLE_NETWORK */
 

	
 
	if (!_pause && _display_opt & DO_FULL_ANIMATION) DoPaletteAnimations();
 

	
 
	if (!_pause || _cheats.build_in_pause.value) MoveAllTextEffects();
 

	
 
	InputLoop();
 

	
 
	MusicLoop();
 
}
 

	
 
void BeforeSaveGame(void)
 
{
 
	const Window* w = FindWindowById(WC_MAIN_WINDOW, 0);
 

	
 
	if (w != NULL) {
 
		_saved_scrollpos_x = WP(w, const vp_d).scrollpos_x;
 
		_saved_scrollpos_y = WP(w, const vp_d).scrollpos_y;
 
		_saved_scrollpos_zoom = w->viewport->zoom;
 
	}
 
}
 

	
 
static void ConvertTownOwner(void)
 
{
 
	TileIndex tile;
 

	
 
	for (tile = 0; tile != MapSize(); tile++) {
 
		if (IsTileType(tile, MP_STREET)) {
 
		switch (GetTileType(tile)) {
 
			case MP_STREET:
 
			if (IsLevelCrossing(tile) && GetCrossingRoadOwner(tile) & 0x80) {
 
				SetCrossingRoadOwner(tile, OWNER_TOWN);
 
			}
 
				/* FALLTHROUGH */
 

	
 
			case MP_TUNNELBRIDGE:
 
			if (GetTileOwner(tile) & 0x80) SetTileOwner(tile, OWNER_TOWN);
 
		} else if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
			if (GetTileOwner(tile) & 0x80) SetTileOwner(tile, OWNER_TOWN);
 
				break;
 

	
 
			default: break;
 
		}
 
	}
 
}
 

	
 
// before savegame version 4, the name of the company determined if it existed
 
static void CheckIsPlayerActive(void)
 
{
 
	Player* p;
 

	
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->name_1 != 0) p->is_active = true;
 
	}
 
}
 

	
 
// since savegame version 4.1, exclusive transport rights are stored at towns
 
static void UpdateExclusiveRights(void)
 
{
 
	Town* t;
 

	
 
	FOR_ALL_TOWNS(t) {
 
		if (t->xy != 0) t->exclusivity = (byte)-1;
 
	}
 

	
 
	/* FIXME old exclusive rights status is not being imported (stored in s->blocked_months_obsolete)
 
			could be implemented this way:
 
			1.) Go through all stations
 
					Build an array town_blocked[ town_id ][ player_id ]
 
				 that stores if at least one station in that town is blocked for a player
 
			2.) Go through that array, if you find a town that is not blocked for
 
					one player, but for all others, then give him exclusivity.
 
	*/
 
}
 

	
 
static const byte convert_currency[] = {
 
	 0,  1, 12,  8,  3,
 
	10, 14, 19,  4,  5,
 
	 9, 11, 13,  6, 17,
 
	16, 22, 21,  7, 15,
 
	18,  2, 20, };
 

	
 
// since savegame version 4.2 the currencies are arranged differently
 
static void UpdateCurrencies(void)
 
{
 
	_opt.currency = convert_currency[_opt.currency];
 
}
 

	
 
/* Up to revision 1413 the invisible tiles at the southern border have not been
 
 * MP_VOID, even though they should have. This is fixed by this function
order_cmd.c
Show inline comments
 
@@ -144,97 +144,97 @@ void AssignOrder(Order *order, Order dat
 
}
 

	
 

	
 
/**
 
 * Delete all news items regarding defective orders about a vehicle
 
 * This could kill still valid warnings (for example about void order when just
 
 * another order gets added), but assume the player will notice the problems,
 
 * when (s)he's changing the orders.
 
 */
 
static void DeleteOrderWarnings(const Vehicle* v)
 
{
 
	DeleteVehicleNews(v->index, STR_TRAIN_HAS_TOO_FEW_ORDERS  + (v->type - VEH_Train) * 4);
 
	DeleteVehicleNews(v->index, STR_TRAIN_HAS_VOID_ORDER      + (v->type - VEH_Train) * 4);
 
	DeleteVehicleNews(v->index, STR_TRAIN_HAS_DUPLICATE_ENTRY + (v->type - VEH_Train) * 4);
 
	DeleteVehicleNews(v->index, STR_TRAIN_HAS_INVALID_ENTRY   + (v->type - VEH_Train) * 4);
 
}
 

	
 

	
 
/** Add an order to the orderlist of a vehicle.
 
 * @param tile unused
 
 * @param p1 various bitstuffed elements
 
 * - p1 = (bit  0 - 15) - ID of the vehicle
 
 * - p1 = (bit 16 - 31) - the selected order (if any). If the last order is given,
 
 *                        the order will be inserted before that one
 
 *                        only the first 8 bits used currently (bit 16 - 23) (max 255)
 
 * @param p2 packed order to insert
 
 */
 
int32 CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	VehicleID veh   = GB(p1,  0, 16);
 
	OrderID sel_ord = GB(p1, 16, 16);
 
	Order new_order = UnpackOrder(p2);
 

	
 
	if (!IsVehicleIndex(veh)) return CMD_ERROR;
 
	v = GetVehicle(veh);
 
	if (v->type == 0 || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	/* Check if the inserted order is to the correct destination (owner, type),
 
	 * and has the correct flags if any */
 
	switch (new_order.type) {
 
		case OT_GOTO_STATION: {
 
			const Station *st;
 

	
 
			if (!IsStationIndex(new_order.station)) return CMD_ERROR;
 
			st = GetStation(new_order.station);
 

	
 
			if (!IsValidStation(st) ||
 
					(st->airport_type != AT_OILRIG && !(IsBuoy(st)) && !CheckOwnership(st->owner))) {
 
					(st->airport_type != AT_OILRIG && !IsBuoy(st) && !CheckOwnership(st->owner))) {
 
				return CMD_ERROR;
 
			}
 

	
 
			switch (v->type) {
 
				case VEH_Train:
 
					if (!(st->facilities & FACIL_TRAIN)) return CMD_ERROR;
 
					break;
 

	
 
				case VEH_Road:
 
					if (v->cargo_type == CT_PASSENGERS) {
 
						if (!(st->facilities & FACIL_BUS_STOP)) return CMD_ERROR;
 
					} else {
 
						if (!(st->facilities & FACIL_TRUCK_STOP)) return CMD_ERROR;
 
					}
 
					break;
 

	
 
				case VEH_Ship:
 
					if (!(st->facilities & FACIL_DOCK)) return CMD_ERROR;
 
					break;
 

	
 
				case VEH_Aircraft:
 
					if (!(st->facilities & FACIL_AIRPORT)) return CMD_ERROR;
 
					break;
 

	
 
				default: return CMD_ERROR;
 
			}
 

	
 
			/* Order flags can be any of the following for stations:
 
			 * [full-load | unload] [+ transfer] [+ non-stop]
 
			 * non-stop orders (if any) are only valid for trains */
 
			switch (new_order.flags) {
 
				case 0:
 
				case OF_FULL_LOAD:
 
				case OF_FULL_LOAD | OF_TRANSFER:
 
				case OF_UNLOAD:
 
				case OF_UNLOAD | OF_TRANSFER:
 
				case OF_TRANSFER:
 
					break;
 

	
 
				case OF_NON_STOP:
 
				case OF_NON_STOP | OF_FULL_LOAD:
 
				case OF_NON_STOP | OF_FULL_LOAD | OF_TRANSFER:
 
				case OF_NON_STOP | OF_UNLOAD:
 
				case OF_NON_STOP | OF_UNLOAD | OF_TRANSFER:
 
				case OF_NON_STOP | OF_TRANSFER:
 
					if (v->type != VEH_Train) return CMD_ERROR;
 
					break;
 

	
order_gui.c
Show inline comments
 
@@ -423,99 +423,97 @@ static void OrdersWndProc(Window *w, Win
 
				if (xy)
 
					ScrollMainWindowToTile(xy);
 

	
 
				return;
 
			}
 

	
 
			if (sel == WP(w,order_d).sel) sel = -1;
 
			WP(w,order_d).sel = sel;
 
			SetWindowDirty(w);
 
		}	break;
 

	
 
		case 4: /* skip button */
 
			OrderClick_Skip(w, v);
 
			break;
 

	
 
		case 5: /* delete button */
 
			OrderClick_Delete(w, v);
 
			break;
 

	
 
		case 6: /* non stop button */
 
			OrderClick_Nonstop(w, v);
 
			break;
 

	
 
		case 7: /* goto button */
 
			OrderClick_Goto(w, v);
 
			break;
 

	
 
		case 8: /* full load button */
 
			OrderClick_FullLoad(w, v);
 
			break;
 

	
 
		case 9: /* unload button */
 
			OrderClick_Unload(w, v);
 
			break;
 
		case 10: /* transfer button */
 
			OrderClick_Transfer(w, v);
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_KEYPRESS: {
 
		Vehicle *v = GetVehicle(w->window_number);
 
		uint i;
 

	
 
		for (i = 0; i < lengthof(_order_keycodes); i++) {
 
			if (e->keypress.keycode == _order_keycodes[i]) {
 
				e->keypress.cont = false;
 
				//see if the button is disabled
 
				if (!(HASBIT(w->disabled_state, (i + 4)))) {
 
					_order_button_proc[i](w, v);
 
				}
 
				if (!HASBIT(w->disabled_state, i + 4)) _order_button_proc[i](w, v);
 
				break;
 
			}
 
		}
 
		break;
 
	}
 

	
 
	case WE_RCLICK: {
 
		const Vehicle* v = GetVehicle(w->window_number);
 
		int s = OrderGetSel(w);
 

	
 
		if (e->click.widget != 8) break;
 
		if (s == v->num_orders || GetVehicleOrder(v, s)->type != OT_GOTO_DEPOT) {
 
			GuiShowTooltips(STR_8857_MAKE_THE_HIGHLIGHTED_ORDER);
 
		} else {
 
			GuiShowTooltips(STR_SERVICE_HINT);
 
		}
 
	} break;
 

	
 
	case WE_4: {
 
		if (FindWindowById(WC_VEHICLE_VIEW, w->window_number) == NULL)
 
			DeleteWindow(w);
 
	} break;
 

	
 
	case WE_PLACE_OBJ: {
 
		OrdersPlaceObj(GetVehicle(w->window_number), e->place.tile, w);
 
	} break;
 

	
 
	case WE_ABORT_PLACE_OBJ: {
 
		CLRBIT(w->click_state, 7);
 
		InvalidateWidget(w, 7);
 
	} break;
 

	
 
	// check if a vehicle in a depot was clicked..
 
	case WE_MOUSELOOP: {
 
		const Vehicle* v = _place_clicked_vehicle;
 
		/*
 
		 * Check if we clicked on a vehicle
 
		 * and if the GOTO button of this window is pressed
 
		 * This is because of all open order windows WE_MOUSELOOP is called
 
		 * and if you have 3 windows open, and this check is not done
 
		 * the order is copied to the last open window instead of the
 
		 * one where GOTO is enalbed
 
		 */
 
		if (v != NULL && HASBIT(w->click_state, 7)) {
 
			_place_clicked_vehicle = NULL;
 
			HandleOrderVehClick(GetVehicle(w->window_number), v, w);
 
		}
 
	} break;
pathfind.c
Show inline comments
 
@@ -135,97 +135,97 @@ static const byte _otherdir_mask[4] = {
 
static void TPFMode2(TrackPathFinder* tpf, TileIndex tile, DiagDirection direction)
 
{
 
	uint bits;
 
	int i;
 
	RememberData rd;
 

	
 
	assert(tpf->tracktype == TRANSPORT_WATER);
 

	
 
	// This addition will sometimes overflow by a single tile.
 
	// The use of TILE_MASK here makes sure that we still point at a valid
 
	// tile, and then this tile will be in the sentinel row/col, so GetTileTrackStatus will fail.
 
	tile = TILE_MASK(tile + TileOffsByDir(direction));
 

	
 
	if (++tpf->rd.cur_length > 50)
 
		return;
 

	
 
	bits = GetTileTrackStatus(tile, tpf->tracktype);
 
	bits = (byte)((bits | (bits >> 8)) & _bits_mask[direction]);
 
	if (bits == 0)
 
		return;
 

	
 
	assert(TileX(tile) != MapMaxX() && TileY(tile) != MapMaxY());
 

	
 
	if ( (bits & (bits - 1)) == 0 ) {
 
		/* only one direction */
 
		i = 0;
 
		while (!(bits&1))
 
			i++, bits>>=1;
 

	
 
		rd = tpf->rd;
 
		goto continue_here;
 
	}
 
	/* several directions */
 
	i=0;
 
	do {
 
		if (!(bits & 1)) continue;
 
		rd = tpf->rd;
 

	
 
		// Change direction 4 times only
 
		if ((byte)i != tpf->rd.pft_var6) {
 
			if (++tpf->rd.depth > 4) {
 
				tpf->rd = rd;
 
				return;
 
			}
 
			tpf->rd.pft_var6 = (byte)i;
 
		}
 

	
 
continue_here:;
 
		tpf->the_dir = HASBIT(_otherdir_mask[direction],i) ? (i+8) : i;
 
		tpf->the_dir = i + (HASBIT(_otherdir_mask[direction], i) ? 8 : 0);
 

	
 
		if (!tpf->enum_proc(tile, tpf->userdata, tpf->the_dir, tpf->rd.cur_length, NULL)) {
 
			TPFMode2(tpf, tile, _tpf_new_direction[tpf->the_dir]);
 
		}
 

	
 
		tpf->rd = rd;
 
	} while (++i, bits>>=1);
 

	
 
}
 

	
 

	
 
/* Returns the end tile and the length of a tunnel. The length does not
 
 * include the starting tile (entry), it does include the end tile (exit).
 
 */
 
FindLengthOfTunnelResult FindLengthOfTunnel(TileIndex tile, DiagDirection dir)
 
{
 
	TileIndexDiff delta = TileOffsByDir(dir);
 
	uint z = GetTileZ(tile);
 
	FindLengthOfTunnelResult flotr;
 

	
 
	flotr.length = 0;
 

	
 
	dir = ReverseDiagDir(dir);
 
	do {
 
		flotr.length++;
 
		tile += delta;
 
	} while(
 
		!IsTunnelTile(tile) ||
 
		GetTunnelDirection(tile) != dir ||
 
		GetTileZ(tile) != z
 
	);
 

	
 
	flotr.tile = tile;
 
	return flotr;
 
}
 

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

	
 
static uint SkipToEndOfTunnel(TrackPathFinder* tpf, TileIndex tile, DiagDirection direction)
 
{
 
	FindLengthOfTunnelResult flotr;
 
	TPFSetTileBit(tpf, tile, 14);
 
	flotr = FindLengthOfTunnel(tile, direction);
 
	tpf->rd.cur_length += flotr.length;
 
	TPFSetTileBit(tpf, flotr.tile, 14);
 
	return flotr.tile;
 
}
 

	
 
@@ -709,98 +709,97 @@ callback_and_continue:
 
		direction = _tpf_new_direction[si.track];
 

	
 
start_at:
 
		// If the tile is the entry tile of a tunnel, and we're not going out of the tunnel,
 
		//   need to find the exit of the tunnel.
 
		if (IsTunnelTile(tile) &&
 
				GetTunnelDirection(tile) != ReverseDiagDir(direction)) {
 
			/* We are not just driving out of the tunnel */
 
			if (GetTunnelDirection(tile) != direction ||
 
					GetTunnelTransportType(tile) != tpf->tracktype) {
 
				// We are not driving into the tunnel, or it is an invalid tunnel
 
				continue;
 
			}
 
			if (!HASBIT(tpf->railtypes, GetRailType(tile))) {
 
				bits = 0;
 
				break;
 
			}
 
			flotr = FindLengthOfTunnel(tile, direction);
 
			si.cur_length += flotr.length * DIAG_FACTOR;
 
			tile = flotr.tile;
 
			// tile now points to the exit tile of the tunnel
 
		}
 

	
 
		// This is a special loop used to go through
 
		// a rail net and find the first intersection
 
		tile_org = tile;
 
		for (;;) {
 
			assert(direction <= 3);
 
			tile += TileOffsByDir(direction);
 

	
 
			// too long search length? bail out.
 
			if (si.cur_length >= tpf->maxlength) {
 
				DEBUG(ntp,1) ("[NTP] cur_length too big");
 
				bits = 0;
 
				break;
 
			}
 

	
 
			// Not a regular rail tile?
 
			// Then we can't use the code below, but revert to more general code.
 
			if (!IsTileType(tile, MP_RAILWAY) || !IsPlainRailTile(tile)) {
 
				// We found a tile which is not a normal railway tile.
 
				// Determine which tracks that exist on this tile.
 
				bits = GetTileTrackStatus(tile, TRANSPORT_RAIL) & _tpfmode1_and[direction];
 
				bits = (bits | (bits >> 8)) & 0x3F;
 

	
 
				// Check that the tile contains exactly one track
 
				if (bits == 0 || KILL_FIRST_BIT(bits) != 0) break;
 

	
 
				/* Check the rail type only if the train is *NOT* on top of
 
				 * a bridge. */
 
				/* Check the rail type only if the train is *NOT* on top of a bridge. */
 
				if (!(IsBridgeTile(tile) && IsBridgeMiddle(tile) && GetBridgeAxis(tile) == DiagDirToAxis(direction))) {
 
					if (IsTileType(tile, MP_STREET) ? !HASBIT(tpf->railtypes, GetRailTypeCrossing(tile)) : !HASBIT(tpf->railtypes, GetRailType(tile))) {
 
						bits = 0;
 
						break;
 
					}
 
				}
 

	
 
				///////////////////
 
				// If we reach here, the tile has exactly one track.
 
				//   tile - index to a tile that is not rail tile, but still straight (with optional signals)
 
				//   bits - bitmask of which track that exist on the tile (exactly one bit is set)
 
				//   direction - which direction are we moving in?
 
				///////////////////
 
				si.track = _new_track[FIND_FIRST_BIT(bits)][direction];
 
				si.cur_length += _length_of_track[si.track];
 
				goto callback_and_continue;
 
			}
 

	
 
			/* Regular rail tile, determine which tracks exist. */
 
			allbits = GetTrackBits(tile);
 
			/* Which tracks are reachable? */
 
			bits = allbits & DiagdirReachesTracks(direction);
 

	
 
			/* The tile has no reachable tracks => End of rail segment
 
			 * or Intersection => End of rail segment. We check this agains all the
 
			 * bits, not just reachable ones, to prevent infinite loops. */
 
			if (bits == 0 || TracksOverlap(allbits)) break;
 

	
 
			if (!HASBIT(tpf->railtypes, GetRailType(tile))) {
 
				bits = 0;
 
				break;
 
			}
 

	
 
			/* If we reach here, the tile has exactly one track, and this
 
			 track is reachable => Rail segment continues */
 

	
 
			track = _new_track[FIND_FIRST_BIT(bits)][direction];
 
			assert(track != 0xff);
 

	
 
			si.cur_length += _length_of_track[track];
 

	
 
			// Check if this rail is an upwards slope. If it is, then add a penalty.
 
			// Small optimization here.. if (track&7)>1 then it can't be a slope so we avoid calling GetTileSlope
 
			if ((track & 7) <= 1 && (_is_upwards_slope[GetTileSlope(tile, NULL)] & (1 << track)) ) {
 
				// upwards slope. add some penalty.
 
				si.cur_length += 4*DIAG_FACTOR;
 
			}
 

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

	
 
#ifndef PATHFIND_H
 
#define PATHFIND_H
 

	
 
#include "direction.h"
 

	
 
//#define PF_BENCH // perform simple benchmarks on the train pathfinder (not
 
//supported on all archs)
 

	
 
typedef struct TrackPathFinder TrackPathFinder;
 
typedef bool TPFEnumProc(TileIndex tile, void *data, int track, uint length, byte *state);
 
typedef void TPFAfterProc(TrackPathFinder *tpf);
 

	
 
typedef bool NTPEnumProc(TileIndex tile, void *data, int track, uint length);
 

	
 
#define PATHFIND_GET_LINK_OFFS(tpf, link) ((byte*)(link) - (byte*)tpf->links)
 
#define PATHFIND_GET_LINK_PTR(tpf, link_offs) (TrackPathFinderLink*)((byte*)tpf->links + (link_offs))
 

	
 
/* y7 y6 y5 y4 y3 y2 y1 y0 x7 x6 x5 x4 x3 x2 x1 x0
 
 * y7 y6 y5 y4 y3 y2 y1 y0 x4 x3 x2 x1 x0  0  0  0
 
 *  0  0 y7 y6 y5 y4 y3 y2 y1 y0 x4 x3 x2 x1 x0  0
 
 *  0  0  0  0 y5 y4 y3 y2 y1 y0 x4 x3 x2 x1 x0  0
 
 */
 
#define PATHFIND_HASH_TILE(tile) (TileX(tile) & 0x1F) + ((TileY(tile) & 0x1F) << 5)
 

	
 
typedef struct TrackPathFinderLink {
 
	TileIndex tile;
 
	uint16 flags;
 
	uint16 next;
 
} TrackPathFinderLink;
 

	
 
typedef struct RememberData {
 
	uint16 cur_length;
 
	byte depth;
 
	byte pft_var6;
 
} RememberData;
 

	
 
struct TrackPathFinder {
 

	
 
	int num_links_left;
 
	TrackPathFinderLink *new_link;
 

	
 
	TPFEnumProc *enum_proc;
 

	
 
	void *userdata;
 

	
 
	RememberData rd;
 

	
 
	int the_dir;
 

	
 
	byte tracktype;
 
	byte var2;
 
	bool disable_tile_hash;
 
	bool hasbit_13;
 

	
 
	uint16 hash_head[0x400];
 
	TileIndex hash_tile[0x400]; /* stores the link index when multi link. */
 

	
 
	TrackPathFinderLink links[0x400]; /* hopefully, this is enough. */
 
};
 

	
 
void FollowTrack(TileIndex tile, uint16 flags, DiagDirection direction, TPFEnumProc* enum_proc, TPFAfterProc* after_proc, void* data);
 

	
 
typedef struct {
 
	TileIndex tile;
 
	int length;
 
} FindLengthOfTunnelResult;
 
FindLengthOfTunnelResult FindLengthOfTunnel(TileIndex tile, DiagDirection direction);
 

	
 
void NewTrainPathfind(TileIndex tile, TileIndex dest, RailTypeMask railtypes, DiagDirection direction, NTPEnumProc* enum_proc, void* data);
 

	
 
#endif /* PATHFIND_H */
player_gui.c
Show inline comments
 
@@ -226,97 +226,97 @@ static const WindowDesc * const desc_tab
 
static void DoShowPlayerFinances(PlayerID player, bool show_small, bool show_stickied)
 
{
 
	Window *w;
 
	int mode;
 

	
 
	mode = (player != _local_player) * 2 + show_small;
 
	w = AllocateWindowDescFront(desc_table[mode], player);
 
	if (w != NULL) {
 
		w->caption_color = w->window_number;
 
		WP(w,def_d).data_1 = mode;
 
		if (show_stickied) {
 
			w->flags4 |= WF_STICKY;
 
		}
 
	}
 
}
 

	
 
void ShowPlayerFinances(PlayerID player)
 
{
 
	DoShowPlayerFinances(player, false, false);
 
}
 

	
 
static void SelectPlayerColorWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		const Player* p;
 
		uint used_colors = 0;
 
		int num_free = 16;
 
		int x,y,pos;
 
		int i;
 

	
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active) {
 
				SETBIT(used_colors, p->player_color);
 
				num_free--;
 
			}
 
		}
 
		WP(w,def_d).data_1 = used_colors;
 
		SetVScrollCount(w, num_free);
 
		DrawWindowWidgets(w);
 

	
 
		x = 2;
 
		y = 17;
 
		pos = w->vscroll.pos;
 

	
 
		for (i = 0; i != 16; i++) {
 
			if (!(used_colors & 1) && --pos < 0 && pos >= -8) {
 
				DrawString(x + 30, y, STR_00D1_DARK_BLUE + i, 2);
 
				DrawSprite(((GENERAL_SPRITE_COLOR(i) | PALETTE_MODIFIER_COLOR) | SPR_VEH_BUS_SIDE_VIEW), x + 14, y + 4);
 
				DrawSprite(GENERAL_SPRITE_COLOR(i) | PALETTE_MODIFIER_COLOR | SPR_VEH_BUS_SIDE_VIEW, x + 14, y + 4);
 
				y += 14;
 
			}
 
			used_colors >>= 1;
 
		}
 
	} break;
 

	
 
	case WE_CLICK:
 
		if (e->click.widget == 2) {
 
			int item = (e->click.pt.y - 13) / 14;
 
			uint used_colors;
 
			int i;
 

	
 
			if ((uint)item >= 8)
 
				return;
 
			item += w->vscroll.pos;
 
			used_colors = WP(w,def_d).data_1;
 

	
 
			for (i = 0; i != 16; i++) {
 
				if (!(used_colors & 1) && --item < 0) {
 
					DoCommandP(0, 0, i, NULL, CMD_SET_PLAYER_COLOR);
 
					DeleteWindow(w);
 
					break;
 
				}
 
				used_colors >>= 1;
 
			}
 
		}
 
		break;
 
	}
 
}
 

	
 
static const Widget _select_player_color_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   149,     0,    13, STR_7007_NEW_COLOR_SCHEME, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_IMGBTN,   RESIZE_NONE,    14,     0,   137,    14,   127, 0x0, STR_7034_CLICK_ON_SELECTED_NEW_COLOR},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,    14,   138,   149,    14,   127, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _select_player_color_desc = {
 
	-1,-1, 150, 128,
 
	WC_PLAYER_COLOR,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_select_player_color_widgets,
 
	SelectPlayerColorWndProc
 
};
 

	
 
static void SelectPlayerFaceWndProc(Window *w, WindowEvent *e)
 
{
players.c
Show inline comments
 
@@ -221,97 +221,97 @@ static void SubtractMoneyFromAnyPlayer(P
 
	p->yearly_expenses[0][_yearly_expenses_type] += cost;
 

	
 
	if ( ( 1 << _yearly_expenses_type ) & (1<<7|1<<8|1<<9|1<<10))
 
		p->cur_economy.income -= cost;
 
	else if (( 1 << _yearly_expenses_type ) & (1<<2|1<<3|1<<4|1<<5|1<<6|1<<11))
 
		p->cur_economy.expenses -= cost;
 

	
 
	InvalidatePlayerWindows(p);
 
}
 

	
 
void SubtractMoneyFromPlayer(int32 cost)
 
{
 
	PlayerID pid = _current_player;
 
	if (pid < MAX_PLAYERS)
 
		SubtractMoneyFromAnyPlayer(GetPlayer(pid), cost);
 
}
 

	
 
void SubtractMoneyFromPlayerFract(PlayerID player, int32 cost)
 
{
 
	Player *p = GetPlayer(player);
 
	byte m = p->player_money_fraction;
 
	p->player_money_fraction = m - (byte)cost;
 
	cost >>= 8;
 
	if (p->player_money_fraction > m)
 
		cost++;
 
	if (cost != 0)
 
		SubtractMoneyFromAnyPlayer(p, cost);
 
}
 

	
 
// the player_money field is kept as it is, but money64 contains the actual amount of money.
 
void UpdatePlayerMoney32(Player *p)
 
{
 
	if (p->money64 < -2000000000)
 
		p->player_money = -2000000000;
 
	else if (p->money64 > 2000000000)
 
		p->player_money = 2000000000;
 
	else
 
		p->player_money = (int32)p->money64;
 
}
 

	
 
void GetNameOfOwner(PlayerID owner, TileIndex tile)
 
{
 
	SetDParam(2, owner);
 

	
 
	if (owner != OWNER_TOWN) {
 
		if (owner >= 8)
 
			SetDParam(0, STR_0150_SOMEONE);
 
		else {
 
			Player *p = GetPlayer(owner);
 
			const Player* p = GetPlayer(owner);
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
		}
 
	} else {
 
		Town *t = ClosestTownFromTile(tile, (uint)-1);
 
		SetDParam(0, STR_TOWN);
 
		SetDParam(1, t->index);
 
	}
 
}
 

	
 

	
 
bool CheckOwnership(PlayerID owner)
 
{
 
	assert(owner <= OWNER_WATER);
 

	
 
	if (owner == _current_player)
 
		return true;
 
	_error_message = STR_013B_OWNED_BY;
 
	GetNameOfOwner(owner, 0);
 
	return false;
 
}
 

	
 
bool CheckTileOwnership(TileIndex tile)
 
{
 
	PlayerID owner = GetTileOwner(tile);
 

	
 
	assert(owner <= OWNER_WATER);
 

	
 
	if (owner == _current_player)
 
		return true;
 
	_error_message = STR_013B_OWNED_BY;
 

	
 
	// no need to get the name of the owner unless we're the local player (saves some time)
 
	if (IsLocalPlayer()) GetNameOfOwner(owner, tile);
 
	return false;
 
}
 

	
 
static void GenerateCompanyName(Player *p)
 
{
 
	TileIndex tile;
 
	Town *t;
 
	StringID str;
 
	Player *pp;
 
	uint32 strp;
 
	char buffer[100];
 

	
 
	if (p->name_1 != STR_SV_UNNAMED)
 
		return;
 
@@ -434,178 +434,180 @@ static byte GeneratePlayerColor(void)
 
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;
 

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

	
 
	// Make a color
 
	p->player_color = GeneratePlayerColor();
 
	_player_colors[p->index] = p->player_color;
 
	p->name_1 = STR_SV_UNNAMED;
 
	p->is_active = true;
 

	
 
	p->money64 = p->player_money = p->current_loan = 100000;
 

	
 
	p->is_ai = is_ai;
 
	p->ai.state = 5; /* AIS_WANT_NEW_ROUTE */
 
	p->share_owners[0] = p->share_owners[1] = p->share_owners[2] = p->share_owners[3] = OWNER_SPECTATOR;
 

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

	
 
	/* Engine renewal settings */
 
	p->engine_renew_list = NULL;
 
	p->renew_keep_length = false;
 
	p->engine_renew = false;
 
	p->engine_renew_months = -6;
 
	p->engine_renew_money = 100000;
 

	
 
	GeneratePresidentName(p);
 

	
 
	InvalidateWindow(WC_GRAPH_LEGEND, 0);
 
	InvalidateWindow(WC_TOOLBAR_MENU, 0);
 
	InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
	if (is_ai && (!_networking || _network_server) && _ai.enabled)
 
		AI_StartNewAI(p->index);
 

	
 
	return p;
 
}
 

	
 
void StartupPlayers(void)
 
{
 
	// The AI starts like in the setting with +2 month max
 
	_next_competitor_start = _opt.diff.competitor_start_time * 90 * DAY_TICKS + RandomRange(60 * DAY_TICKS) + 1;
 
}
 

	
 
static void MaybeStartNewPlayer(void)
 
{
 
	uint n;
 
	Player *p;
 

	
 
	// count number of competitors
 
	n = 0;
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active && p->is_ai)
 
			n++;
 
		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 ? InteractiveRandomRange(_opt.diff.max_no_competitors + 2) : RandomRange(_opt.diff.max_no_competitors + 2)) )
 
			/* Send a command to all clients to start  up a new AI. Works fine for Multiplayer and Singleplayer */
 
	if (n < (uint)_opt.diff.max_no_competitors &&
 
			n < (_network_server ?
 
				InteractiveRandomRange(_opt.diff.max_no_competitors + 2) :
 
				RandomRange(_opt.diff.max_no_competitors + 2)
 
			)) {
 
		/* Send a command to all clients to start up a new AI.
 
		 * Works fine for Multiplayer and Singleplayer */
 
			DoCommandP(0, 1, 0, NULL, CMD_PLAYER_CTRL);
 
	}
 

	
 
	// The next AI starts like the difficulty setting said, with +2 month max
 
	_next_competitor_start = _opt.diff.competitor_start_time * 90 * DAY_TICKS + 1;
 
	_next_competitor_start += _network_server ? InteractiveRandomRange(60 * DAY_TICKS) : RandomRange(60 * DAY_TICKS);
 
}
 

	
 
void InitializePlayers(void)
 
{
 
	int i;
 
	uint i;
 

	
 
	memset(_players, 0, sizeof(_players));
 
	for (i = 0; i != MAX_PLAYERS; i++)
 
		_players[i].index=i;
 
	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;
 
	if (_game_mode == GM_EDITOR) return;
 

	
 
	p = GetPlayer(_cur_player_tick_index);
 
	_cur_player_tick_index = (_cur_player_tick_index + 1) % MAX_PLAYERS;
 
	if (p->name_1 != 0) GenerateCompanyName(p);
 

	
 
	if (AI_AllowNewAI() && _game_mode != GM_MENU && !--_next_competitor_start)
 
		MaybeStartNewPlayer();
 
}
 

	
 
// index is the next parameter in _decode_parameters to set up
 
StringID GetPlayerNameString(PlayerID player, uint index)
 
{
 
	if (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(PlayerID pi)
 
{
 
	DeleteWindowById(WC_COMPANY, pi);
 
@@ -771,105 +773,110 @@ int32 CmdReplaceVehicle(TileIndex tile, 
 
			if (p->renew_keep_length == (bool)GB(p2, 0, 1))
 
				return CMD_ERROR;
 

	
 
			if (flags & DC_EXEC) {
 
				p->renew_keep_length = (bool)GB(p2, 0, 1);
 
				if (IsLocalPlayer()) {
 
					InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train);
 
				}
 
			}
 
		break;
 

	
 
	}
 
	return 0;
 
}
 

	
 
/** Control the players: add, delete, etc.
 
 * @param tile unused
 
 * @param p1 various functionality
 
 * - p1 = 0 - create a new player, Which player (network) it will be is in p2
 
 * - p1 = 1 - create a new AI player
 
 * - p1 = 2 - delete a player. Player is identified by p2
 
 * - p1 = 3 - merge two companies together. Player to merge #1 with player #2. Identified by p2
 
 * @param p2 various functionality, dictated by p1
 
 * - p1 = 0 - ClientID of the newly created player
 
 * - p1 = 2 - PlayerID of the that is getting deleted
 
 * - p1 = 3 - #1 p2 = (bit  0-15) - player to merge (p2 & 0xFFFF)
 
 *          - #2 p2 = (bit 16-31) - player to be merged into ((p2>>16)&0xFFFF)
 
 * @todo In the case of p1=0, create new player, the clientID of the new player is in parameter
 
 * p2. This parameter is passed in at function DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND)
 
 * on the server itself. First of all this is unbelievably ugly; second of all, well,
 
 * it IS ugly! <b>Someone fix this up :)</b> So where to fix?@n
 
 * @arg - network_server.c:838 DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND)@n
 
 * @arg - network_client.c:536 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP) from where the map has been received
 
 */
 
int32 CmdPlayerCtrl(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (flags & DC_EXEC) _current_player = OWNER_NONE;
 

	
 
	switch (p1) {
 
	case 0: { /* Create a new player */
 
		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)
 
		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 && (!_ai.network_client || _ai.network_playas == OWNER_SPECTATOR)) {
 
			if (_local_player == OWNER_SPECTATOR &&
 
					(!_ai.network_client || _ai.network_playas == OWNER_SPECTATOR)) {
 
				/* Check if we do not want to be a spectator in network */
 
				if (!_networking || (_network_server && !_network_dedicated) || _network_playas != OWNER_SPECTATOR || _ai.network_client) {
 
				if (!_networking ||
 
						(_network_server && !_network_dedicated) ||
 
						_network_playas != OWNER_SPECTATOR ||
 
						_ai.network_client) {
 
					if (_ai.network_client) {
 
						/* As ai-network-client, we have our own rulez (disable GUI and stuff) */
 
						_ai.network_playas = p->index;
 
						_local_player      = OWNER_SPECTATOR;
 
						if (_ai.network_playas != OWNER_SPECTATOR) {
 
							/* If we didn't join the game as a spectator, activate the AI */
 
							AI_StartNewAI(_ai.network_playas);
 
						}
 
					} else {
 
						_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) { // Creating player failed, defer client to spectator
 
				/* 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) */
 
@@ -937,98 +944,97 @@ static const StringID _endgame_perf_titl
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
	STR_0213_BUSINESSMAN,
 
	STR_0214_ENTREPRENEUR,
 
	STR_0214_ENTREPRENEUR,
 
	STR_0215_INDUSTRIALIST,
 
	STR_0215_INDUSTRIALIST,
 
	STR_0216_CAPITALIST,
 
	STR_0216_CAPITALIST,
 
	STR_0217_MAGNATE,
 
	STR_0217_MAGNATE,
 
	STR_0218_MOGUL,
 
	STR_0218_MOGUL,
 
	STR_0219_TYCOON_OF_THE_CENTURY,
 
};
 

	
 
StringID EndGameGetPerformanceTitleFromValue(uint value)
 
{
 
	value = minu(value, 1000) >> 6;
 
	if (value >= lengthof(_endgame_perf_titles)) value = lengthof(_endgame_perf_titles) - 1;
 

	
 
	return _endgame_perf_titles[value];
 
}
 

	
 
/* Return true if any cheat has been used, false otherwise */
 
static bool CheatHasBeenUsed(void)
 
{
 
	const Cheat* cht = (Cheat*) &_cheats;
 
	const Cheat* cht_last = &cht[sizeof(_cheats) / sizeof(Cheat)];
 

	
 
	for (; cht != cht_last; cht++) {
 
		if (cht->been_used)
 
			return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
/* Save the highscore for the player */
 
int8 SaveHighScoreValue(const Player *p)
 
{
 
	HighScore *hs = _highscore_table[_opt.diff_level];
 
	uint i;
 
	uint16 score = p->old_economy[0].performance_history;
 

	
 
	/* Exclude cheaters from the honour of being in the highscore table */
 
	if (CheatHasBeenUsed())
 
		return -1;
 
	if (CheatHasBeenUsed()) return -1;
 

	
 
	for (i = 0; i < lengthof(_highscore_table[0]); i++) {
 
		/* You are in the TOP5. Move all values one down and save us there */
 
		if (hs[i].score <= score) {
 
			// move all elements one down starting from the replaced one
 
			memmove(&hs[i + 1], &hs[i], sizeof(HighScore) * (lengthof(_highscore_table[0]) - i - 1));
 
			SetDParam(0, p->president_name_1);
 
			SetDParam(1, p->president_name_2);
 
			SetDParam(2, p->name_1);
 
			SetDParam(3, p->name_2);
 
			GetString(hs[i].company, STR_HIGHSCORE_NAME); // get manager/company name string
 
			hs[i].score = score;
 
			hs[i].title = EndGameGetPerformanceTitleFromValue(score);
 
			return i;
 
		}
 
	}
 

	
 
	return -1; // too bad; we did not make it into the top5
 
}
 

	
 
/* Sort all players given their performance */
 
static int CDECL HighScoreSorter(const void *a, const void *b)
 
{
 
	const Player *pa = *(const Player* const*)a;
 
	const Player *pb = *(const Player* const*)b;
 

	
 
	return pb->old_economy[0].performance_history - pa->old_economy[0].performance_history;
 
}
 

	
 
/* Save the highscores in a network game when it has ended */
 
#define LAST_HS_ITEM lengthof(_highscore_table) - 1
 
int8 SaveHighScoreValueNetwork(void)
 
{
 
	const Player* p;
 
	const Player* pl[MAX_PLAYERS];
 
	size_t count = 0;
 
	int8 player = -1;
 

	
 
	/* Sort all active players with the highest score first */
 
	FOR_ALL_PLAYERS(p) if (p->is_active) pl[count++] = p;
 
	qsort((Player*)pl, count, sizeof(pl[0]), HighScoreSorter);
 

	
 
	{
 
		uint i;
 

	
 
		memset(_highscore_table[LAST_HS_ITEM], 0, sizeof(_highscore_table[0]));
 

	
 
		/* Copy over Top5 companies */
 
@@ -1202,90 +1208,91 @@ static const SaveLoad _player_ai_desc[] 
 
	SLE_CONDVAR(PlayerAI, start_tile_a, SLE_UINT32, 6, SL_MAX_VERSION),
 
	SLE_CONDVAR(PlayerAI, cur_tile_a,   SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(PlayerAI, cur_tile_a,   SLE_UINT32, 6, SL_MAX_VERSION),
 
	SLE_VAR(PlayerAI,start_dir_a,				SLE_UINT8),
 
	SLE_VAR(PlayerAI,cur_dir_a,					SLE_UINT8),
 

	
 
	SLE_CONDVAR(PlayerAI, start_tile_b, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(PlayerAI, start_tile_b, SLE_UINT32, 6, SL_MAX_VERSION),
 
	SLE_CONDVAR(PlayerAI, cur_tile_b,   SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(PlayerAI, cur_tile_b,   SLE_UINT32, 6, SL_MAX_VERSION),
 
	SLE_VAR(PlayerAI,start_dir_b,				SLE_UINT8),
 
	SLE_VAR(PlayerAI,cur_dir_b,					SLE_UINT8),
 

	
 
	SLE_REF(PlayerAI,cur_veh,						REF_VEHICLE),
 

	
 
	SLE_ARR(PlayerAI,wagon_list,				SLE_UINT16, 9),
 
	SLE_ARR(PlayerAI,order_list_blocks,	SLE_UINT8, 20),
 
	SLE_ARR(PlayerAI,banned_tiles,			SLE_UINT16, 16),
 

	
 
	SLE_CONDNULL(64, 2, SL_MAX_VERSION),
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _player_ai_build_rec_desc[] = {
 
	SLE_CONDVAR(AiBuildRec,spec_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(AiBuildRec,spec_tile, SLE_UINT32, 6, SL_MAX_VERSION),
 
	SLE_CONDVAR(AiBuildRec,use_tile,  SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(AiBuildRec,use_tile,  SLE_UINT32, 6, SL_MAX_VERSION),
 
	SLE_VAR(AiBuildRec,rand_rng,			SLE_UINT8),
 
	SLE_VAR(AiBuildRec,cur_building_rule,SLE_UINT8),
 
	SLE_VAR(AiBuildRec,unk6,					SLE_UINT8),
 
	SLE_VAR(AiBuildRec,unk7,					SLE_UINT8),
 
	SLE_VAR(AiBuildRec,buildcmd_a,		SLE_UINT8),
 
	SLE_VAR(AiBuildRec,buildcmd_b,		SLE_UINT8),
 
	SLE_VAR(AiBuildRec,direction,			SLE_UINT8),
 
	SLE_VAR(AiBuildRec,cargo,					SLE_UINT8),
 
	SLE_END()
 
};
 

	
 
static 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++)
 
		for (i = 0; i != p->ai.num_build_rec; i++) {
 
			SlObject(&p->ai.src + i, _player_ai_build_rec_desc);
 
	}
 
	}
 

	
 
	// Write economy
 
	SlObject(&p->cur_economy, _player_economy_desc);
 

	
 
	// Write old economy entries.
 
	for (i = 0; i < p->num_valid_stat_ent; i++) {
 
		SlObject(&p->old_economy[i], _player_economy_desc);
 
	}
 
}
 

	
 
static void Save_PLYR(void)
 
{
 
	Player *p;
 
	FOR_ALL_PLAYERS(p) {
 
		if (p->is_active) {
 
			SlSetArrayIndex(p->index);
 
			SlAutolength((AutolengthProc*)SaveLoad_PLYR, p);
 
		}
 
	}
 
}
 

	
 
static void Load_PLYR(void)
 
{
 
	int index;
 
	while ((index = SlIterateArray()) != -1) {
 
		Player *p = GetPlayer(index);
 
		SaveLoad_PLYR(p);
 
		_player_colors[index] = p->player_color;
 
		UpdatePlayerMoney32(p);
 

	
 
		/* This is needed so an AI is attached to a loaded AI */
 
		if (p->is_ai && (!_networking || _network_server) && _ai.enabled)
 
			AI_StartNewAI(p->index);
 
	}
 
}
 

	
 
const ChunkHandler _player_chunk_handlers[] = {
 
	{ 'PLYR', Save_PLYR, Load_PLYR, CH_ARRAY | CH_LAST},
 
};
rail_cmd.c
Show inline comments
 
@@ -680,97 +680,97 @@ int32 CmdBuildSingleSignal(TileIndex til
 
			if (!HasSignalOnTrack(tile, track)) {
 
				// build new signals
 
				_m[tile].m3 |= SignalOnTrack(track);
 
			} else {
 
				if (pre_signal) {
 
					// cycle between normal -> pre -> exit -> combo -> ...
 
					SignalType type = GetSignalType(tile);
 

	
 
					SetSignalType(tile, type == SIGTYPE_COMBO ? SIGTYPE_NORMAL : type + 1);
 
				} else {
 
					CycleSignalSide(tile, track);
 
				}
 
			}
 
		} else {
 
			/* If CmdBuildManySignals is called with copying signals, just copy the
 
			 * direction of the first signal given as parameter by CmdBuildManySignals */
 
			_m[tile].m3 &= ~SignalOnTrack(track);
 
			_m[tile].m3 |= p2 & SignalOnTrack(track);
 
			SetSignalVariant(tile, sigvar);
 
		}
 

	
 
		MarkTileDirtyByTile(tile);
 
		SetSignalsOnBothDir(tile, track);
 
		YapfNotifyTrackLayoutChange(tile, track);
 
	}
 

	
 
	return cost;
 
}
 

	
 
/**	Build many signals by dragging; AutoSignals
 
 * @param tile start tile of drag
 
 * @param p1  end tile of drag
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit  0)    - 0 = build, 1 = remove signals
 
 * - p2 = (bit  3)    - 0 = signals, 1 = semaphores
 
 * - p2 = (bit  4- 6) - track-orientation, valid values: 0-5 (Track enum)
 
 * - p2 = (bit 24-31) - user defined signals_density
 
 */
 
static int32 CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 ret, total_cost, signal_ctr;
 
	byte signals;
 
	bool error = true;
 
	TileIndex end_tile;
 

	
 
	int mode = p2 & 0x1;
 
	Track track = GB(p2, 4, 3);
 
	Trackdir trackdir = TrackToTrackdir(track);
 
	byte semaphores = (HASBIT(p2, 3)) ? 8 : 0;
 
	byte semaphores = (HASBIT(p2, 3) ? 8 : 0);
 
	byte signal_density = (p2 >> 24);
 

	
 
	if (p1 >= MapSize()) return CMD_ERROR;
 
	end_tile = p1;
 
	if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
 

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

	
 
	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 

	
 
	/* for vertical/horizontal tracks, double the given signals density
 
	* since the original amount will be too dense (shorter tracks) */
 
	if (!IsDiagonalTrack(track))
 
		signal_density *= 2;
 

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

	
 
	track = TrackdirToTrack(trackdir); /* trackdir might have changed, keep track in sync */
 

	
 
	// copy the signal-style of the first rail-piece if existing
 
	if (GetRailTileType(tile) == RAIL_TILE_SIGNALS && GetTrackBits(tile) != 0) { /* XXX: GetTrackBits check useless? */
 
		signals = _m[tile].m3 & SignalOnTrack(track);
 
		if (signals == 0) signals = SignalOnTrack(track); /* Can this actually occur? */
 

	
 
		// copy signal/semaphores style (independent of CTRL)
 
		semaphores = (GetSignalVariant(tile) == SIG_ELECTRIC ? 0 : 8);
 
	} else // no signals exist, drag a two-way signal stretch
 
		signals = SignalOnTrack(track);
 

	
 
	/* signal_ctr         - amount of tiles already processed
 
	 * signals_density    - patch setting to put signal on every Nth tile (double space on |, -- tracks)
 
	 **********
 
	 * trackdir   - trackdir to build with autorail
 
	 * semaphores - semaphores or signals
 
	 * signals    - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
 
	                and convert all others to semaphore/signal
 
	 * mode       - 1 remove signals, 0 build signals */
 
	signal_ctr = total_cost = 0;
 
	for (;;) {
 
		// only build/remove signals with the specified density
 
		if ((signal_ctr % signal_density) == 0 ) {
 
			ret = DoCommand(tile, TrackdirToTrack(trackdir) | semaphores, signals, flags, (mode == 1) ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS);
 

	
 
			/* Abort placement for any other error than NOT_SUITABLE_TRACK
 
			 * This includes vehicles on track, competitor's tracks, etc. */
 
			if (CmdFailed(ret)) {
 
				if (_error_message != STR_1005_NO_SUITABLE_RAILROAD_TRACK && mode != 1) return CMD_ERROR;
 
				_error_message = INVALID_STRING_ID;
road_cmd.c
Show inline comments
 
@@ -696,163 +696,158 @@ static void DrawRoadBits(TileInfo* ti, R
 
{
 
	const DrawRoadTileStruct *drts;
 
	PalSpriteID image = 0;
 

	
 
	if (ti->tileh != SLOPE_FLAT) {
 
		int foundation = GetRoadFoundation(ti->tileh, road);
 

	
 
		if (foundation != 0) DrawFoundation(ti, foundation);
 

	
 
		// DrawFoundation() modifies ti.
 
		// Default sloped sprites..
 
		if (ti->tileh != SLOPE_FLAT) image = _road_sloped_sprites[ti->tileh - 1] + 0x53F;
 
	}
 

	
 
	if (image == 0) image = _road_tile_sprites_1[road];
 

	
 
	if (GetGroundType(ti->tile) == RGT_BARREN) image |= PALETTE_TO_BARE_LAND;
 

	
 
	if (IsOnSnow(ti->tile)) {
 
		image += 19;
 
	} else if (HasPavement(ti->tile)) {
 
		// Pavement tiles.
 
		image -= 19;
 
	}
 

	
 
	DrawGroundSprite(image);
 

	
 
	// Return if full detail is disabled, or we are zoomed fully out.
 
	if (!(_display_opt & DO_FULL_DETAIL) || _cur_dpi->zoom == 2) return;
 

	
 
	if (HasRoadWorks(ti->tile)) {
 
		// Road works
 
		DrawGroundSprite(road & ROAD_X ? SPR_EXCAVATION_X : SPR_EXCAVATION_Y);
 
		return;
 
	}
 

	
 
	// Draw extra details.
 
	for (drts = _road_display_table[GetGroundType(ti->tile)][road]; drts->image != 0; drts++) {
 
		int x = ti->x | drts->subcoord_x;
 
		int y = ti->y | drts->subcoord_y;
 
		byte z = ti->z;
 
		if (ti->tileh != SLOPE_FLAT) z = GetSlopeZ(x, y);
 
		AddSortableSpriteToDraw(drts->image, x, y, 2, 2, 0x10, z);
 
	}
 
}
 

	
 
static void DrawTile_Road(TileInfo *ti)
 
{
 
	PalSpriteID image;
 

	
 
	switch (GetRoadTileType(ti->tile)) {
 
		case ROAD_TILE_NORMAL:
 
			DrawRoadBits(ti, GetRoadBits(ti->tile));
 
			break;
 

	
 
		case ROAD_TILE_CROSSING: {
 
			PalSpriteID image;
 

	
 
			if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh);
 

	
 
			image = GetRailTypeInfo(GetRailTypeCrossing(ti->tile))->base_sprites.crossing;
 

	
 
			if (GetCrossingRoadAxis(ti->tile) == AXIS_X) image++;
 

	
 
			if (IsCrossingBarred(ti->tile)) image += 2;
 

	
 
			if (IsOnSnow(ti->tile)) {
 
				image += 8;
 
			} else {
 
				if (GetGroundType(ti->tile) == RGT_BARREN) image |= PALETTE_TO_BARE_LAND;
 
				if (HasPavement(ti->tile)) image += 4;
 
			}
 

	
 
			DrawGroundSprite(image);
 
			if (GetRailTypeCrossing(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti);
 
			break;
 
		}
 

	
 
		default:
 
		case ROAD_TILE_DEPOT: {
 
			uint32 ormod;
 
			PlayerID player;
 
			const DrawRoadSeqStruct* drss;
 

	
 
			if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, ti->tileh);
 

	
 
			ormod = PALETTE_TO_GREY;	//was this a bug/problem?
 
			player = GetTileOwner(ti->tile);
 
			if (player < MAX_PLAYERS) ormod = PLAYER_SPRITE_COLOR(player);
 

	
 
			drss = _road_display_datas[GetRoadDepotDirection(ti->tile)];
 

	
 
			DrawGroundSprite(drss++->image);
 

	
 
			for (; drss->image != 0; drss++) {
 
				uint32 image = drss->image;
 

	
 
				if (image & PALETTE_MODIFIER_COLOR) image |= ormod;
 
				if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
 

	
 
				AddSortableSpriteToDraw(image, ti->x | drss->subcoord_x,
 
					ti->y | drss->subcoord_y, drss->width, drss->height, 0x14, ti->z
 
				);
 
			}
 
			break;
 
		}
 
	}
 
}
 

	
 
void DrawRoadDepotSprite(int x, int y, int image)
 
{
 
	uint32 ormod;
 
	const DrawRoadSeqStruct *dtss;
 

	
 
	ormod = PLAYER_SPRITE_COLOR(_local_player);
 

	
 
	dtss = _road_display_datas[image];
 
	const DrawRoadSeqStruct* dtss = _road_display_datas[image];
 
	uint32 ormod = PLAYER_SPRITE_COLOR(_local_player);
 

	
 
	x += 33;
 
	y += 17;
 

	
 
	DrawSprite(dtss++->image, x, y);
 

	
 
	for (; dtss->image != 0; dtss++) {
 
		Point pt = RemapCoords(dtss->subcoord_x, dtss->subcoord_y, 0);
 

	
 
		image = dtss->image;
 
		if (image & PALETTE_MODIFIER_COLOR) image |= ormod;
 

	
 
		DrawSprite(image, x + pt.x, y + pt.y);
 
	}
 
}
 

	
 
static uint GetSlopeZ_Road(const TileInfo* ti)
 
{
 
	Slope tileh = ti->tileh;
 
	uint z = ti->z;
 

	
 
	if (tileh == SLOPE_FLAT) return z;
 
	if (GetRoadTileType(ti->tile) == ROAD_TILE_NORMAL) {
 
		uint f = GetRoadFoundation(tileh, GetRoadBits(ti->tile));
 

	
 
		if (f != 0) {
 
			if (f < 15) return z + TILE_HEIGHT; // leveled foundation
 
			tileh = _inclined_tileh[f - 15]; // inclined foundation
 
		}
 
		return z + GetPartialZ(ti->x & 0xF, ti->y & 0xF, tileh);
 
	} else {
 
		return z + TILE_HEIGHT;
 
	}
 
}
 

	
 
static Slope GetSlopeTileh_Road(TileIndex tile, Slope tileh)
 
{
 
	if (tileh == SLOPE_FLAT) return SLOPE_FLAT;
 
	if (GetRoadTileType(tile) == ROAD_TILE_NORMAL) {
 
		uint f = GetRoadFoundation(tileh, GetRoadBits(tile));
 

	
 
		if (f == 0) return tileh;
 
		if (f < 15) return SLOPE_FLAT; // leveled foundation
 
		return _inclined_tileh[f - 15]; // inclined foundation
 
	} else {
 
		return SLOPE_FLAT;
 
	}
 
}
road_map.h
Show inline comments
 
/* $Id$ */
 

	
 
#ifndef ROAD_MAP_H
 
#define ROAD_MAP_H
 

	
 
#include "macros.h"
 
#include "rail.h"
 
#include "road.h"
 
#include "tile.h"
 

	
 

	
 
typedef enum RoadTileType {
 
	ROAD_TILE_NORMAL,
 
	ROAD_TILE_CROSSING,
 
	ROAD_TILE_DEPOT
 
} RoadTileType;
 

	
 
static inline RoadTileType GetRoadTileType(TileIndex t)
 
{
 
	assert(IsTileType(t, MP_STREET));
 
	return (RoadTileType)(GB(_m[t].m5, 4, 4));
 
	return (RoadTileType)GB(_m[t].m5, 4, 4);
 
}
 

	
 
static inline bool IsLevelCrossing(TileIndex t)
 
{
 
	return GetRoadTileType(t) == ROAD_TILE_CROSSING;
 
}
 

	
 
static inline bool IsLevelCrossingTile(TileIndex t)
 
{
 
	return IsTileType(t, MP_STREET) && IsLevelCrossing(t);
 
}
 

	
 
static inline RoadBits GetRoadBits(TileIndex t)
 
{
 
	assert(GetRoadTileType(t) == ROAD_TILE_NORMAL);
 
	return (RoadBits)(GB(_m[t].m5, 0, 4));
 
	return (RoadBits)GB(_m[t].m5, 0, 4);
 
}
 

	
 
static inline void SetRoadBits(TileIndex t, RoadBits r)
 
{
 
	assert(GetRoadTileType(t) == ROAD_TILE_NORMAL); // XXX incomplete
 
	SB(_m[t].m5, 0, 4, r);
 
}
 

	
 

	
 
static inline Axis GetCrossingRoadAxis(TileIndex t)
 
{
 
	assert(GetRoadTileType(t) == ROAD_TILE_CROSSING);
 
	return (Axis)GB(_m[t].m5, 3, 1);
 
}
 

	
 
static inline RoadBits GetCrossingRoadBits(TileIndex tile)
 
{
 
	return GetCrossingRoadAxis(tile) == AXIS_X ? ROAD_X : ROAD_Y;
 
}
 

	
 
static inline TrackBits GetCrossingRailBits(TileIndex tile)
 
{
 
	return GetCrossingRoadAxis(tile) == AXIS_X ? TRACK_BIT_Y : TRACK_BIT_X;
 
}
 

	
 

	
 
// TODO swap owner of road and rail
 
static inline Owner GetCrossingRoadOwner(TileIndex t)
 
{
 
	assert(GetRoadTileType(t) == ROAD_TILE_CROSSING);
 
	return (Owner)_m[t].m3;
 
}
 

	
 
static inline void SetCrossingRoadOwner(TileIndex t, Owner o)
 
{
 
	assert(GetRoadTileType(t) == ROAD_TILE_CROSSING);
 
	_m[t].m3 = o;
 
}
 

	
 
static inline void UnbarCrossing(TileIndex t)
 
{
 
	assert(GetRoadTileType(t) == ROAD_TILE_CROSSING);
 
	CLRBIT(_m[t].m5, 2);
 
}
 

	
 
static inline void BarCrossing(TileIndex t)
 
{
 
	assert(GetRoadTileType(t) == ROAD_TILE_CROSSING);
roadveh_gui.c
Show inline comments
 
@@ -370,100 +370,99 @@ static void RoadVehViewWndProc(Window *w
 
	} break;
 

	
 
	case WE_CLICK: {
 
		const Vehicle* v = GetVehicle(w->window_number);
 

	
 
		switch (e->click.widget) {
 
		case 5: /* start stop */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_ROADVEH | CMD_MSG(STR_9015_CAN_T_STOP_START_ROAD_VEHICLE));
 
			break;
 
		case 6: /* center main view */
 
			ScrollMainWindowTo(v->x_pos, v->y_pos);
 
			break;
 
		case 7: /* goto depot */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_SEND_ROADVEH_TO_DEPOT | CMD_MSG(STR_9018_CAN_T_SEND_VEHICLE_TO_DEPOT));
 
			break;
 
		case 8: /* turn around */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_TURN_ROADVEH | CMD_MSG(STR_9033_CAN_T_MAKE_VEHICLE_TURN));
 
			break;
 
		case 9: /* show orders */
 
			ShowOrdersWindow(v);
 
			break;
 
		case 10: /* show details */
 
			ShowRoadVehDetailsWindow(v);
 
			break;
 
		case 11: /* clone vehicle */
 
			DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneRoadVeh, CMD_CLONE_VEHICLE | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE));
 
			break;
 
		case 12: /* Refit vehicle */
 
			ShowRoadVehRefitWindow(v);
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_RESIZE:
 
		w->viewport->width  += e->sizing.diff.x;
 
		w->viewport->height += e->sizing.diff.y;
 
		w->viewport->virtual_width  += e->sizing.diff.x;
 
		w->viewport->virtual_height += e->sizing.diff.y;
 
		break;
 

	
 
	case WE_DESTROY:
 
		DeleteWindowById(WC_VEHICLE_REFIT, w->window_number);
 
		DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number);
 
		DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number);
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		{
 
			Vehicle *v;
 
			uint32 h;
 
			v = GetVehicle(w->window_number);
 
			h = IsRoadVehInDepotStopped(v) ? (1 << 7) | (1 << 8) : (1 << 11) | (1 << 12);
 
			const Vehicle* v = GetVehicle(w->window_number);
 
			uint32 h = IsRoadVehInDepotStopped(v) ? 1 << 7 | 1 << 8 : 1 << 11 | 1 << 12;
 

	
 
			if (h != w->hidden_state) {
 
				w->hidden_state = h;
 
				SetWindowDirty(w);
 
			}
 
		}
 
	}
 
}
 

	
 
static const Widget _roadveh_view_widgets[] = {
 
{ WWT_CLOSEBOX,   RESIZE_NONE,  14,   0,  10,   0,  13, STR_00C5, STR_018B_CLOSE_WINDOW },
 
{ WWT_CAPTION,    RESIZE_RIGHT, 14,  11, 237,   0,  13, STR_9002, STR_018C_WINDOW_TITLE_DRAG_THIS },
 
{ WWT_STICKYBOX,  RESIZE_LR,    14, 238, 249,   0,  13, 0x0,      STR_STICKY_BUTTON },
 
{ WWT_IMGBTN,     RESIZE_RB,    14,   0, 231,  14, 103, 0x0,      STR_NULL },
 
{ WWT_6,          RESIZE_RB,    14,   2, 229,  16, 101, 0x0,      STR_NULL },
 
{ WWT_PUSHIMGBTN, RESIZE_RTB,   14,   0, 237, 104, 115, 0x0,      STR_901C_CURRENT_VEHICLE_ACTION },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  14,  31, 0x2AB,    STR_901E_CENTER_MAIN_VIEW_ON_VEHICLE },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  32,  49, 0x2AE,    STR_901F_SEND_VEHICLE_TO_DEPOT },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  50,  67, 0x2CB,    STR_9020_FORCE_VEHICLE_TO_TURN_AROUND },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  68,  85, 0x2B2,    STR_901D_SHOW_VEHICLE_S_ORDERS },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  86, 103, 0x2B3,    STR_9021_SHOW_ROAD_VEHICLE_DETAILS },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  32,  49, SPR_CLONE_ROADVEH,      STR_CLONE_ROAD_VEHICLE_INFO },
 
{ WWT_PUSHIMGBTN, RESIZE_LR,    14, 232, 249,  50,  67, 0x2B4,    STR_REFIT_ROAD_VEHICLE_TO_CARRY },
 
{ WWT_PANEL,      RESIZE_LRB,   14, 232, 249, 104, 103, 0x0,      STR_NULL },
 
{ WWT_RESIZEBOX,  RESIZE_LRTB,  14, 238, 249, 104, 115, 0x0,      STR_NULL },
 
{ WIDGETS_END }
 
};
 

	
 
static const WindowDesc _roadveh_view_desc = {
 
	-1,-1, 250, 116,
 
	WC_VEHICLE_VIEW,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 
	_roadveh_view_widgets,
 
	RoadVehViewWndProc,
 
};
 

	
 
void ShowRoadVehViewWindow(const Vehicle* v)
 
{
 
	Window* w = AllocateWindowDescFront(&_roadveh_view_desc, v->index);
 

	
 
	if (w != NULL) {
 
		w->caption_color = v->owner;
 
		AssignWindowViewport(w, 3, 17, 0xE2, 0x54, w->window_number | (1 << 31), 0);
 
	}
 
}
 

	
 

	
 
static void DrawNewRoadVehWindow(Window *w)
 
{
 
@@ -962,100 +961,101 @@ static void PlayerRoadVehWndProc(Window 
 
	case WE_PAINT: {
 
		int x = 2;
 
		int y = PLY_WND_PRC__OFFSET_TOP_WIDGET;
 
		int max;
 
		int i;
 

	
 
		BuildVehicleList(vl, VEH_Road, owner, station);
 
		SortVehicleList(vl);
 

	
 
		SetVScrollCount(w, vl->list_length);
 

	
 
		// disable 'Sort By' tooltip on Unsorted sorting criteria
 
		if (vl->sort_type == SORT_BY_UNSORTED)
 
			w->disabled_state |= (1 << 3);
 

	
 
		/* draw the widgets */
 
		{
 
			const Player *p = GetPlayer(owner);
 
			if (station == INVALID_STATION) {
 
				/* Company Name -- (###) Road vehicles */
 
				SetDParam(0, p->name_1);
 
				SetDParam(1, p->name_2);
 
				SetDParam(2, w->vscroll.count);
 
				w->widget[1].unkA = STR_9001_ROAD_VEHICLES;
 
			} else {
 
				/* Station Name -- (###) Road vehicles */
 
				SetDParam(0, station);
 
				SetDParam(1, w->vscroll.count);
 
				w->widget[1].unkA = STR_SCHEDULED_ROAD_VEHICLES;
 
			}
 
			DrawWindowWidgets(w);
 
		}
 
		/* draw sorting criteria string */
 
		DrawString(85, 15, _vehicle_sort_listing[vl->sort_type], 0x10);
 
		/* draw arrow pointing up/down for ascending/descending sorting */
 
		DoDrawString(vl->flags & VL_DESC ? DOWNARROW : UPARROW, 69, 15, 0x10);
 

	
 
		max = min(w->vscroll.pos + w->vscroll.cap, vl->list_length);
 
		for (i = w->vscroll.pos; i < max; ++i) {
 
			Vehicle *v = GetVehicle(vl->sort_list[i].index);
 
			StringID str;
 

	
 
			assert(v->type == VEH_Road && v->owner == owner);
 

	
 
			DrawRoadVehImage(v, x + 22, y + 6, INVALID_VEHICLE);
 
			DrawVehicleProfitButton(v, x, y + 13);
 

	
 
			SetDParam(0, v->unitnumber);
 
			if (IsRoadVehInDepot(v))
 
			if (IsRoadVehInDepot(v)) {
 
				str = STR_021F;
 
			else
 
			} else {
 
				str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
 
			}
 
			DrawString(x, y + 2, str, 0);
 

	
 
			SetDParam(0, v->profit_this_year);
 
			SetDParam(1, v->profit_last_year);
 
			DrawString(x + 24, y + 18, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
 

	
 
			if (v->string_id != STR_SV_ROADVEH_NAME) {
 
				SetDParam(0, v->string_id);
 
				DrawString(x + 24, y, STR_01AB, 0);
 
			}
 

	
 
			y += PLY_WND_PRC__SIZE_OF_ROW_SMALL;
 
		}
 
		}	break;
 

	
 
	case WE_CLICK: {
 
		switch (e->click.widget) {
 
		case 3: /* Flip sorting method ascending/descending */
 
			vl->flags ^= VL_DESC;
 
			vl->flags |= VL_RESORT;
 
			_sorting.roadveh.order = !!(vl->flags & VL_DESC);
 
			SetWindowDirty(w);
 
			break;
 

	
 
		case 4: case 5:/* Select sorting criteria dropdown menu */
 
			ShowDropDownMenu(w, _vehicle_sort_listing, vl->sort_type, 5, 0, 0);
 
			return;
 
		case 7: { /* Matrix to show vehicles */
 
			uint32 id_v = (e->click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / PLY_WND_PRC__SIZE_OF_ROW_SMALL;
 

	
 
			if (id_v >= w->vscroll.cap) return; // click out of bounds
 

	
 
			id_v += w->vscroll.pos;
 

	
 
			{
 
				Vehicle *v;
 

	
 
				if (id_v >= vl->list_length) return; // click out of list bound
 

	
 
				v	= GetVehicle(vl->sort_list[id_v].index);
 

	
 
				assert(v->type == VEH_Road && v->owner == owner);
 

	
 
				ShowRoadVehViewWindow(v);
 
			}
 
		} break;
 

	
 
		case 9: /* Build new Vehicle */

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)