? ai/aisettler.c ? ai/aisettler.h Index: ai/Makefile.am =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/Makefile.am,v retrieving revision 1.17 diff -u -r1.17 Makefile.am --- ai/Makefile.am 2 May 2004 12:13:51 -0000 1.17 +++ ai/Makefile.am 16 Jun 2004 23:52:05 -0000 @@ -27,6 +27,8 @@ aihunt.h \ ailog.c \ ailog.h \ + aisettler.c \ + aisettler.h \ aitech.c \ aitech.h \ aitools.c \ Index: ai/aidata.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aidata.c,v retrieving revision 1.27 diff -u -r1.27 aidata.c --- ai/aidata.c 4 Jun 2004 15:25:13 -0000 1.27 +++ ai/aidata.c 16 Jun 2004 23:52:05 -0000 @@ -305,6 +309,9 @@ ai->angry_priority = TRADE_WEIGHTING * 3; /* grave danger */ ai->pollution_priority = POLLUTION_WEIGHTING; + ai->growth_priority = 15; /* WAG */ + + /* Goals */ ai_best_government(pplayer); /*** Interception engine ***/ @@ -368,6 +375,26 @@ struct ai_data *ai = &aidata[pplayer->player_no]; int i; + /* The values below are WAGs that have been optimized to the + * default rules. They are not changed during the game. */ + + /* Perfection gives us an idea of how long to search for optimal + * solutions, instead of going for quick and dirty solutions that + * waste valuable time. Decrease for maps where good city placements + * are hard to find. Lower means more perfection. */ + ai->perfection = 3; + /* How much to deemphasise the potential for city growth for city + * placements. Decrease this value if large cities are important + * or planned. Increase if running strict smallpox. */ + ai->growth_potential_deemphasis = 8; + /* Percentage bonus to city locations near an ocean. */ + ai->naval_emphasis = 20; + /* Modifier for defense bonus that is applied to city location want. + * Increase this to lower emphasis on defensive positions, decrease + * it if you want more emphasis on heavily defended cities. */ + ai->defense_emphasis = 800; + + /* Government variables */ ai->govt_reeval = 0; ai->government_want = fc_calloc(game.government_count + 1, sizeof(int)); Index: ai/aidata.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aidata.h,v retrieving revision 1.12 diff -u -r1.12 aidata.h --- ai/aidata.h 23 May 2004 02:05:37 -0000 1.12 +++ ai/aidata.h 16 Jun 2004 23:52:06 -0000 @@ -91,9 +91,13 @@ struct { int *workers; /* cities to workers on continent*/ int *cities; /* number of cities on continent */ + int passengers; /* number of passengers waiting for boats */ - int boats; - int available_boats; + int boats; /* total number of boats */ + int available_boats; /* number of boats not booked or used */ + int mean_avail_boats; /* excess number of boats somehow averaged + over several turns */ + int average_production; bv_id diplomat_reservations; } stats; @@ -110,6 +114,11 @@ int unhappy_priority; int angry_priority; int pollution_priority; + int growth_priority; + int perfection; + int growth_potential_deemphasis; + int naval_emphasis; + int defense_emphasis; /* Government data */ int *government_want; @@ -138,7 +147,7 @@ void ai_set_passenger(struct unit *punit, struct unit *passenger); void ai_set_ferry(struct unit *punit, struct unit *ferry); void ai_clear_ferry(struct unit *punit); +bool ai_player_has_enough_boats(struct player *pplayer, int enough); int ai_available_boats(struct player *pplayer); - #endif Index: ai/aitools.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v retrieving revision 1.111 diff -u -r1.111 aitools.c --- ai/aitools.c 16 Jun 2004 12:59:34 -0000 1.111 +++ ai/aitools.c 16 Jun 2004 23:52:06 -0000 @@ -15,6 +15,7 @@ #include #endif +#include #include #include @@ -48,6 +49,7 @@ #include "aidata.h" #include "ailog.h" #include "aiunit.h" +#include "citymap.h" #include "aitools.h" @@ -267,7 +269,6 @@ struct pf_parameter param; struct pf_map *search_map; - UNIT_LOG(LOGLEVEL_FINDFERRY, punit, "asked find_ferry for a boat"); if (ai_available_boats(unit_owner(punit)) <= 0 @@ -474,10 +475,13 @@ && !unit_flag(punit, F_MARINES))) { if (!find_beachhead(punit, dest_x, dest_y, &beach_x, &beach_y)) { /* Nowhere to go */ + UNIT_LOG(LOG_NORMAL, punit, "FAILED to find beachhead to (%d,%d)", + dest_x, dest_y); return FALSE; } UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Found beachhead (%d,%d)", beach_x, beach_y); + assert(is_normal_map_pos(beach_x, beach_y)); } else { beach_x = dest_x; beach_y = dest_y; @@ -541,7 +545,8 @@ } /* liable to bump into someone that will kill us. Should avoid? */ } else { - UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Not moving"); + UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Not moving to (%d, %d) (moves_left=%d)", + dest_x, dest_y, punit->moves_left); return FALSE; } @@ -621,7 +626,9 @@ } if (punit->ai.ai_role == AIUNIT_BUILD_CITY) { + assert(is_normal_map_pos(goto_dest_x(punit), goto_dest_y(punit))); remove_city_from_minimap(goto_dest_x(punit), goto_dest_y(punit)); + citymap_free_city_spot(goto_dest_x(punit), goto_dest_y(punit), punit->id); } if (punit->ai.ai_role == AIUNIT_HUNTER) { @@ -656,9 +663,11 @@ ai_unit_new_role(bodyguard, AIUNIT_NONE, -1, -1); } - if (punit->ai.ai_role == AIUNIT_BUILD_CITY) { + /* Reserve city spot, _unless_ we want to add ourselves to a city. */ + if (punit->ai.ai_role == AIUNIT_BUILD_CITY && !map_get_city(x, y)) { assert(is_normal_map_pos(x, y)); add_city_to_minimap(x, y); + citymap_reserve_city_spot(x, y, punit->id); } if (punit->ai.ai_role == AIUNIT_HUNTER) { /* Set victim's hunted bit - the hunt is on! */ Index: common/aicore/citymap.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/aicore/citymap.c,v retrieving revision 1.2 diff -u -r1.2 citymap.c --- common/aicore/citymap.c 14 Jun 2004 22:01:07 -0000 1.2 +++ common/aicore/citymap.c 16 Jun 2004 23:52:07 -0000 @@ -47,7 +47,7 @@ * which has a negative value. */ -static int citymap[MAP_MAX_WIDTH][MAP_MAX_HEIGHT]; +int citymap[MAP_MAX_WIDTH][MAP_MAX_HEIGHT]; #define LOG_CITYMAP LOG_DEBUG @@ -85,39 +85,6 @@ } /************************************************************************** - Returns a positive value if within a city radius, which is 1 x number of - cities you are within the radius of, or zero or less if not. A negative - value means this tile is reserved by a city and should not be taken. -**************************************************************************/ -int citymap_read(int x, int y) -{ -#ifdef DEBUG - assert(is_normal_map_pos(x, y)); -#endif - - return citymap[x][y]; -} - -/************************************************************************** - A tile is reserved if it contains a city or unit id, or a worker is - assigned to it. -**************************************************************************/ -bool citymap_is_reserved(int x, int y) -{ - struct tile *ptile; - -#ifdef DEBUG - assert(is_normal_map_pos(x, y)); -#endif - - ptile = map_get_tile(x, y); - if (ptile->worked || ptile->city) { - return TRUE; - } - return (citymap[x][y] < 0); -} - -/************************************************************************** This function reserves a single tile for a (possibly virtual) city with a settler's or a city's id. Then it 'crowds' tiles that this city can use to make them less attractive to other cities we may consider making. Index: common/aicore/citymap.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/common/aicore/citymap.h,v retrieving revision 1.2 diff -u -r1.2 citymap.h --- common/aicore/citymap.h 14 Jun 2004 22:01:07 -0000 1.2 +++ common/aicore/citymap.h 16 Jun 2004 23:52:07 -0000 @@ -13,11 +13,36 @@ #ifndef FC__CITYMAP_H #define FC__CITYMAP_H +extern int citymap[MAP_MAX_WIDTH][MAP_MAX_HEIGHT]; + void citymap_turn_init(struct player *pplayer); -int citymap_read(int x, int y); -bool citymap_is_reserved(int x, int y); void citymap_reserve_city_spot(int x, int y, int id); void citymap_free_city_spot(int x, int y, int id); void citymap_reserve_tile(int x, int y, int id); +/************************************************************************** + Returns a positive value if within a city radius, which is 1 x number of + cities you are within the radius of, or zero or less if not. A negative + value means this tile is reserved by a city and should not be taken. +**************************************************************************/ +static inline int citymap_read(int x, int y) +{ + return citymap[x][y]; +} + +/************************************************************************** + A tile is reserved if it contains a city or unit id, or a worker is + assigned to it. +**************************************************************************/ +static inline bool citymap_is_reserved(int x, int y) +{ + struct tile *ptile; + + ptile = map_get_tile(x, y); + if (ptile->worked || ptile->city) { + return TRUE; + } + return (citymap[x][y] < 0); +} + #endif Index: server/settlers.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/settlers.c,v retrieving revision 1.182 diff -u -r1.182 settlers.c --- server/settlers.c 23 May 2004 02:05:37 -0000 1.182 +++ server/settlers.c 16 Jun 2004 23:52:08 -0000 @@ -39,8 +39,10 @@ #include "aicity.h" #include "aidata.h" #include "ailog.h" +#include "aisettler.h" #include "aitools.h" #include "aiunit.h" +#include "citymap.h" #include "settlers.h" @@ -54,9 +56,6 @@ BV_DEFINE(enemy_mask, MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS); static enemy_mask enemies[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS]; -static void auto_settler_findwork(struct player *pplayer, - struct unit *punit); -static void auto_settlers_player(struct player *pplayer); static bool is_already_assigned(struct unit *myunit, struct player *pplayer, int x, int y); static int city_desirability(struct player *pplayer, int x, int y); @@ -69,6 +68,11 @@ int x = punit->x, y = punit->y; struct city *pcity; + handle_unit_activity_request(punit, ACTIVITY_IDLE); + + /* Free city reservations */ + ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); + handle_unit_build_city(pplayer, punit->id, city_name_suggestion(pplayer, x, y)); pcity = map_get_city(x, y); @@ -1209,7 +1213,7 @@ } /************************************************************************** - find some work for the settler + Find some work for the settler **************************************************************************/ static void auto_settler_findwork(struct player *pplayer, struct unit *punit) { @@ -1254,9 +1258,9 @@ return; } - /* If we intent on building a city then reserve the square. */ - if (unit_flag(punit, F_CITIES) && - best_act == ACTIVITY_UNKNOWN /* flag */) { + /* If we intend to build a city then reserve the square. */ + if (unit_flag(punit, F_CITIES) + && best_act == ACTIVITY_UNKNOWN /* flag */) { ai_unit_new_role(punit, AIUNIT_BUILD_CITY, gx, gy); } else { ai_unit_new_role(punit, AIUNIT_AUTO_SETTLER, gx, gy); @@ -1273,7 +1277,6 @@ && same_pos(gx, gy, punit->x, punit->y)) { if (best_act == ACTIVITY_UNKNOWN) { remove_city_from_minimap(gx, gy); /* yeah, I know. -- Syela */ - handle_unit_activity_request(punit, ACTIVITY_IDLE); (void) ai_do_build_city(pplayer, punit); return; } @@ -1284,6 +1287,124 @@ } /************************************************************************** + New implementation of server/settlers.c function with same name +**************************************************************************/ +#define LOG_SETTLER LOG_DEBUG +static void auto_settler_findwork2(struct player *pplayer, struct unit *punit) +{ + struct cityresult result; + int best_impr = 0; /* best terrain improvement we can do */ + enum unit_activity best_act = ACTIVITY_IDLE; /* compat. kludge */ + int gx = -1, gy = -1; + struct ai_data *ai = ai_data_get(pplayer); + + CHECK_UNIT(punit); + + result.total = 0; + result.result = 0; + + assert(pplayer && punit); + assert(unit_flag(punit, F_CITIES) || unit_flag(punit, F_SETTLERS)); + + /*** If we are on a city mission: Go where we should ***/ + + if (punit->ai.ai_role == AIUNIT_BUILD_CITY) { + int x = goto_dest_x(punit), y = goto_dest_y(punit), sanity = punit->id; + + /* Check that missions is still possible */ + if (!city_can_be_built_here(x, y, punit)) { + UNIT_LOG(LOG_SETTLER, punit, "city founding mission failed"); + ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); + return; /* avoid recursion at all cost */ + } else { + /* Go there */ + if ((!ai_gothere(pplayer, punit, x, y) && !find_unit_by_id(sanity)) + || punit->moves_left <= 0) { + return; + } + if (same_pos(punit->x, punit->y, x, y)) { + if (!ai_do_build_city(pplayer, punit)) { + UNIT_LOG(LOG_ERROR, punit, "could not make city on %s", + map_get_tile_info_text(punit->x, punit->y)); + ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); + } else { + return; /* We came, we saw, we built... */ + } + } else { + UNIT_LOG(LOG_SETTLER, punit, "could not go to target"); + /* ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); */ + return; + } + } + } + + CHECK_UNIT(punit); + + /*** Try find some work ***/ + + if (unit_flag(punit, F_SETTLERS)) { + best_impr = evaluate_improvements(punit, &best_act, &gx, &gy); + } + + if (unit_flag(punit, F_CITIES)) { + find_best_city_placement(punit, &result, TRUE, FALSE); + UNIT_LOG(LOG_SETTLER, punit, "city want %d (impr want %d)", result.result, + best_impr); + if (result.result > best_impr) { + if (map_get_city(result.x, result.y)) { + UNIT_LOG(LOG_SETTLER, punit, "immigrates to %s (%d, %d)", + map_get_city(result.x, result.y), result.x, result.y); + } else { + UNIT_LOG(LOG_SETTLER, punit, "makes city at (%d, %d)", + result.x, result.y); + if (punit->debug) { + print_cityresult(pplayer, &result, ai); + } + } + /* Go make a city! */ + ai_unit_new_role(punit, AIUNIT_BUILD_CITY, result.x, result.y); + /* Reserve best other tile */ + citymap_reserve_tile(result.other_x, result.other_y, punit->id); + set_goto_dest(punit, result.x, result.y); /* TMP */ + } else if (best_impr > 0) { + UNIT_LOG(LOG_SETTLER, punit, "improves terrain"); + /* Terrain improvements follows the old model, and is recalculated + * each turn. */ + ai_unit_new_role(punit, AIUNIT_AUTO_SETTLER, gx, gy); + /* Mark the square as taken. */ + if (gx != -1 && gy != -1) { + map_get_tile(gx, gy)->assigned = + map_get_tile(gx, gy)->assigned | 1<player_no; + } else { + UNIT_LOG(LOG_NORMAL, punit, "giving up trying to improve terrain"); + return; /* We cannot do anything */ + } + set_goto_dest(punit, gx, gy); /* TMP */ + if (do_unit_goto(punit, GOTO_MOVE_ANY, FALSE) == GR_DIED) { + return; + } + if (punit->moves_left > 0 + && same_pos(gx, gy, punit->x, punit->y)) { + handle_unit_activity_request(punit, best_act); + send_unit_info(NULL, punit); + return; + } + } else { + UNIT_LOG(LOG_SETTLER, punit, "cannot find work"); + ai_unit_new_role(punit, AIUNIT_NONE, -1, -1); + return; + } + } + + /*** Recurse if we want to found a city ***/ + + if (punit->ai.ai_role == AIUNIT_BUILD_CITY) { + auto_settler_findwork2(pplayer, punit); + } +} +#undef LOG_SETTLER + +/************************************************************************** Do all tile improvement calculations and cache them for later. **************************************************************************/ void initialize_infrastructure_cache(struct city *pcity) @@ -1318,15 +1439,20 @@ } /************************************************************************** - run through all the players settlers and let those on ai.control work - automagically + Run through all the players settlers and let those on ai.control work + automagically. **************************************************************************/ -static void auto_settlers_player(struct player *pplayer) +void auto_settlers_player(struct player *pplayer) { static struct timer *t = NULL; /* alloc once, never free */ t = renew_timer_start(t, TIMER_CPU, TIMER_DEBUG); + if (pplayer->ai.control && ai_handicap(pplayer, H_EXPERIMENTAL)) { + /* Set up our city map. */ + citymap_turn_init(pplayer); + } + city_list_iterate(pplayer->cities, pcity) initialize_infrastructure_cache(pcity); /* saves oodles of time -- Syela */ city_list_iterate_end; @@ -1354,9 +1480,12 @@ handle_unit_activity_request(punit, ACTIVITY_IDLE); } if (punit->activity == ACTIVITY_IDLE) { - auto_settler_findwork(pplayer, punit); + if (pplayer->ai.control && ai_handicap(pplayer, H_EXPERIMENTAL)) { + auto_settler_findwork2(pplayer, punit); + } else { + auto_settler_findwork(pplayer, punit); + } } - freelog(LOG_DEBUG, "Has been processed."); } } unit_list_iterate_end; @@ -1475,7 +1604,6 @@ */ } - /************************************************************************** Recalculate enemies[] table **************************************************************************/ @@ -1491,9 +1619,9 @@ } /************************************************************************** - Do the auto_settler stuff for all the players. + Initialize autosettler code. **************************************************************************/ -void auto_settlers(void) +void auto_settlers_init(void) { assign_settlers(); assign_territory(); @@ -1504,8 +1632,8 @@ } /************************************************************************** -used to use old crappy formulas for settler want, but now using actual -want! + Return want for city settler. Note that we rely here on the fact that + ai_settler_init() has been run while doing autosettlers. **************************************************************************/ void contemplate_new_city(struct city *pcity) { @@ -1525,24 +1653,38 @@ virtualunit = create_unit_virtual(pplayer, pcity, unit_type, 0); virtualunit->x = pcity->x; virtualunit->y = pcity->y; - want = evaluate_city_building(virtualunit, &gx, &gy, &ferryboat); - free(virtualunit); - - unit_list_iterate(pplayer->units, qpass) { - /* We want a ferryboat with want 199 */ - if (qpass->ai.ferryboat == pcity->id) - want = -199; - } unit_list_iterate_end; - /* Did we count on using an existing boat. If yes we need to keep it - * in the city. */ - pcity->ai.founder_boat = (ferryboat != NULL); + assert(pplayer->ai.control); - if (gx == -1) { - pcity->ai.founder_want = -want; + if (pplayer->ai.control && ai_handicap(pplayer, H_EXPERIMENTAL)) { + struct cityresult result; + bool is_coastal = is_ocean_near_tile(pcity->x, pcity->y); + + find_best_city_placement(virtualunit, &result, is_coastal, is_coastal); + + CITY_LOG(LOG_DEBUG, pcity, "want(%d) to establish city at" + " (%d, %d) and will %s to get there", result.result, + result.x, result.y, + (result.virt_boat ? "build a boat" : + (result.overseas ? "use a boat" : "walk"))); + + pcity->ai.founder_want = (result.virt_boat ? + -result.result : result.result); + pcity->ai.founder_boat = result.overseas; } else { - pcity->ai.founder_want = want; /* boat */ + want = evaluate_city_building(virtualunit, &gx, &gy, &ferryboat); + + /* Did we count on using an existing boat. If yes we need to keep it + * in the city. */ + pcity->ai.founder_boat = (ferryboat != NULL); + + if (gx == -1) { + pcity->ai.founder_want = -want; + } else { + pcity->ai.founder_want = want; /* boat */ + } } + free(virtualunit); } /************************************************************************** Index: server/settlers.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/settlers.h,v retrieving revision 1.23 diff -u -r1.23 settlers.h --- server/settlers.h 23 Sep 2003 15:59:05 -0000 1.23 +++ server/settlers.h 16 Jun 2004 23:52:08 -0000 @@ -19,7 +19,8 @@ struct unit; struct city; -void auto_settlers(void); +void auto_settlers_init(void); +void auto_settlers_player(struct player *pplayer); int find_boat(struct player *pplayer, int *x, int *y, int cap); #define MORT 24 Index: server/srv_main.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v retrieving revision 1.169 diff -u -r1.169 srv_main.c --- server/srv_main.c 9 Jun 2004 21:27:46 -0000 1.169 +++ server/srv_main.c 16 Jun 2004 23:52:08 -0000 @@ -97,6 +97,8 @@ #include "advmilitary.h" #include "aidata.h" #include "aihand.h" +#include "aisettler.h" +#include "citymap.h" #include "srv_main.h" @@ -513,8 +515,13 @@ nocity_send = TRUE; /* AI end of turn activities */ + auto_settlers_init(); players_iterate(pplayer) { if (pplayer->ai.control) { + ai_settler_init(pplayer); + } + auto_settlers_player(pplayer); + if (pplayer->ai.control) { ai_do_last_activities(pplayer); } } players_iterate_end; @@ -1424,8 +1431,6 @@ summon_barbarians(); /* wild guess really, no idea where to put it, but I want to give them chance to move their units */ /* Moved this to after the human turn for efficiency -- Syela */ - freelog(LOG_DEBUG, "Autosettlers"); - auto_settlers(); freelog(LOG_DEBUG, "Auto-Attack phase"); auto_attack(); freelog(LOG_DEBUG, "Endturn"); Index: server/stdinhand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v retrieving revision 1.323 diff -u -r1.323 stdinhand.c --- server/stdinhand.c 12 Jun 2004 17:42:28 -0000 1.323 +++ server/stdinhand.c 16 Jun 2004 23:52:09 -0000 @@ -60,7 +60,9 @@ #include "srv_main.h" #include "advmilitary.h" /* assess_danger_player() */ +#include "aidata.h" #include "ailog.h" +#include "aisettler.h" #include "stdinhand.h" @@ -1209,7 +1211,8 @@ "votes have been cast for or against.") }, {"debug", ALLOW_CTRL, - N_("debug | city | units | unit >"), + N_("debug | city | units | unit | " + "settler >"), N_("Turn on or off AI debugging of given entity."), N_("Print AI debug information about given entity and turn continous " "debugging output for this entity on or off."), @@ -3314,7 +3317,7 @@ int ntokens = 0, i; const char *usage = _("Undefined arguments. Usage: debug | city | units | " - "unit >."); + "unit | settler >."); if (server_state != RUN_GAME_STATE) { cmd_reply(CMD_DEBUG, caller, C_SYNTAX, @@ -3327,7 +3330,7 @@ if (str != NULL || strlen(str) > 0) { sz_strlcpy(buf, str); - ntokens = get_tokens(buf, arg, 3, TOKEN_DELIMITERS); + ntokens = get_tokens(buf, arg, 4, TOKEN_DELIMITERS); } if (strcmp(arg[0], "player") == 0) { @@ -3432,6 +3435,32 @@ UNIT_LOG(LOG_NORMAL, punit, _("%s's %s debugged."), unit_owner(punit)->name, unit_name(punit->type)); } + } else if (strcmp(arg[0], "settler") == 0) { + struct player *pplayer; + enum m_pre_result match_result; + struct ai_data *ai; + struct cityresult result; + + if (ntokens != 4) { + cmd_reply(CMD_DEBUG, caller, C_SYNTAX, usage); + goto cleanup; + } + pplayer = find_player_by_name_prefix(arg[1], &match_result); + if (pplayer == NULL) { + cmd_reply_no_such_player(CMD_DEBUG, caller, arg[1], match_result); + goto cleanup; + } + if (sscanf(arg[2], "%d", &result.x) != 1) { + cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 3 must be integer.")); + goto cleanup; + } + if (sscanf(arg[3], "%d", &result.y) != 1) { + cmd_reply(CMD_DEBUG, caller, C_SYNTAX, _("Value 4 must be integer.")); + goto cleanup; + } + ai = ai_data_get(pplayer); + cityresult_fill(pplayer, ai, &result); + print_cityresult(pplayer, &result, ai); } else { cmd_reply(CMD_DEBUG, caller, C_SYNTAX, usage); }