diff options
-rw-r--r-- | ai.h | 32 | ||||
-rw-r--r-- | ai_build.c | 2 | ||||
-rw-r--r-- | ai_new.c | 224 | ||||
-rw-r--r-- | ai_pathfinder.c | 2 | ||||
-rw-r--r-- | ai_shared.c | 39 | ||||
-rw-r--r-- | player.h | 54 |
6 files changed, 250 insertions, 103 deletions
@@ -10,7 +10,7 @@ * This can also alter the AI in a negative way. I will never claim these settings * are perfect, but don't change them if you don't know what the effect is. */ - + // How many times it the H multiplied. The higher, the more it will go straight to the // end point. The lower, how more it will find the route with the lowest cost. // also: the lower, the longer it takes before route is calculated.. @@ -136,11 +136,29 @@ // How many thick between building 2 vehicles #define AI_BUILD_VEHICLE_TIME_BETWEEN 74 +// How many days must there between vehicle checks +// The more often, the less non-money-making lines there will be +// but the unfair it may seem to a human player +#define AI_DAYS_BETWEEN_VEHICLE_CHECKS 30 + +// How money profit does a vehicle needs to make to stay in order +// This is the profit of this year + profit of last year +// But also for vehicles that are just one year old. In other words: +// Vehicles of 2 years do easier meet this setting then vehicles +// of one year. This is a very good thing. New vehicles are filtered, +// while old vehicles stay longer, because we do get less in return. +#define AI_MINIMUM_ROUTE_PROFIT 1000 + +// A vehicle is considered lost when he his cargo is more then 180 days old +#define AI_VEHICLE_LOST_DAYS 180 + +// How many times may the AI try to find a route before it gives up +#define AI_MAX_TRIES_FOR_SAME_ROUTE 8 + /* * End of defines */ - // This stops 90degrees curves static const byte _illegal_curves[6] = { 255, 255, // Horz and vert, don't have the effect @@ -174,6 +192,7 @@ enum { AI_STATE_GIVE_ORDERS, AI_STATE_START_VEHICLE, AI_STATE_REPAY_MONEY, + AI_STATE_CHECK_ALL_VEHICLES, AI_STATE_ACTION_DONE, AI_STATE_STOP, // Temporary function to stop the AI }; @@ -190,6 +209,7 @@ enum { AI_ACTION_BUS_ROUTE, AI_ACTION_TRUCK_ROUTE, AI_ACTION_REPAY_LOAN, + AI_ACTION_CHECK_ALL_VEHICLES, }; // Used for from_type/to_type @@ -199,6 +219,12 @@ enum { AI_INDUSTRY, }; +// Flags for in the vehicle +enum { + AI_VEHICLEFLAG_SELL = 1, + // Remember, flags must be in power of 2 +}; + #define AI_NO_CARGO 0xFF // Means that there is no cargo defined yet (used for industry) #define AI_NEED_CARGO 0xFE // Used when the AI needs to find out a cargo for the route #define AI_STATION_RANGE TILE_XY(TILE_X_MAX, TILE_Y_MAX) @@ -229,6 +255,8 @@ void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo int AiNew_GetRailDirection(uint tile_a, uint tile_b, uint tile_c); int AiNew_GetRoadDirection(uint tile_a, uint tile_b, uint tile_c); int AiNew_GetDirection(uint tile_a, uint tile_b); +bool AiNew_SetSpecialVehicleFlag(Player *p, Vehicle *v, uint flag); +uint AiNew_GetSpecialVehicleFlag(Player *p, Vehicle *v); // ai_build.c bool AiNew_Build_CompanyHQ(Player *p, uint tile); diff --git a/ai_build.c b/ai_build.c index 909c7e673..2614dc9d1 100644 --- a/ai_build.c +++ b/ai_build.c @@ -177,7 +177,7 @@ int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte fla // Build the tile res = DoCommandByTile(route[part], dir, 0, flag | DC_NO_WATER, CMD_BUILD_ROAD); // Currently, we ignore CMD_ERRORs! - if (res == CMD_ERROR && !IS_TILETYPE(route[part], MP_STREET) && (flag == DC_EXEC && !EnsureNoVehicle(route[part]))) { + if (res == CMD_ERROR && flag == DC_EXEC && !IS_TILETYPE(route[part], MP_STREET) && !EnsureNoVehicle(route[part])) { // Problem.. let's just abort it all! DEBUG(ai,0)("Darn, the route could not be builded.. aborting!"); p->ainew.state = AI_STATE_NOTHING; @@ -31,7 +31,7 @@ static void AiNew_State_FirstTime(Player *p) { // With this assert, that problem can never happen. assert(p->ainew.state == AI_STATE_FIRST_TIME); // We first have to init some things - + if (_current_player == 1) { ShowErrorMessage(-1, TEMP_AI_IN_PROGRESS, 0, 0); } @@ -44,8 +44,9 @@ static void AiNew_State_FirstTime(Player *p) { p->ainew.path_info.end_tile_tl = 0; p->ainew.path_info.end_tile_br = 0; p->ainew.pathfinder = new_AyStar_AiPathFinder(12, &p->ainew.path_info); - + p->ainew.idle = 0; + p->ainew.last_vehiclecheck_date = _date; // We ALWAYS start with a bus route.. just some basic money ;) p->ainew.action = AI_ACTION_BUS_ROUTE; @@ -92,19 +93,23 @@ static void AiNew_State_WakeUp(Player *p) { // 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 = 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 < 65) p->ainew.action = AI_ACTION_TRUCK_ROUTE; + if (c < 85) p->ainew.action = AI_ACTION_TRUCK_ROUTE; else p->ainew.action = AI_ACTION_BUS_ROUTE; } }/* else if (c < 200 && !_patches.ai_disable_veh_train) { @@ -112,14 +117,21 @@ static void AiNew_State_WakeUp(Player *p) { p->ainew.action = AI_ACTION_TRAIN_ROUTE; } }*/ + + 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 (_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; @@ -131,7 +143,12 @@ static void AiNew_State_WakeUp(Player *p) { 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) { @@ -155,7 +172,7 @@ static void AiNew_State_WakeUp(Player *p) { p->ainew.tbt = AI_TRUCK; return; } - + p->ainew.state = AI_STATE_NOTHING; } @@ -171,16 +188,16 @@ static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type) { Station *st; int 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 && CHANCE16(1,4)) return false; - + if (t->max_pass - t->act_pass < AI_CHECKCITY_NEEDED_CARGO && !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. @@ -219,12 +236,12 @@ static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type) { 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; } @@ -233,15 +250,15 @@ static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type) { Station *st; int count = 0; int j = 0; - + if (i->town != NULL && i->town->ratings[_current_player] < 0 && CHANCE16(1,4)) return false; - + // No limits on delevering stations! // Or for industry that does not give anything yet if (i->produced_cargo[0] == 0xFF || 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. @@ -288,7 +305,7 @@ static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type) { // All check are okay, so we can build here! return true; } - + return true; } @@ -296,14 +313,14 @@ static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type) { 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) { @@ -323,7 +340,7 @@ static void AiNew_State_LocateRoute(Player *p) { // 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) { @@ -331,7 +348,7 @@ static void AiNew_State_LocateRoute(Player *p) { 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) { @@ -341,7 +358,7 @@ static void AiNew_State_LocateRoute(Player *p) { else p->ainew.temp = 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 @@ -352,22 +369,22 @@ static void AiNew_State_LocateRoute(Player *p) { } 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 @@ -376,16 +393,16 @@ static void AiNew_State_LocateRoute(Player *p) { else p->ainew.temp = 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. - + if (p->ainew.from_type == AI_CITY && p->ainew.tbt == AI_BUS) { int max_cargo = DEREF_TOWN(p->ainew.from_ic)->max_pass + DEREF_TOWN(p->ainew.temp)->max_pass; max_cargo -= DEREF_TOWN(p->ainew.from_ic)->act_pass + DEREF_TOWN(p->ainew.temp)->act_pass; @@ -400,7 +417,7 @@ static void AiNew_State_LocateRoute(Player *p) { p->ainew.from_tile = 0; p->ainew.to_tile = 0; - + return; } } else if (p->ainew.tbt == AI_TRUCK) { @@ -469,7 +486,7 @@ static void AiNew_State_LocateRoute(Player *p) { } 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; @@ -513,7 +530,7 @@ static void AiNew_State_FindStation(Player *p) { Town *town = NULL; Industry *industry = NULL; assert(p->ainew.state == AI_STATE_FIND_STATION); - + if (p->ainew.from_tile == 0) { // First we scan for a station in the from-city if (p->ainew.from_type == AI_CITY) { @@ -626,11 +643,11 @@ static void AiNew_State_FindStation(Player *p) { 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 @@ -659,7 +676,7 @@ static void AiNew_State_FindStation(Player *p) { 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 @@ -683,18 +700,18 @@ static void AiNew_State_FindPath(Player *p) { 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; - + // 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... @@ -725,7 +742,7 @@ static void AiNew_State_FindDepot(Player *p) { 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<lengthof(_tileoffs_by_dir);j++) { @@ -747,7 +764,7 @@ static void AiNew_State_FindDepot(Player *p) { } } } - + // 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; @@ -761,7 +778,7 @@ static void AiNew_State_FindDepot(Player *p) { // Bridge or tunnel.. we can't place a depot there continue; } - + tile = p->ainew.path_info.route[i]; for (j=0;j<lengthof(_tileoffs_by_dir);j++) { @@ -835,7 +852,7 @@ static int AiNew_HowManyVehicles(Player *p) { max_cargo = DEREF_INDUSTRY(p->ainew.from_ic)->total_production[0]; else max_cargo = DEREF_INDUSTRY(p->ainew.to_ic)->total_production[0]; - + // This is because moving 60% is more then 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 @@ -858,16 +875,16 @@ static int AiNew_HowManyVehicles(Player *p) { 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); @@ -878,12 +895,12 @@ static void AiNew_State_VerifyRoute(Player *p) { } p->ainew.amount_veh = res; p->ainew.cur_veh = 0; - + // Check how much it it going to cost us.. for (i=0;i<res;i++) { p->ainew.new_cost += AiNew_Build_Vehicle(p, 0, DC_QUERY_COST); } - + // Now we know how much the route is going to cost us // Check if we have enough money for it! if (p->ainew.new_cost > p->player_money - AI_MINIMUM_MONEY) { @@ -892,7 +909,7 @@ static void AiNew_State_VerifyRoute(Player *p) { p->ainew.state = AI_STATE_NOTHING; return; } - + // Now we can build the route, check the direction of the stations! if (p->ainew.from_direction == AI_PATHFINDER_NO_DIRECTION) { p->ainew.from_direction = AiNew_GetDirection(p->ainew.path_info.route[p->ainew.path_info.route_length-1], p->ainew.path_info.route[p->ainew.path_info.route_length-2]); @@ -904,10 +921,10 @@ static void AiNew_State_VerifyRoute(Player *p) { p->ainew.from_tile = p->ainew.path_info.route[p->ainew.path_info.route_length-1]; if (p->ainew.to_tile == AI_STATION_RANGE) p->ainew.to_tile = p->ainew.path_info.route[0]; - + p->ainew.state = AI_STATE_BUILD_STATION; p->ainew.temp = 0; - + DEBUG(ai,1)("[AiNew] The route is set and buildable.. going to build it!"); } @@ -952,7 +969,7 @@ static void AiNew_State_BuildPath(Player *p) { // By let the counter count from AI_BUILDPATH_PAUSE to 0, we have a nice way :) if (--p->ainew.counter != 0) return; p->ainew.counter = (4 - _opt.diff.competitor_speed) * AI_BUILDPATH_PAUSE + 1; - + // Increase the building position p->ainew.path_info.position++; // Build route @@ -964,7 +981,7 @@ static void AiNew_State_BuildPath(Player *p) { static const byte _roadbits_by_dir[4] = {2,1,8,4}; // If they not queue, they have to go up and down to try again at a station... // We don't want that, so try building some road left or right of the station - short dir1, dir2, dir3; + int dir1, dir2, dir3; TileIndex tile; int i, r; for (i=0;i<2;i++) { @@ -983,11 +1000,11 @@ static void AiNew_State_BuildPath(Player *p) { if (dir2 > 3) dir2 = 0; dir3 = p->ainew.to_direction; } - + DoCommandByTile(tile, _roadbits_by_dir[dir1], 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); DoCommandByTile(tile, _roadbits_by_dir[dir2], 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); DoCommandByTile(tile, _roadbits_by_dir[dir3^2], 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - + dir1 = _tileoffs_by_dir[dir1]; dir2 = _tileoffs_by_dir[dir2]; dir3 = _tileoffs_by_dir[dir3]; @@ -1000,21 +1017,21 @@ static void AiNew_State_BuildPath(Player *p) { r = CMD_ERROR; if (IS_TILETYPE(tile+dir2, MP_CLEAR) || IS_TILETYPE(tile+dir2, MP_TREES)) - DoCommandByTile(tile+dir2, AiNew_GetRoadDirection(tile, tile+dir2, tile+dir2+dir2), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); + r = DoCommandByTile(tile+dir2, AiNew_GetRoadDirection(tile, tile+dir2, tile+dir2+dir2), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); if (r != CMD_ERROR) if (IS_TILETYPE(tile+dir2+dir2, MP_CLEAR) || IS_TILETYPE(tile+dir2+dir2, MP_TREES)) DoCommandByTile(tile+dir2+dir2, AiNew_GetRoadDirection(tile+dir2, tile+dir2+dir2, tile+dir2+dir2+dir2), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); r = CMD_ERROR; if (IS_TILETYPE(tile+dir3, MP_CLEAR) || IS_TILETYPE(tile+dir3, MP_TREES)) - DoCommandByTile(tile+dir3, AiNew_GetRoadDirection(tile, tile+dir3, tile+dir3+dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); + r = DoCommandByTile(tile+dir3, AiNew_GetRoadDirection(tile, tile+dir3, tile+dir3+dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); if (r != CMD_ERROR) if (IS_TILETYPE(tile+dir3+dir3, MP_CLEAR) || IS_TILETYPE(tile+dir3+dir3, MP_TREES)) DoCommandByTile(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; } @@ -1024,7 +1041,7 @@ static void AiNew_State_BuildPath(Player *p) { static void AiNew_State_BuildDepot(Player *p) { int res = 0; assert(p->ainew.state == AI_STATE_BUILD_DEPOT); - + if (IS_TILETYPE(p->ainew.depot_tile, MP_STREET) && _map5[p->ainew.depot_tile] & 0x20) { if (_map_owner[p->ainew.depot_tile] == _current_player) { // The depot is already builded! @@ -1036,18 +1053,18 @@ static void AiNew_State_BuildDepot(Player *p) { 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 + _tileoffs_by_dir[p->ainew.depot_direction])) return; - + res = AiNew_Build_Depot(p, p->ainew.depot_tile, p->ainew.depot_direction, DC_EXEC); if (res == CMD_ERROR) { 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 = 1; p->ainew.veh_main_id = (VehicleID)-1; @@ -1057,7 +1074,7 @@ static void AiNew_State_BuildDepot(Player *p) { 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! @@ -1070,7 +1087,7 @@ static void AiNew_State_BuildVehicle(Player *p) { // 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 (res == CMD_ERROR) { @@ -1095,10 +1112,10 @@ static void AiNew_State_BuildVehicle(Player *p) { static void AiNew_State_GiveOrders(Player *p) { int order, flags; assert(p->ainew.state == AI_STATE_GIVE_ORDERS); - + if (p->ainew.veh_main_id != (VehicleID)-1) { DoCommandByTile(0, p->ainew.veh_id + (p->ainew.veh_main_id << 16), 0, DC_EXEC, CMD_CLONE_ORDER); - + // Skip the first order if it is a second vehicle // This to make vehicles go different ways.. if (p->ainew.veh_id & 1) @@ -1108,7 +1125,7 @@ static void AiNew_State_GiveOrders(Player *p) { } else { p->ainew.veh_main_id = p->ainew.veh_id; } - + // When more then 1 vehicle, we send them to different directions order = 0; flags = (_map2[p->ainew.from_tile] << 8) | OT_GOTO_STATION; @@ -1136,7 +1153,7 @@ static void AiNew_State_GiveOrders(Player *p) { // Start the vehicle static void AiNew_State_StartVehicle(Player *p) { assert(p->ainew.state == AI_STATE_START_VEHICLE); - + // 3, 2, 1... go! (give START_STOP command ;)) DoCommandByTile(0, p->ainew.veh_id, 0, DC_EXEC, CMD_START_STOP_ROADVEH); // Try to build an other vehicle (that function will stop building when needed) @@ -1151,6 +1168,62 @@ static void AiNew_State_RepayMoney(Player *p) { p->ainew.state = AI_STATE_ACTION_DONE; } +static void AiNew_CheckVehicle(Player *p, Vehicle *v) { + // When a vehicle is under the 6 months, we don't check for anything + if (v->age < 180) return; + + // When a vehicle is older then 1 year, it should make money... + if (v->age > 360) { + // If both years together are not more then AI_MINIMUM_ROUTE_PROFIT, + // it is not worth the line I guess... + if (v->profit_last_year + v->profit_this_year < AI_MINIMUM_ROUTE_PROFIT || + (v->reliability * 100 >> 16) < 40) { + // There is a possibility that the route is fucked up... + if (v->cargo_days > AI_VEHICLE_LOST_DAYS) { + // The vehicle is lost.. check the route, or else, get the vehicle + // back to a depot + // TODO: make this piece of code + } + + + // We are already sending him back + if (AiNew_GetSpecialVehicleFlag(p, v) & AI_VEHICLEFLAG_SELL) { + if (v->type == VEH_Road && IsRoadDepotTile(v->tile) && + (v->vehstatus&VS_STOPPED)) { + // We are at the depot, sell the vehicle + DoCommandByTile(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH); + } + return; + } + + if (!AiNew_SetSpecialVehicleFlag(p, v, AI_VEHICLEFLAG_SELL)) return; + { + int res = 0; + if (v->type == VEH_Road) + res = DoCommandByTile(0, v->index, 0, DC_EXEC, CMD_SEND_ROADVEH_TO_DEPOT); + // This means we can not find a depot :s +// if (res == CMD_ERROR) + } + } + } +} + +// Checks all vehicles if they are still valid and make money and stuff +static void AiNew_State_CheckAllVehicles(Player *p) { + Vehicle *v; + + FOR_ALL_VEHICLES(v) { + if (v->type == 0) continue; + if (v->owner != p->index) continue; + // Currently, we only know how to handle road-vehicles + if (v->type != VEH_Road) continue; + + AiNew_CheckVehicle(p, v); + } + + p->ainew.state = AI_STATE_ACTION_DONE; +} + // Using the technique simular to the original AI // Keeps things logical // It really should be in the same order as the AI_STATE's are! @@ -1171,6 +1244,7 @@ static AiNew_StateFunction* const _ainew_state[] = { AiNew_State_GiveOrders, AiNew_State_StartVehicle, AiNew_State_RepayMoney, + AiNew_State_CheckAllVehicles, AiNew_State_ActionDone, NULL, }; @@ -1184,7 +1258,7 @@ void AiNewDoGameLoop(Player *p) { // If it is a human player, it is not an AI, so bubye! if (IS_HUMAN_PLAYER(_current_player)) return; - + if (p->ainew.state == AI_STATE_STARTUP) { // The AI just got alive! p->ainew.state = AI_STATE_FIRST_TIME; @@ -1193,10 +1267,10 @@ void AiNewDoGameLoop(Player *p) { // Only startup the AI return; } - + // We keep a ticker. We use it for competitor_speed p->ainew.tick++; - + // See what the speed is switch (_opt.diff.competitor_speed) { case 0: // Very slow diff --git a/ai_pathfinder.c b/ai_pathfinder.c index 01ce2198e..03439359f 100644 --- a/ai_pathfinder.c +++ b/ai_pathfinder.c @@ -187,7 +187,7 @@ static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *curr // This problem only is valid for tunnels: // When the last tile was not yet a tunnel, check if we enter from the right side.. if (!IS_TILETYPE(current->path.node.tile, MP_TUNNELBRIDGE) && (_map5[current->path.node.tile + _tiles_around[i]] & 0x80) == 0) { - if ((i^2) != (_map5[current->path.node.tile + _tiles_around[i]] & 3)) continue; + if (i != (_map5[current->path.node.tile + _tiles_around[i]] & 3)) continue; } } } diff --git a/ai_shared.c b/ai_shared.c index 3e2c4a930..9e7edaad0 100644 --- a/ai_shared.c +++ b/ai_shared.c @@ -1,7 +1,7 @@ #include "stdafx.h" #include "ttd.h" -#include "player.h" #include "ai.h" +#include "vehicle.h" int AiNew_GetRailDirection(uint tile_a, uint tile_b, uint tile_c) { // 0 = vert @@ -79,4 +79,39 @@ int AiNew_GetDirection(uint tile_a, uint tile_b) { if (GET_TILE_X(tile_a) < GET_TILE_X(tile_b)) return 2; return 0; } - + +// This functions looks up if this vehicle is special for this AI +// and returns his flag +uint AiNew_GetSpecialVehicleFlag(Player *p, Vehicle *v) { + int i; + for (i=0;i<AI_MAX_SPECIAL_VEHICLES;i++) { + if (p->ainew.special_vehicles[i].veh_id == v->index) { + return p->ainew.special_vehicles[i].flag; + } + } + + // Not found :( + return 0; +} + +bool AiNew_SetSpecialVehicleFlag(Player *p, Vehicle *v, uint flag) { + int i, new_id = -1; + for (i=0;i<AI_MAX_SPECIAL_VEHICLES;i++) { + if (p->ainew.special_vehicles[i].veh_id == v->index) { + p->ainew.special_vehicles[i].flag |= flag; + return true; + } + if (new_id == -1 && p->ainew.special_vehicles[i].veh_id == 0 && + p->ainew.special_vehicles[i].flag == 0) + new_id = i; + } + + // Out of special_vehicle spots :s + if (new_id == -1) { + DEBUG(ai, 1)("special_vehicles list is too small :("); + return false; + } + p->ainew.special_vehicles[new_id].veh_id = v->index; + p->ainew.special_vehicles[new_id].flag = flag; + return true; +} @@ -47,19 +47,19 @@ typedef struct PlayerAI { TileIndex cur_tile_a; byte cur_dir_a; byte start_dir_a; - + TileIndex start_tile_b; TileIndex cur_tile_b; byte cur_dir_b; byte start_dir_b; Vehicle *cur_veh; /* only used by some states */ - + AiBuildRec src, dst, mid1, mid2; - + VehicleID wagon_list[9]; byte order_list_blocks[20]; - + TileIndex banned_tiles[16]; byte banned_val[16]; } PlayerAI; @@ -76,58 +76,68 @@ typedef struct Ai_PathFinderInfo { byte route_extra[500]; // Some extra information about the route like bridge/tunnel int route_length; int position; // Current position in the build-path, needed to build the path - + bool rail_or_road; // true = rail, false = road } Ai_PathFinderInfo; +// The amount of memory reserved for the AI-special-vehicles +#define AI_MAX_SPECIAL_VEHICLES 100 + +typedef struct Ai_SpecialVehicle { + VehicleID veh_id; + uint32 flag; +} Ai_SpecialVehicle; + typedef struct PlayerAiNew { uint8 state; uint tick; uint idle; - + int temp; // A value used in more then one function, but it just temporary // The use is pretty simple: with this we can 'think' about stuff // in more then one tick, and more then one AI. A static will not // do, because they are not saved. This way, the AI is almost human ;) int counter; // For the same reason as temp, we have counter. It can count how // long we are trying something, and just abort if it takes too long - + // Pathfinder stuff Ai_PathFinderInfo path_info; AyStar *pathfinder; - + // Route stuff - + byte cargo; byte tbt; // train/bus/truck 0/1/2 AI_TRAIN/AI_BUS/AI_TRUCK int new_cost; - + byte action; - + uint last_id; // here is stored the last id of the searched city/industry - + uint last_vehiclecheck_date; // Used in CheckVehicle + Ai_SpecialVehicle special_vehicles[AI_MAX_SPECIAL_VEHICLES]; // Some vehicles have some special flags + TileIndex from_tile; TileIndex to_tile; - + byte from_direction; byte to_direction; - + bool from_deliver; // True if this is the station that GIVES cargo bool to_deliver; - + TileIndex depot_tile; byte depot_direction; - + byte amount_veh; // How many vehicles we are going to build in this route byte cur_veh; // How many vehicles did we bought? VehicleID veh_id; // Used when bought a vehicle VehicleID veh_main_id; // The ID of the first vehicle, for shared copy - + int from_ic; // ic = industry/city. This is the ID of them byte from_type; // AI_NO_TYPE/AI_CITY/AI_INDUSTRY int to_ic; byte to_type; - + } PlayerAiNew; @@ -155,12 +165,12 @@ typedef struct Player { TileIndex location_of_house; TileIndex last_build_coordinate; - + byte share_owners[4]; - + byte inaugurated_year; byte num_valid_stat_ent; - + byte quarters_of_bankrupcy; byte bankrupt_asked; // which players were asked about buying it? int16 bankrupt_timeout; @@ -170,7 +180,7 @@ typedef struct Player { byte is_ai; PlayerAI ai; PlayerAiNew ainew; - + int64 yearly_expenses[3][13]; PlayerEconomyEntry cur_economy; PlayerEconomyEntry old_economy[24]; |