home *** CD-ROM | disk | FTP | other *** search
- /**********************************************************************
- Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- ***********************************************************************/
- #include <stdio.h>
- #include <string.h>
-
- #include <settlers.h>
- #include <packets.h>
- #include <map.h>
- #include <game.h>
- #include <citytools.h>
- #include <aiunit.h>
- #include <gotohand.h>
- #include <assert.h>
- #include <cityhand.h>
- #include <unithand.h>
- #include <unitfunc.h>
- #include <maphand.h>
- #include <aicity.h>
- #include <unittools.h>
-
- extern struct move_cost_map warmap;
- signed short int minimap[MAP_MAX_WIDTH][MAP_MAX_HEIGHT];
- /* negative: in_city_radius, 0: unassigned, positive: city_des */
-
- /**************************************************************************
- ...
- **************************************************************************/
- const char *get_a_name(struct player *pplayer)
- {
- static char buf[80];
- static int x=0;
- sprintf(buf, "%s", city_name_suggestion(pplayer)); /* Not a Number -- Syela */
- if (!buf[0]) sprintf(buf, "city%d", x++);
- return buf;
- }
-
- /**************************************************************************
- ...
- **************************************************************************/
- int ai_do_build_city(struct player *pplayer, struct unit *punit)
- {
- int x,y;
- struct packet_unit_request req;
- struct city *pcity;
- req.unit_id=punit->id;
- strcpy(req.name, get_a_name(pplayer));
- x = punit->x; y = punit->y; /* Trevor Pering points out that punit gets freed */
- handle_unit_build_city(pplayer, &req);
- pcity=map_get_city(x, y); /* so we need to cache x and y for a very short time */
- if (!pcity)
- return 0;
- for (y=0;y<5;y++)
- for (x=0;x<5;x++) {
- if ((x==0 || x==4) && (y==0 || y==4))
- continue;
- light_square(pplayer, x+pcity->x-2, y+pcity->y-2, 0);
- }
- return 1;
- }
-
- int amortize(int b, int d)
- {
- int num = MORT - 1;
- int denom;
- int s = 1;
- assert(d >= 0);
- if (b < 0) { s = -1; b *= s; }
- while (d && b) {
- denom = 1;
- while (d >= 12 && !(b>>28) && !(denom>>27)) {
- b *= 3; /* this is a kluge but it is 99.9% accurate and saves time */
- denom *= 5; /* as long as MORT remains 24! -- Syela */
- d -= 12;
- }
- while (!(b>>25) && d && !(denom>>25)) {
- b *= num;
- denom *= MORT;
- d--;
- }
- if (denom > 1) {
- b = (b + (denom>>1)) / denom;
- }
- }
- return(b * s);
- }
-
- void generate_minimap(void)
- {
- int a, i, j;
-
- memset(minimap, 0, sizeof(minimap));
- for (a = 0; a < game.nplayers; a++) {
- city_list_iterate(game.players[a].cities, pcity)
- city_map_iterate(i, j) {
- minimap[map_adjust_x(pcity->x+i-2)][map_adjust_y(pcity->y+j-2)]--;
- }
- city_list_iterate_end;
- }
- }
-
- void remove_city_from_minimap(int x, int y)
- {
- int i, j, n, xx;
- /*printf("Removing (%d, %d) from minimap.\n", x, y);*/
- for (j = -4; j <= 4; j++) {
- if (y+j < 0 || y+j >= map.ysize) continue;
- for (i = -4; i <= 4; i++) {
- xx = map_adjust_x(x+i);
- n = i * i + j * j;
- if (n <= 5) {
- if (minimap[xx][y+j] < 0) minimap[xx][y+j]++;
- else minimap[xx][y+j] = 0;
- } else if (n <= 20) {
- if (minimap[xx][y+j] > 0) minimap[xx][y+j] = 0;
- }
- }
- }
- }
-
- void add_city_to_minimap(int x, int y)
- {
- int i, j, n, xx;
- /*printf("Adding (%d, %d) to minimap.\n", x, y);*/
- for (j = -4; j <= 4; j++) {
- if (y+j < 0 || y+j >= map.ysize) continue;
- for (i = -4; i <= 4; i++) {
- xx = map_adjust_x(x+i);
- n = i * i + j * j;
- if (n <= 5) {
- if (minimap[xx][y+j] < 0) minimap[xx][y+j]--;
- else minimap[xx][y+j] = -1;
- } else if (n <= 20) {
- if (minimap[xx][y+j] > 0) minimap[xx][y+j] = 0;
- }
- }
- }
- }
-
- void locally_zero_minimap(int x, int y)
- {
- int i, j, n, xx;
- for (j = -2; j <= 2; j++) {
- if (y+j < 0 || y+j >= map.ysize) continue;
- for (i = -2; i <= 2; i++) {
- xx = map_adjust_x(x+i);
- n = i * i + j * j;
- if (n <= 5) {
- if (minimap[xx][y+j] > 0) minimap[xx][y+j] = 0;
- }
- }
- }
- }
-
- int city_desirability(int x, int y)
- { /* this whole funct assumes G_REP^H^H^HDEMOCRACY -- Syela */
- int taken[5][5], food[5][5], shield[5][5], trade[5][5];
- int irrig[5][5], mine[5][5], road[5][5];
- int i, j, f, n = 0;
- int worst, b2, best, ii, jj, val, cur;
- int d = 0;
- int a, i0, j0; /* need some temp variables */
- int temp=0, tmp=0;
- int debug = 0;
- int g = 1;
- struct tile *ptile;
- int con, con2;
- int har, t, sh;
- struct tile_type *ptype;
- struct city *pcity;
-
- ptile = map_get_tile(x, y);
- if (ptile->terrain == T_OCEAN) return(0);
- pcity = map_get_city(x, y);
- if (pcity && pcity->size > 7) return(0);
- if (!pcity && minimap[x][y] < 0) return(0);
- if (!pcity && minimap[x][y] > 0) return(minimap[x][y]);
-
- har = is_terrain_near_tile(x, y, T_OCEAN);
-
- sh = SHIELD_WEIGHTING * MORT;
- t = TRADE_WEIGHTING * MORT; /* assuming DEMOCRACY now, much easier! - Syela */
-
- con = ptile->continent;
-
- /* not worth the computations AFAICT -- Syela
- city_list_iterate(pplayer->cities, acity)
- if (city_got_building(acity, B_PYRAMIDS)) g++;
- city_list_iterate_end;
- */
-
- memset(taken, 0, sizeof(taken));
- memset(food, 0, sizeof(food));
- memset(shield, 0, sizeof(shield));
- memset(trade, 0, sizeof(trade));
- memset(irrig, 0, sizeof(irrig));
- memset(mine, 0, sizeof(mine));
- memset(road, 0, sizeof(road));
-
- city_map_iterate(i, j) {
- if ((minimap[map_adjust_x(x+i-2)][map_adjust_y(y+j-2)] >= 0 && !pcity) ||
- (pcity && get_worker_city(pcity, i, j) == C_TILE_EMPTY)) {
- ptile = map_get_tile(x+i-2, y+j-2);
- con2 = ptile->continent;
- ptype = get_tile_type(ptile->terrain);
- food[i][j] = ((ptile->special&S_SPECIAL ? ptype->food_special : ptype->food) - 2) * MORT;
- if (i == 2 && j == 2) food[i][j] += 2 * MORT;
- if (ptype->irrigation_result == ptile->terrain && con2 == con) {
- if (ptile->special&S_IRRIGATION || (i == 2 && j == 2)) irrig[i][j] = MORT;
- else if (is_water_adjacent_to_tile(x+i-2, y+j-2) &&
- ptile->terrain != T_HILLS) irrig[i][j] = MORT - 9; /* KLUGE */
- /* all of these kluges are hardcoded amortize calls to save much CPU use -- Syela */
- } else if (ptile->terrain == T_OCEAN && har) food[i][j] += MORT; /* harbor */
- shield[i][j] = (ptile->special&S_SPECIAL ? ptype->shield_special : ptype->shield) * sh;
- if (i == 2 && j == 2 && !shield[i][j]) shield[i][j] = sh;
- if (ptile->terrain == T_OCEAN && har) shield[i][j] += sh;
- /* above line is not sufficiently tested. AI was building on shores, but not
- as far out in the ocean as possible, which limits growth in the very long
- term (after SEWER). These cities will all eventually have OFFSHORE, and
- I need to acknowledge that. I probably shouldn't treat it as free, but
- that's the easiest, and I doubt pathological behavior will result. -- Syela */
- if (ptile->special&S_MINE) mine[i][j] = (ptile->terrain == T_HILLS ? sh * 3 : sh);
- else if (ptile->terrain == T_HILLS && con2 == con) mine[i][j] = sh * 3 - 300; /* KLUGE */
- trade[i][j] =(ptile->special&S_SPECIAL ? ptype->trade_special : ptype->trade) * t;
- if (ptile->terrain == T_DESERT || ptile->terrain == T_GRASSLAND ||
- ptile->terrain == T_PLAINS) {
- if (ptile->special&S_ROAD || (i == 2 && j == 2)) road[i][j] = t;
- else if (con2 == con)
- road[i][j] = t - 70; /* KLUGE */ /* actualy exactly 70 1/2 */
- }
- if (trade[i][j]) trade[i][j] += t;
- else if (road[i][j]) road[i][j] += t;
- }
- }
-
- if (pcity) { /* quick-n-dirty immigration routine -- Syela */
- n = pcity->size;
- best = 0; ii = 0; jj = 0; /* blame -Wall -Werror for these */
- city_map_iterate(i, j) {
- cur = (food[i][j]) * food_weighting(n) + /* ignoring irrig on purpose */
- (shield[i][j] + mine[i][j]) +
- (trade[i][j] + road[i][j]);
- if (cur > best && (i != 2 || j != 2)) { best = cur; ii = i; jj = j; }
- }
- if (!best) return(0);
- val = (shield[ii][jj] + mine[ii][jj]) +
- (food[ii][jj] + irrig[ii][jj]) * FOOD_WEIGHTING + /* seems to be needed */
- (trade[ii][jj] + road[ii][jj]);
- val -= amortize(40 * SHIELD_WEIGHTING + (50 - 20 * g) * FOOD_WEIGHTING, 12);
- /* 12 is arbitrary; need deterrent to represent loss of a settlers -- Syela */
- /*printf("Desire to immigrate to %s = %d -> %d\n", pcity->name, val,
- (val * 100) / MORT / 70);*/
- return(val);
- }
-
- f = food[2][2] + irrig[2][2];
- if (!f) return(0); /* no starving cities, thank you! -- Syela */
- val = f * FOOD_WEIGHTING + /* this needs to be here, strange as it seems */
- (shield[2][2] + mine[2][2]) +
- (trade[2][2] + road[2][2]);
- taken[2][2]++;
- /* val is mort times the real value */
- /* treating harbor as free to approximate advantage of building boats. -- Syela */
- val += (4 * (get_tile_type(map_get_tile(x, y)->terrain)->defense_bonus) - 40) * SHIELD_WEIGHTING;
- /* don't build cities in danger!! FIX! -- Syela */
- val += 8 * MORT; /* one science per city */
- if (debug)
- printf("City value (%d, %d) = %d, har = %d, f = %d\n", x, y, val, har, f);
-
- for (n = 1; n <= 20 && f > 0; n++) {
- for (a = 1; a; a--) {
- best = 0; worst = -1; b2 = 0; i0 = 0; j0 = 0; ii = 0; jj = 0;
- city_map_iterate(i, j) {
- cur = (food[i][j]) * food_weighting(n) + /* ignoring irrig on purpose */
- (shield[i][j] + mine[i][j]) +
- (trade[i][j] + road[i][j]);
- if (!taken[i][j]) {
- if (cur > best) { b2 = best; best = cur; ii = i; jj = j; }
- else if (cur > b2) b2 = cur;
- } else if (i != 2 || j != 2) {
- if (cur < worst || worst < 0) { worst = cur; i0 = i; j0 = j; }
- }
- }
- if (!best) break;
- cur = amortize((shield[ii][jj] + mine[ii][jj]) +
- (food[ii][jj] + irrig[ii][jj]) * FOOD_WEIGHTING + /* seems to be needed */
- (trade[ii][jj] + road[ii][jj]), d);
- f += food[ii][jj] + irrig[ii][jj];
- if (cur > 0) val += cur;
- taken[ii][jj]++;
- if (debug)
- printf("Value of (%d, %d) = %d food = %d, type = %s, n = %d, d = %d\n",
- ii, jj, cur, (food[ii][jj] + irrig[ii][jj]),
- get_tile_type(map_get_tile(x + ii - 2, y + jj - 2)->terrain)->terrain_name, n, d);
-
- /* I hoped to avoid the following, but it seems I can't take ANY shortcuts
- in this unspeakable routine, so here comes even more CPU usage in order to
- get this EXACTLY right instead of just reasonably close. -- Syela */
- if (worst < b2 && worst >= 0) {
- cur = amortize((shield[i0][j0] + mine[i0][j0]) +
- (trade[i0][j0] + road[i0][j0]), d);
- f -= (food[i0][j0] + irrig[i0][j0]);
- val -= cur;
- taken[i0][j0]--;
- a++;
- if (debug)
- printf("REJECTING Value of (%d, %d) = %d food = %d, type = %s, n = %d, d = %d\n",
- i0, j0, cur, (food[i0][j0] + irrig[i0][j0]),
- get_tile_type(map_get_tile(x + i0 - 2, y + j0 - 2)->terrain)->terrain_name, n, d);
- }
- }
- if (!best) break;
- if (f > 0) d += (game.foodbox * MORT * n + (f*g) - 1) / (f*g);
- if (n == 4) {
- val -= amortize(40 * SHIELD_WEIGHTING + (50 - 20 * g) * FOOD_WEIGHTING, d); /* lers */
- temp = amortize(40 * SHIELD_WEIGHTING, d); /* temple */
- tmp = val;
- }
- } /* end while */
- if (n > 4) {
- if (val - temp > tmp) val -= temp;
- else val = tmp;
- }
- val -= 110 * SHIELD_WEIGHTING; /* WAG: walls, defenders */
- minimap[x][y] = val;
-
- if (debug)
- printf("Total value of (%d, %d) [%d workers] = %d\n", x, y, n, val);
- return(val);
- }
-
- /**************************************************************************
- ...
- **************************************************************************/
- void ai_manage_settler(struct player *pplayer, struct unit *punit)
- {
- punit->ai.control = 1;
- if (punit->ai.ai_role == AIUNIT_NONE) /* if BUILD_CITY must remain BUILD_CITY */
- punit->ai.ai_role = AIUNIT_AUTO_SETTLER;
- /* gonna handle city-building in the auto-settler routine -- Syela */
- return;
- }
-
- /*************************************************************************
- returns dy according to the wrap rules
- **************************************************************************/
- int make_dy(int y1, int y2)
- {
- int dy=y2-y1;
- if (dy<0) dy=-dy;
- return dy;
- }
-
- /*************************************************************************
- returns dx according to the wrap rules
- **************************************************************************/
- int make_dx(int x1, int x2)
- {
- int tmp;
- x1=map_adjust_x(x1);
- x2=map_adjust_x(x2);
- if(x1>x2)
- tmp=x1, x1=x2, x2=tmp;
-
- return MIN(x2-x1, map.xsize-x2+x1);
- }
-
- /**************************************************************************
- return 1 if there is already a unit on this square or one destined for it
- (via goto)
- **************************************************************************/
- int is_already_assigned(struct unit *myunit, struct player *pplayer, int x, int y)
- {
- x=map_adjust_x(x);
- y=map_adjust_y(y);
- if (same_pos(myunit->x, myunit->y, x, y) ||
- same_pos(myunit->goto_dest_x, myunit->goto_dest_y, x, y)) {
- /* I'm still not sure this is exactly right -- Syela */
- unit_list_iterate(map_get_tile(x, y)->units, punit)
- if (myunit==punit) continue;
- if (punit->owner!=pplayer->player_no)
- return 1;
- if (unit_flag(punit->type, F_SETTLERS) && unit_flag(myunit->type, F_SETTLERS))
- return 1;
- unit_list_iterate_end;
- return 0;
- }
- return(map_get_tile(x, y)->assigned & (1<<pplayer->player_no));
- }
-
- int old_is_already_assigned(struct unit *myunit, struct player *pplayer, int x, int y)
- {
- x=map_adjust_x(x);
- y=map_adjust_y(y);
- #ifdef RIDICULOUS
- if (same_pos(myunit->x, myunit->y, x, y))
- return 0;
- #endif
- unit_list_iterate(map_get_tile(x, y)->units, punit) {
- if (myunit==punit) continue;
- if (punit->owner!=pplayer->player_no)
- return 1;
- if (unit_flag(punit->type, F_SETTLERS) && unit_flag(myunit->type, F_SETTLERS))
- return 1;
- }
- unit_list_iterate_end;
- #ifndef RIDICULOUS
- if (same_pos(myunit->x, myunit->y, x, y))
- return 0;
- #endif
- unit_list_iterate(pplayer->units, punit) {
- if (myunit==punit) continue;
- if (punit->owner!=pplayer->player_no)
- return 1;
- if (same_pos(punit->goto_dest_x, punit->goto_dest_y, x, y) && unit_flag(punit->type, F_SETTLERS) && unit_flag(myunit->type,F_SETTLERS) && punit->activity==ACTIVITY_GOTO)
- return 1;
- if (same_pos(punit->goto_dest_x, punit->goto_dest_y, x, y) && !unit_flag(myunit->type, F_SETTLERS) && punit->activity==ACTIVITY_GOTO)
- return 1;
- }
- unit_list_iterate_end;
- return 0;
- }
-
- /* all of the benefit and ai_calc routines rewritten by Syela */
- /* to conform with city_tile_value and related calculations elsewhere */
- /* all of these functions are VERY CPU-inefficient and are being cached */
- /* I don't really want to rewrite them and possibly screw them up. */
- /* The cache should keep the CPU increase linear instead of quadratic. -- Syela */
-
- int ai_calc_pollution(struct city *pcity, struct player *pplayer, int i, int j)
- {
- int x, y, m;
- x = pcity->x + i - 2; y = pcity->y + j - 2;
- if (!(map_get_special(x, y) & S_POLLUTION)) return(0);
- map_clear_special(x, y, S_POLLUTION);
- m = city_tile_value(pcity, i, j, 0, 0);
- map_set_special(x, y, S_POLLUTION);
- return(m);
- }
-
- int is_wet(struct player *pplayer, int x, int y)
- {
- if (!map_get_known(x, y, pplayer) && !pplayer->ai.control) return 0;
- if (map_get_terrain(x,y) == T_OCEAN || map_get_terrain(x,y) == T_RIVER ||
- map_get_special(x,y)&S_IRRIGATION) return 1;
- return 0;
- }
-
- int ai_calc_irrigate(struct city *pcity, struct player *pplayer, int i, int j)
- {
- int x, y, m;
- struct tile *ptile;
- struct tile_type *type;
- x = pcity->x + i - 2; y = pcity->y + j - 2;
- ptile = map_get_tile(x, y);
- type=get_tile_type(ptile->terrain);
-
- /* changing terrain types?? */
-
- if((ptile->terrain==type->irrigation_result &&
- !(ptile->special&S_IRRIGATION) &&
- !(ptile->special&S_MINE) && !(ptile->city_id) &&
- (is_wet(pplayer,x,y) || is_wet(pplayer,x,y-1) || is_wet(pplayer,x,y+1) ||
- is_wet(pplayer,x-1,y) || is_wet(pplayer,x+1,y)))) {
- map_set_special(x, y, S_IRRIGATION);
- m = city_tile_value(pcity, i, j, 0, 0);
- map_clear_special(x, y, S_IRRIGATION);
- return(m);
- } else return(0);
- }
-
- int ai_calc_mine(struct city *pcity, struct player *pplayer, int i, int j)
- {
- int x, y, m;
- struct tile *ptile;
- x = pcity->x + i - 2; y = pcity->y + j - 2;
- ptile = map_get_tile(x, y);
- /* changing terrain types?? */
- if ((ptile->terrain == T_HILLS || ptile->terrain == T_MOUNTAINS) &&
- !(ptile->special&S_MINE)) {
- map_set_special(x, y, S_MINE);
- m = city_tile_value(pcity, i, j, 0, 0);
- map_clear_special(x, y, S_MINE);
- return(m);
- } else return(0);
- }
-
- int road_bonus(int x, int y, int spc)
- {
- int m = 0, k;
- int rd[12], te[12];
- int ii[12] = { -1, 0, 1, -1, 1, -1, 0, 1, 0, -2, 2, 0 };
- int jj[12] = { -1, -1, -1, 0, 0, 1, 1, 1, -2, 0, 0, 2 };
- struct tile *ptile;
- for (k = 0; k < 12; k++) {
- ptile = map_get_tile(x + ii[k], y + jj[k]);
- rd[k] = ptile->special&spc;
- te[k] = (ptile->terrain == T_MOUNTAINS || ptile->terrain == T_OCEAN);
- if (!rd[k]) {
- unit_list_iterate(ptile->units, punit)
- if (punit->activity == ACTIVITY_ROAD || punit->activity == ACTIVITY_RAILROAD)
- rd[k] = spc;
- unit_list_iterate_end;
- }
- }
-
- if (rd[0] && !rd[1] && !rd[3] && (!rd[2] || !rd[8]) &&
- (!te[2] || !te[4] || !te[7] || !te[6] || !te[5])) m++;
- if (rd[2] && !rd[1] && !rd[4] && (!rd[7] || !rd[10]) &&
- (!te[0] || !te[3] || !te[7] || !te[6] || !te[5])) m++;
- if (rd[5] && !rd[6] && !rd[3] && (!rd[5] || !rd[11]) &&
- (!te[2] || !te[4] || !te[7] || !te[1] || !te[0])) m++;
- if (rd[7] && !rd[6] && !rd[4] && (!rd[0] || !rd[9]) &&
- (!te[2] || !te[3] || !te[0] || !te[1] || !te[5])) m++;
-
- if (rd[1] && !rd[4] && !rd[3] && (!te[5] || !te[6] || !te[7])) m++;
- if (rd[3] && !rd[1] && !rd[6] && (!te[2] || !te[4] || !te[7])) m++;
- if (rd[4] && !rd[1] && !rd[6] && (!te[0] || !te[3] || !te[5])) m++;
- if (rd[6] && !rd[4] && !rd[3] && (!te[0] || !te[1] || !te[2])) m++;
- return(m);
- }
-
- int ai_calc_road(struct city *pcity, struct player *pplayer, int i, int j)
- {
- int x, y, m;
- struct tile *ptile;
- x = pcity->x + i - 2; y = pcity->y + j - 2;
- ptile = map_get_tile(x, y);
- if (ptile->terrain != T_OCEAN && (ptile->terrain != T_RIVER ||
- get_invention(pplayer, A_BRIDGE) == TECH_KNOWN) &&
- !(ptile->special&S_ROAD)) {
- ptile->special|=S_ROAD; /* have to do this to avoid reset_move_costs -- Syela */
- m = city_tile_value(pcity, i, j, 0, 0);
- ptile->special&=~S_ROAD;
- return(m);
- } else return(0);
- }
-
- int ai_calc_railroad(struct city *pcity, struct player *pplayer, int i, int j)
- {
- int x, y, m;
- struct tile *ptile;
- x = pcity->x + i - 2; y = pcity->y + j - 2;
- ptile = map_get_tile(x, y);
- if (ptile->terrain != T_OCEAN &&
- get_invention(pplayer, A_RAILROAD) == TECH_KNOWN &&
- !(ptile->special&S_RAILROAD)) {
- if (ptile->special&S_ROAD) {
- ptile->special|=S_RAILROAD;
- m = city_tile_value(pcity, i, j, 0, 0);
- ptile->special&=~S_RAILROAD;
- return(m);
- } else {
- map_set_special(x, y, S_ROAD | S_RAILROAD);
- m = city_tile_value(pcity, i, j, 0, 0);
- map_clear_special(x, y, S_ROAD | S_RAILROAD);
- return(m);
- }
- } else return(0);
- /* bonuses for adjacent railroad tiles */
- }
-
- /*************************************************************************
- return how good this square is for a new city.
- **************************************************************************/
- int is_ok_city_spot(int x, int y)
- {
- int dx, dy;
- int i;
- switch (map_get_terrain(x,y)) {
- case T_OCEAN:
- case T_UNKNOWN:
- case T_MOUNTAINS:
- case T_FOREST:
- case T_HILLS:
- case T_ARCTIC:
- case T_DESERT:
- case T_JUNGLE:
- case T_SWAMP:
- case T_TUNDRA:
- case T_LAST:
- return 0;
- case T_GRASSLAND:
- case T_PLAINS:
- case T_RIVER:
- break;
- default:
- break;
- }
- for (i = 0; i < game.nplayers; i++) {
- city_list_iterate(game.players[i].cities, pcity) {
- if (map_distance(x, y, pcity->x, pcity->y)<=8) {
- dx=make_dx(pcity->x, x);
- dy=make_dy(pcity->y, y);
- if (dx<=5 && dy<5)
- return 0;
- if (dx<5 && dy<=5)
- return 0;
- }
- }
- city_list_iterate_end;
- }
- return 1;
- }
-
- /*************************************************************************
- return the city if any that is in range of this square.
- **************************************************************************/
- int in_city_radius(int x, int y)
- {
- int i, j;
- city_map_iterate(i, j) {
- if (map_get_tile(x+i-2, y+j-2)->city_id) return 1;
- }
- return 0;
- }
-
- /**************************************************************************
- simply puts the settler unit into goto
- **************************************************************************/
- int auto_settler_do_goto(struct player *pplayer, struct unit *punit, int x, int y)
- {
- punit->goto_dest_x=map_adjust_x(x);
- punit->goto_dest_y=map_adjust_y(y);
- punit->activity=ACTIVITY_GOTO;
- punit->activity_count=0;
- send_unit_info(0, punit, 0);
- do_unit_goto(pplayer, punit);
- return 1;
- }
-
- int find_boat(struct player *pplayer, int *x, int *y, int cap)
- { /* this function uses the current warmap, whatever it may hold */
- /* unit is no longer an arg! we just trust the map! -- Syela */
- int best = 22, id = 0; /* arbitrary maximum distance, I will admit! */
- unit_list_iterate(pplayer->units, aunit)
- if (get_transporter_capacity(aunit) &&
- !unit_flag(aunit->type, F_CARRIER | F_SUBMARINE)) {
- if (warmap.cost[aunit->x][aunit->y] < best &&
- (warmap.cost[aunit->x][aunit->y] == 0 ||
- is_transporter_with_free_space(pplayer, aunit->x, aunit->y) >= cap)) {
- id = aunit->id;
- best = warmap.cost[aunit->x][aunit->y];
- *x = aunit->x;
- *y = aunit->y;
- }
- }
- unit_list_iterate_end;
- if (id) return(id);
- #ifdef ALLOW_VIRTUAL_BOATS
- city_list_iterate(pplayer->cities, pcity)
- if (pcity->is_building_unit &&
- unit_types[pcity->currently_building].transport_capacity &&
- !unit_flag(pcity->currently_building, F_CARRIER | F_SUBMARINE)) {
- if (warmap.cost[pcity->x][pcity->y] < best) {
- id = pcity->id;
- best = warmap.cost[pcity->x][pcity->y];
- *x = pcity->x;
- *y = pcity->y;
- }
- }
- city_list_iterate_end;
- #endif
- return(id);
- }
-
- struct unit *other_passengers(struct unit *punit)
- {
- unit_list_iterate(map_get_tile(punit->x, punit->y)->units, aunit)
- if (is_ground_unit(aunit) && aunit != punit) return aunit;
- unit_list_iterate_end;
- return 0;
- }
-
- /********************************************************************
- find some work for the settler
- *********************************************************************/
- int auto_settler_findwork(struct player *pplayer, struct unit *punit)
- {
- int gx,gy;
- int co=map_get_continent(punit->x, punit->y);
- int t=0, v=0, v2;
- int x, y, z, i, j, ww = 99999; /* ww = 0 leads to v2=0 activities being OK */
- int m = unit_types[punit->type].move_rate;
- int food, val, w, a, b, d, fu;
- struct city *mycity = map_get_city(punit->x, punit->y);
- struct unit *ferryboat;
- int boatid, bx, by;
- int id = punit->id;
- int near;
- int nav = (get_invention(pplayer, A_NAVIGATION) == TECH_KNOWN);
- struct ai_choice choice; /* for nav want only */
-
- choice.type = 1;
- choice.choice = U_CARAVEL;
- choice.want = 0; /* will change as needed */
-
- if (punit->id) fu = 30; /* fu is estimated food cost to produce settler -- Syela */
- else {
- if (mycity->size == 1) fu = 20;
- else fu = 40 * (mycity->size - 1) / mycity->size;
- if (city_got_building(mycity, B_GRANARY) ||
- city_affected_by_wonder(mycity, B_PYRAMIDS)) fu -= 20;
- }
-
- gx=-1;
- gy=-1;
- /* iterating over the whole map is just ridiculous. let's only look at
- our own cities. The old method wasted billions of CPU cycles and led to
- AI settlers improving enemy cities. */ /* arguably should include city_spot */
- generate_warmap(mycity, punit);
- food = (get_government(pplayer->player_no) > G_COMMUNISM ? 2 : 1);
- if (punit->id && !punit->homecity) food = 0; /* thanks, Peter */
- city_list_iterate(pplayer->cities, pcity)
- w = worst_worker_tile_value(pcity);
- city_map_iterate(i, j) {
- if (get_worker_city(pcity, i, j) == C_TILE_UNAVAILABLE) continue;
- x = map_adjust_x(pcity->x + i - 2);
- y = map_adjust_y(pcity->y + j - 2);
- if (map_get_continent(x, y) == co &&
- warmap.cost[x][y] <= THRESHOLD * m &&
- !enemies_at(punit, x, y) && /* helps some, not nearly enough */
- !is_already_assigned(punit, pplayer, x, y)) {
- /* calling is_already_assigned once instead of four times for obvious reasons */
- /* structure is much the same as it once was but subroutines are not -- Syela */
- z = (warmap.cost[x][y]) / m;
- val = city_tile_value(pcity, i, j, 0, 0);
- if (w > val && (i != 2 || j != 2)) val = w;
- /* perhaps I should just subtract w rather than val but I prefer this -- Syela */
-
- v2 = pcity->ai.irrigate[i][j];
- b = (v2 - val)<<6; /* arbitrary, for rounding errors */
- if (b > 0) {
- d = (map_build_irrigation_time(x, y) * 3 + m - 1) / m + z;
- a = amortize(b, d);
- v2 = ((a * b) / (MAX(1, b - a)))>>6;
- } else v2 = 0;
- if (v2>v || (v2 == v && val > ww)) {
- /*printf("Replacing (%d, %d) = %d with (%d, %d) I=%d d=%d, b=%d\n",
- gx, gy, v, x, y, v2, d, b);*/
- t=ACTIVITY_IRRIGATE;
- v=v2; gx=x; gy=y; ww=val;
- }
-
- v2 = pcity->ai.mine[i][j];
- b = (v2 - val)<<6; /* arbitrary, for rounding errors */
- if (b > 0) {
- d = (map_build_mine_time(x, y) * 3 + m - 1) / m + z;
- a = amortize(b, d);
- v2 = ((a * b) / (MAX(1, b - a)))>>6;
- } else v2 = 0;
- if (v2>v || (v2 == v && val > ww)) {
- /*printf("Replacing (%d, %d) = %d with (%d, %d) M=%d d=%d, b=%d\n",
- gx, gy, v, x, y, v2, d, b);*/
- t=ACTIVITY_MINE;
- v=v2; gx=x; gy=y; ww=val;
- }
-
- if (!(map_get_tile(x,y)->special&S_ROAD)) {
- v2 = pcity->ai.road[i][j];
- if (v2) v2 = MAX(v2, val) + road_bonus(x, y, S_ROAD) * 8;
- /* guessing about the weighting based on unit upkeeps; * 11 was too high! -- Syela */
- b = (v2 - val)<<6; /* arbitrary, for rounding errors */
- if (b > 0) {
- d = (map_build_road_time(x, y) * 3 + 3 + m - 1) / m + z; /* uniquely weird! */
- a = amortize(b, d);
- v2 = ((a * b) / (MAX(1, b - a)))>>6;
- } else v2 = 0;
- if (v2>v || (v2 == v && val > ww)) {
- /*printf("Replacing (%d, %d) = %d with (%d, %d) R=%d d=%d, b=%d\n",
- gx, gy, v, x, y, v2, d, b);*/
- t=ACTIVITY_ROAD;
- v=v2; gx=x; gy=y; ww=val;
- }
-
- v2 = pcity->ai.railroad[i][j];
- if (v2) v2 = MAX(v2, val) + road_bonus(x, y, S_RAILROAD) * 4;
- b = (v2 - val)<<6; /* arbitrary, for rounding errors */
- if (b > 0) {
- d = (3 * 3 + 3 * map_build_road_time(x,y) + 3 + m - 1) / m + z;
- a = amortize(b, d);
- v2 = ((a * b) / (MAX(1, b - a)))>>6;
- } else v2 = 0;
- if (v2>v || (v2 == v && val > ww)) {
- t=ACTIVITY_ROAD;
- v=v2; gx=x; gy=y; ww=val;
- }
- } else {
- v2 = pcity->ai.railroad[i][j];
- if (v2) v2 = MAX(v2, val) + road_bonus(x, y, S_RAILROAD) * 4;
- b = (v2 - val)<<6; /* arbitrary, for rounding errors */
- if (b > 0) {
- d = (3 * 3 + m - 1) / m + z;
- a = amortize(b, d);
- v2 = ((a * b) / (MAX(1, b - a)))>>6;
- } else v2 = 0;
- if (v2>v || (v2 == v && val > ww)) {
- t=ACTIVITY_RAILROAD;
- v=v2; gx=x; gy=y; ww=val;
- }
- } /* end else */
-
- v2 = pcity->ai.detox[i][j];
- if (v2) v2 += pplayer->ai.warmth;
- b = (v2 - val)<<6; /* arbitrary, for rounding errors */
- if (b > 0) {
- d = (3 * 3 + z + m - 1) / m + z;
- a = amortize(b, d);
- v2 = ((a * b) / (MAX(1, b - a)))>>6;
- } else v2 = 0;
- if (v2>v || (v2 == v && val > ww)) {
- t=ACTIVITY_POLLUTION;
- v=v2; gx=x; gy=y; ww=val;
- }
- } /* end if we are a legal destination */
- } /* end city map iterate */
- city_list_iterate_end;
-
- v = (v - food * FOOD_WEIGHTING) * 100 / (40 + fu);
- if (v < 0) v = 0; /* Bad Things happen without this line! :( -- Syela */
-
- boatid = find_boat(pplayer, &bx, &by, 1); /* might need 2 for body */
- if ((ferryboat = unit_list_find(&(map_get_tile(punit->x, punit->y)->units), boatid)))
- really_generate_warmap(mycity, ferryboat, SEA_MOVING);
-
- if (pplayer->ai.control) { /* don't want to make cities otherwise */
- if (punit->ai.ai_role == AIUNIT_BUILD_CITY) {
- remove_city_from_minimap(punit->goto_dest_x, punit->goto_dest_y);
- }
- punit->ai.ai_role = AIUNIT_AUTO_SETTLER; /* here and not before! -- Syela */
- for (j = -11; j <= 11; j++) { /* hope this is far enough -- Syela */
- y = punit->y + j;
- if (y < 0 || y > map.ysize) continue;
- for (i = -11; i <= 11; i++) {
- x = map_adjust_x(punit->x + i);
- w = 0;
- near = (MAX((MAX(i, -i)),(MAX(j, -j))));
- if (!is_already_assigned(punit, pplayer, x, y) &&
- map_get_terrain(x, y) != T_OCEAN &&
- (near < 8 || map_get_continent(x, y) != co)) {
- if (ferryboat) { /* already aboard ship, can use real warmap */
- if (!is_terrain_near_tile(x, y, T_OCEAN)) z = 9999;
- else z = warmap.seacost[x][y] * m / unit_types[ferryboat->type].move_rate;
- } else if (!goto_is_sane(pplayer, punit, x, y, 1)) {
- if (!is_terrain_near_tile(x, y, T_OCEAN)) z = 9999;
- else if (boatid) {
- if (!punit->id && mycity->id == boatid) w = 1;
- z = warmap.cost[bx][by] + real_map_distance(bx, by, x, y) + m;
- } else if (punit->id || !is_terrain_near_tile(mycity->x,
- mycity->y, T_OCEAN)) z = 9999;
- else {
- z = warmap.seacost[x][y] * m / 9; /* this should be fresh */
- /* the only thing that could have munged the seacost is the ferryboat code
- in k_s_w/f_s_t_k, but only if find_boat succeeded */
- w = 1;
- }
- } else z = warmap.cost[x][y];
- d = z / m;
- /* without this, the computer will go 6-7 tiles from X to build a city at Y */
- d *= 2;
- /* and then build its NEXT city halfway between X and Y. -- Syela */
- z = city_desirability(x, y);
- v2 = amortize(z, d);
-
- b = (food * FOOD_WEIGHTING) * MORT;
- if (map_get_continent(x, y) != co) b += SHIELD_WEIGHTING * MORT;
- v2 -= (b - amortize(b, d));
- /* deal with danger Real Soon Now! -- Syela */
- /* v2 is now the value over mort turns */
- v2 = (v2 * 100) / MORT / ((w ? 80 : 40) + fu);
-
- if (v && map_get_city(x, y)) v2 = 0;
- /* I added a line to discourage settling in existing cities, but it was
- inadequate. It may be true that in the short-term building infrastructure
- on tiles that won't be worked for ages is not as useful as helping other
- cities grow and then building engineers later, but this is short-sighted.
- There was also a problem with workers suddenly coming onto unimproved tiles,
- either through city growth or de-elvisization, settlers being built and
- improving those tiles, and then immigrating shortly thereafter. -- Syela */
-
- /*if (w) printf("%s: v = %d, w = 1, v2 = %d\n", mycity->name, v, v2);*/
- if (map_get_continent(x, y) != co && !nav && near >= 8) {
- if (v2 > choice.want && !punit->id) choice.want = v2;
- /*printf("%s@(%d, %d) city_des (%d, %d) = %d, v2 = %d, d = %d\n",
- (punit->id ? unit_types[punit->type].name : mycity->name),
- punit->x, punit->y, x, y, z, v2, d);*/
- } else if (v2 > v) {
- if (w) {
- t = ACTIVITY_UNKNOWN; /* flag */
- v = v2; gx = -1; gy = -1;
- } else {
- t = ACTIVITY_UNKNOWN; /* flag */
- v = v2; gx = x; gy = y;
- }
- }
- }
- }
- }
- }
-
- choice.want -= v;
- if (choice.want > 0) ai_choose_ferryboat(pplayer, mycity, &choice);
-
- /*if (map_get_terrain(punit->x, punit->y) == T_OCEAN)
- printf("Punit %d@(%d,%d) wants to %d at (%d,%d) with desire %d\n",
- punit->id, punit->x, punit->y, t, gx, gy, v);*/
- /* I had the return here, but it led to stupidity where several engineers would
- be built to solve one problem. Moving the return down will solve this. -- Syela */
-
- if (gx!=-1 && gy!=-1) {
- map_get_tile(gx, gy)->assigned =
- map_get_tile(gx, gy)->assigned | 1<<pplayer->player_no;
- }
-
- if (!punit->id) { /* has to be before we try to send_unit_info()! -- Syela */
- /* printf("%s (%d, %d) settler-want = %d, task = %d, target = (%d, %d)\n",
- mycity->name, mycity->x, mycity->y, v, t, gx, gy);*/
- if (gx < 0 && gy < 0) return(0 - v);
- return(v); /* virtual unit for assessing settler want */
- }
-
- if (gx!=-1 && gy!=-1) {
- if (t == ACTIVITY_UNKNOWN) {
- punit->ai.ai_role = AIUNIT_BUILD_CITY;
- add_city_to_minimap(gx, gy);
- } else punit->ai.ai_role = AIUNIT_AUTO_SETTLER;
- if (!same_pos(gx, gy, punit->x, punit->y)) {
- if (!goto_is_sane(pplayer, punit, gx, gy, 1) ||
- (ferryboat && goto_is_sane(pplayer, ferryboat, gx, gy, 1) &&
- (!is_tiles_adjacent(punit->x, punit->y, gx, gy) ||
- !could_unit_move_to_tile(punit, punit->x, punit->y, gx,gy)))) {
- punit->ai.ferryboat = find_boat(pplayer, &x, &y, 1); /* might need 2 */
- /*printf("%d@(%d, %d): Looking for BOAT.\n", punit->id, punit->x, punit->y);*/
- if (!same_pos(x, y, punit->x, punit->y)) {
- auto_settler_do_goto(pplayer, punit, x, y);
- if (!unit_list_find(&pplayer->units, id)) return(0); /* died */
- }
- ferryboat = unit_list_find(&(map_get_tile(punit->x, punit->y)->units),
- punit->ai.ferryboat);
- punit->goto_dest_x = gx;
- punit->goto_dest_y = gy;
- if (ferryboat && (!ferryboat->ai.passenger ||
- ferryboat->ai.passenger == punit->id)) {
- /*printf("We have FOUND BOAT, %d ABOARD %d@(%d,%d)->(%d,%d).\n", punit->id,
- ferryboat->id, punit->x, punit->y, gx, gy);*/
- set_unit_activity(punit, ACTIVITY_SENTRY); /* kinda cheating -- Syela */
- ferryboat->ai.passenger = punit->id;
- auto_settler_do_goto(pplayer, ferryboat, gx, gy);
- set_unit_activity(punit, ACTIVITY_IDLE);
- } /* need to zero pass & ferryboat at some point. */
- }
- if (goto_is_sane(pplayer, punit, gx, gy, 1) && punit->moves_left &&
- ((!ferryboat) || other_passengers(punit) ||
- is_tiles_adjacent(punit->x, punit->y, gx, gy))) {
- /* Two settlers on a boat led to boats yo-yoing back and forth.
- Therefore we need to let them disembark at this point. -- Syela */
- auto_settler_do_goto(pplayer, punit, gx, gy);
- if (!unit_list_find(&pplayer->units, id)) return(0); /* died */
- punit->ai.ferryboat = 0;
- }
- }
- } else punit->ai.control=0;
-
- if (punit->ai.control && punit->moves_left && punit->activity == ACTIVITY_IDLE) {
- /* if players can build with 0 moves left, so should the AI */
- if (same_pos(gx, gy, punit->x, punit->y)) {
- if (t == ACTIVITY_UNKNOWN) {
- remove_city_from_minimap(gx, gy); /* yeah, I know. -- Syela */
- ai_do_build_city(pplayer, punit);
- return(0);
- }
- set_unit_activity(punit, t);
- send_unit_info(0, punit, 0);
- return(0);
- }
- }
- return(0);
- }
-
- void initialize_infrastructure_cache(struct city *pcity)
- {
- int i, j;
- struct player *pplayer = &game.players[pcity->owner];
- city_map_iterate(i, j) {
- pcity->ai.detox[i][j] = ai_calc_pollution(pcity, pplayer, i, j);
- pcity->ai.mine[i][j] = ai_calc_mine(pcity, pplayer, i, j);
- pcity->ai.irrigate[i][j] = ai_calc_irrigate(pcity, pplayer, i, j);
- pcity->ai.road[i][j] = ai_calc_road(pcity, pplayer, i, j);
- /* gonna handle road_bo dynamically for now since it can change
- as punits arrive at adjacent tiles and start laying road -- Syela */
- pcity->ai.railroad[i][j] = ai_calc_railroad(pcity, pplayer, i, j);
- }
- }
-
- /**************************************************************************
- run through all the players settlers and let those on ai.control work
- automagically
- **************************************************************************/
- void auto_settlers_player(struct player *pplayer)
- {
- #ifdef CHRONO
- int sec, usec;
- struct timeval tv;
- gettimeofday(&tv, 0);
- sec = tv.tv_sec; usec = tv.tv_usec;
- #endif
- city_list_iterate(pplayer->cities, pcity)
- initialize_infrastructure_cache(pcity); /* saves oodles of time -- Syela */
- city_list_iterate_end;
- pplayer->ai.warmth = WARMING_FACTOR * total_player_citizens(pplayer) * 10 *
- (game.globalwarming + game.heating) / (map.xsize *
- map.ysize * map.landpercent * 2); /* threat of warming */
- /*printf("Warmth = %d, game.globalwarming=%d\n", pplayer->ai.warmth, game.globalwarming);*/
- unit_list_iterate(pplayer->units, punit) {
- /* printf("%s's settler at (%d, %d)\n", pplayer->name, punit->x, punit->y); */
- if (punit->ai.control) {
- /* printf("Is ai controlled.\n");*/
- if(punit->activity == ACTIVITY_SENTRY)
- set_unit_activity(punit, ACTIVITY_IDLE);
- if (punit->activity == ACTIVITY_GOTO && punit->moves_left)
- set_unit_activity(punit, ACTIVITY_IDLE);
- if (punit->activity == ACTIVITY_IDLE)
- auto_settler_findwork(pplayer, punit);
- /* printf("Has been processed.\n"); */
- }
- }
- unit_list_iterate_end;
- #ifdef CHRONO
- gettimeofday(&tv, 0);
- printf("%s's autosettlers consumed %d microseconds.\n", pplayer->name,
- (tv.tv_sec - sec) * 1000000 + (tv.tv_usec - usec));
- #endif
- }
-
- void assign_settlers_player(struct player *pplayer)
- {
- short i = 1<<pplayer->player_no;
- struct tile *ptile;
- unit_list_iterate(pplayer->units, punit)
- if (unit_flag(punit->type, F_SETTLERS)) {
- if (punit->activity == ACTIVITY_GOTO) {
- ptile = map_get_tile(punit->goto_dest_x, punit->goto_dest_y);
- ptile->assigned = ptile->assigned | i; /* assigned for us only */
- } else {
- ptile = map_get_tile(punit->x, punit->y);
- ptile->assigned = 32767; /* assigned for everyone */
- }
- } else {
- ptile = map_get_tile(punit->x, punit->y);
- ptile->assigned = ptile->assigned | (32767 ^ i); /* assigned for everyone else */
- }
- unit_list_iterate_end;
- }
-
- void assign_settlers()
- {
- int i, x, y;
- for (x = 0; x < map.xsize; x++)
- for (y = 0; y < map.xsize; y++)
- map_get_tile(x, y)->assigned = 0;
-
- for (i = 0; i < game.nplayers; i++) {
- assign_settlers_player(&game.players[i]);
- }
- }
-
- /**************************************************************************
- Do the auto_settler stuff for all the players.
- TODO: This should be moved into the update_player loop i think
- **************************************************************************/
- void auto_settlers()
- {
- int i;
- assign_settlers();
- for (i = 0; i < game.nplayers; i++) {
- auto_settlers_player(&game.players[i]);
- }
- }
-
- void contemplate_settling(struct player *pplayer, struct city *pcity)
- {
- struct unit virtualunit;
- int want;
- /* used to use old crappy formulas for settler want, but now using actual want! */
- memset(&virtualunit, 0, sizeof(struct unit));
- virtualunit.id = 0;
- virtualunit.owner = pplayer->player_no;
- virtualunit.x = pcity->x;
- virtualunit.y = pcity->y;
- virtualunit.type = (can_build_unit(pcity, U_ENGINEERS) ? U_ENGINEERS : U_SETTLERS);
- virtualunit.moves_left = unit_types[virtualunit.type].move_rate;
- virtualunit.hp = 20;
- want = auto_settler_findwork(pplayer, &virtualunit);
- unit_list_iterate(pplayer->units, qpass)
- if (qpass->ai.ferryboat == pcity->id) want = -199;
- unit_list_iterate_end;
- pcity->ai.settler_want = want;
- }
-