Index: ai/aitools.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v retrieving revision 1.50 diff -u -r1.50 aitools.c --- ai/aitools.c 2002/04/09 14:14:12 1.50 +++ ai/aitools.c 2002/08/07 13:08:35 @@ -24,6 +24,7 @@ #include "shared.h" #include "unit.h" +#include "unithand.h" #include "citytools.h" #include "cityturn.h" #include "maphand.h" @@ -35,10 +36,159 @@ #include "aitools.h" -/* dist_nearest_enemy_* are no longer ever used. This is - dist_nearest_enemy_city, respaced so I can read it and therefore - debug it into something useful. -- Syela -*/ +/************************************************************************** + Move a bodyguard along with another unit. We assume that unit has already + been moved to (x, y) which is a valid, safe coordinate, and that our + bodyguard has not. This is an ai_unit_* auxiliary function, do not use + elsewhere. + + FIXME: packetify requests too +**************************************************************************/ +static void ai_unit_bodyguard_move(int unitid, int x, int y) +{ + struct unit *bodyguard = find_unit_by_id(unitid); + struct unit *punit; + struct player *pplayer; + + assert(bodyguard); + pplayer = unit_owner(bodyguard); + assert(pplayer); + punit = find_unit_by_id(bodyguard->ai.charge); + assert(punit); + + if (!is_tiles_adjacent(x, y, bodyguard->x, bodyguard->y)) { + freelog(LOG_VERBOSE, "%s: %s (%d,%d) is too far from its charge (%d,%d)!", + pplayer->name, unit_type(bodyguard)->name, bodyguard->x, + bodyguard->y, x, y); + return; + } + + if (bodyguard->moves_left < map_move_cost(punit, x, y)) { + /* should generally should not happen */ + freelog(LOG_VERBOSE, "%s: %s left its %s bodyguard behind at (%d,%d)!", + pplayer->name, unit_type(punit)->name, + unit_type(bodyguard)->name, x, y); + return; + } + + freelog(LOG_DEBUG, "%s: Dragging %s's bodyguard %s to (%d,%d)", + pplayer->name, unit_type(punit)->name, unit_type(bodyguard)->name, + x, y); + + handle_unit_activity_request(bodyguard, ACTIVITY_IDLE); + (void) ai_unit_move(bodyguard, x, y); + handle_unit_activity_request(bodyguard, ACTIVITY_FORTIFYING); +} + +/************************************************************************** + Check if we have a bodyguard with sanity checking and error recovery. + Repair incompletely referenced bodyguards. When the rest of the bodyguard + mess is cleaned up, this repairing should be replaced with an assert. +**************************************************************************/ +static bool has_bodyguard(struct unit *punit) +{ + struct unit *guard; + if (punit->ai.bodyguard > 0) { + if ((guard = find_unit_by_id(punit->ai.bodyguard))) { + guard->ai.charge = punit->id; + return TRUE; + } else { + punit->ai.bodyguard = 0; + } + } + return FALSE; +} + +/************************************************************************** + Move and attack with an ai unit. We do not wait for server reply. +**************************************************************************/ +void ai_unit_attack(struct unit *punit, int x, int y) +{ + struct packet_move_unit pmove; + int sanity = punit->id; + + assert(punit); + assert(unit_owner(punit)->ai.control); + assert(is_normal_map_pos(x, y)); + assert(is_tiles_adjacent(punit->x, punit->y, x, y)); + + pmove.x = x; + pmove.y = y; + pmove.unid = punit->id; + handle_move_unit(unit_owner(punit), &pmove); + + if (find_unit_by_id(sanity) && same_pos(x, y, punit->x, punit->y)) { + if (has_bodyguard(punit)) { + ai_unit_bodyguard_move(punit->ai.bodyguard, x, y); + } + } +} + +/************************************************************************** + Move an ai unit. Do not attack. Do not leave bodyguard. + + This function returns only when we have a reply from the server and + we can tell the calling function what happened to the move request. + (Right now is not a big problem, since we call the server directly.) +**************************************************************************/ +bool ai_unit_move(struct unit *punit, int x, int y) +{ + struct unit *pdefender = get_defender(punit, x, y); + struct packet_move_unit pmove; + struct unit *bodyguard; + int sanity = punit->id; + struct player *pplayer = unit_owner(punit); + + assert(punit); + assert(unit_owner(punit)->ai.control); + assert(is_normal_map_pos(x, y)); + assert(is_tiles_adjacent(punit->x, punit->y, x, y)); + + /* if enemy, stop and let ai attack function take this case */ + if (pdefender && pplayers_at_war(unit_owner(pdefender), pplayer)) { + return FALSE; + } + + /* barbarians shouldn't enter huts */ + if (is_barbarian(pplayer) && tile_has_special(map_get_tile(x,y), S_HUT)) { + return FALSE; + } + + /* don't leave bodyguard behind */ + if (has_bodyguard(punit) + && (bodyguard = find_unit_by_id(punit->ai.bodyguard)) + && same_pos(punit->x, punit->y, bodyguard->x, bodyguard->y) + && bodyguard->moves_left == 0) { + freelog(LOG_DEBUG, "%s's %s does not want to leave its %s bodyguard.", + pplayer->name, unit_type(punit)->name, unit_type(bodyguard)->name); + return FALSE; + } + + /* Try not to end move next to an enemy */ + if (punit->moves_left <= map_move_cost(punit, x, y) + && unit_type(punit)->move_rate > map_move_cost(punit, x, y) + && enemies_at(punit, x, y) + && !enemies_at(punit, punit->x, punit->y)) { + freelog(LOG_DEBUG, "%s's %s ending move early to stay out of trouble.", + pplayer->name, unit_type(punit)->name); + return FALSE; + } + + /* go */ + pmove.x = x; + pmove.y = y; + pmove.unid = punit->id; + handle_move_unit(unit_owner(punit), &pmove); + + /* handle the results */ + if (find_unit_by_id(sanity) && same_pos(x, y, punit->x, punit->y)) { + if (has_bodyguard(punit)) { + ai_unit_bodyguard_move(punit->ai.bodyguard, x, y); + } + return TRUE; + } + return FALSE; +} /************************************************************************** This looks for the nearest city: Index: ai/aitools.h =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aitools.h,v retrieving revision 1.19 diff -u -r1.19 aitools.h --- ai/aitools.h 2002/04/12 13:50:54 1.19 +++ ai/aitools.h 2002/08/07 13:08:35 @@ -20,6 +20,9 @@ struct government; struct player; +void ai_unit_attack(struct unit *punit, int x, int y); +bool ai_unit_move(struct unit *punit, int x, int y); + struct city *dist_nearest_city(struct player *pplayer, int x, int y, bool everywhere, bool enemy); Index: ai/aiunit.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v retrieving revision 1.200 diff -u -r1.200 aiunit.c --- ai/aiunit.c 2002/07/15 17:06:21 1.200 +++ ai/aiunit.c 2002/08/07 13:08:36 @@ -416,7 +416,7 @@ /* Some tile have unexplored territory adjacent, let's move there. */ - if (!handle_unit_move_request(punit, best_x, best_y, FALSE, FALSE)) { + if (!ai_unit_move(punit, best_x, best_y)) { /* This shouldn't happen, but occassionally it can. */ break; } @@ -940,10 +940,6 @@ static int ai_military_findvictim(struct player *pplayer, struct unit *punit, int *dest_x, int *dest_y) { - /* Set the tile with our target as the best (with value new_best). */ -#define SET_BEST(new_best) \ - do { best = (new_best); *dest_x = x1; *dest_y = y1; } while (FALSE) - int bellig = unit_belligerence_primitive(punit); int x = punit->x, y = punit->y; int best = 0; @@ -965,6 +961,10 @@ } adjc_iterate(x, y, x1, y1) { + /* Macro to set the tile with our target as the best (with value new_best) */ +#define SET_BEST(new_best) \ + do { best = (new_best); *dest_x = x1; *dest_y = y1; } while (FALSE) + struct unit *pdef = get_defender(punit, x1, y1); if (pdef) { @@ -1066,11 +1066,10 @@ continue; } } +#undef SET_BEST } adjc_iterate_end; return best; - -#undef SET_BEST } /************************************************************************* @@ -1108,7 +1107,7 @@ "Stationary escort @(%d,%d) received %d best @(%d,%d)", punit->x, punit->y, i, x, y); if (i >= 40 * SHIELD_WEIGHTING) - handle_unit_move_request(punit, x, y, FALSE, FALSE); + ai_unit_attack(punit, x, y); /* otherwise don't bother, but free cities are free cities and must be snarfed. -- Syela */ } if (aunit && unit_list_find(&map_get_tile(x, y)->units, id) && aunit->ai.bodyguard != 0) @@ -1311,7 +1310,7 @@ "Bodyguard at (%d, %d) is adjacent to (%d, %d)", i, j, punit->x, punit->y); if (aunit->moves_left > 0) return(0); - else return handle_unit_move_request(punit, i, j, FALSE, FALSE) ? 1 : 0; + else return ai_unit_move(punit, i, j) ? 1 : 0; } } unit_list_iterate_end; } adjc_iterate_end; @@ -1521,11 +1520,12 @@ punit->id,punit->x,punit->y,pcity->x,pcity->y); if ((punit->x == pcity->x)&&(punit->y == pcity->y)) { freelog(LOG_DEBUG, "INHOUSE. GOTO AI_NONE(%d)", punit->id); - /* aggro defense goes here -- Syela */ - ai_military_findvictim(pplayer, punit, &dest_x, &dest_y); punit->ai.ai_role=AIUNIT_NONE; - handle_unit_move_request(punit, dest_x, dest_y, FALSE, FALSE); - /* might bash someone */ + /* aggro defense goes here -- Syela */ + if (ai_military_findvictim(pplayer, punit, &dest_x, &dest_y) > 1) { + assert(dest_x != punit->x || dest_y != punit->y); + ai_unit_attack(punit, dest_x, dest_y); /* might bash someone */ + } } else { freelog(LOG_DEBUG, "GOHOME(%d,%d)", punit->goto_dest_x, punit->goto_dest_y); @@ -1948,7 +1948,7 @@ freelog(LOG_DEBUG, "%s's %s at (%d, %d) bashing (%d, %d)", pplayer->name, unit_type(punit)->name, punit->x, punit->y, dest_x, dest_y); - handle_unit_move_request(punit, dest_x, dest_y, FALSE, FALSE); + ai_unit_attack(punit, dest_x, dest_y); punit = find_unit_by_id(id); if (punit) flag = punit->moves_left > 0; else flag = FALSE; } Index: server/gotohand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/gotohand.c,v retrieving revision 1.145 diff -u -r1.145 gotohand.c --- server/gotohand.c 2002/07/29 08:51:00 1.145 +++ server/gotohand.c 2002/08/07 13:08:41 @@ -1332,6 +1332,15 @@ penemy = is_enemy_unit_tile(map_get_tile(x, y), unit_owner(punit)); assert(punit->moves_left > 0); + + /* DO NOT Auto-attack. Findvictim routine will decide if we should. */ + if (penemy && pplayer->ai.control) { + freelog(LOG_DEBUG, "%s: Aborting %s's GOTO for AI attack procedures at (%d,%d).", + pplayer->name, unit_type(punit)->name, punit->x, punit->y); + punit->activity=ACTIVITY_IDLE; + return GR_FAILED; + } + last_tile = same_pos(x, y, punit->goto_dest_x, punit->goto_dest_y); if (!handle_unit_move_request(punit, x, y, FALSE, Index: server/unithand.c =================================================================== RCS file: /home/freeciv/CVS/freeciv/server/unithand.c,v retrieving revision 1.229 diff -u -r1.229 unithand.c --- server/unithand.c 2002/08/07 11:21:52 1.229 +++ server/unithand.c 2002/08/07 13:08:43 @@ -109,9 +109,13 @@ } /************************************************************************** -Handler for PACKET_UNIT_CONNECT request -The unit is send on way and will build something (roads only for now) -along the way +Handler for PACKET_UNIT_CONNECT request. The unit is send on way and will +build something (roads only for now) along the way, using server-side +path-finding. + +FIXME: This should be rewritten to use client-side path finding along so +that we can show in the client where the road-to-be-built will be and +enable the use of waypoints to alter this route. - Per **************************************************************************/ void handle_unit_connect(struct player *pplayer, struct packet_unit_connect *req) @@ -863,15 +867,9 @@ { struct player *pplayer = unit_owner(punit); struct tile *pdesttile = map_get_tile(dest_x, dest_y); - struct tile *psrctile = map_get_tile(punit->x, punit->y); struct unit *pdefender = get_defender(punit, dest_x, dest_y); struct city *pcity = pdesttile->city; - /* barbarians shouldn't enter huts */ - if (is_barbarian(pplayer) && tile_has_special(pdesttile, S_HUT)) { - return FALSE; - } - if (!is_normal_map_pos(dest_x, dest_y)) { return FALSE; } @@ -964,13 +962,6 @@ return FALSE; } - /* DO NOT Auto-attack. Findvictim routine will decide if we should. */ - if (pplayer->ai.control && punit->activity == ACTIVITY_GOTO) { - notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT, - _("Game: Aborting GOTO for AI attack procedures.")); - return FALSE; - } - if (punit->activity == ACTIVITY_GOTO && (dest_x != punit->goto_dest_x || dest_y != punit->goto_dest_y)) { notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT, @@ -979,31 +970,6 @@ return FALSE; } - /* This is for debugging only, and seems to be obsolete as the error message - never appears */ - if (pplayer->ai.control && punit->ai.passenger != 0) { - struct unit *passenger; - passenger = unit_list_find(&psrctile->units, punit->ai.passenger); - if (passenger) { - /* removed what seemed like a very bad abort() -- JMC/jjm */ - if (get_transporter_capacity(punit) == 0) { - freelog(LOG_NORMAL, "%s#%d@(%d,%d) thinks %s#%d is a passenger?", - unit_name(punit->type), punit->id, punit->x, punit->y, - unit_name(passenger->type), passenger->id); - } - } - } - - /* This should be part of can_unit_attack_tile(), but I think the AI - depends on can_unit_attack_tile() not stopping it in the goto, so - leave it here for now. */ - if (unit_type(punit)->attack_strength == 0) { - notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT, - _("Game: A %s cannot attack other units."), - unit_name(punit->type)); - return FALSE; - } - handle_unit_attack_request(punit, pdefender); return TRUE; } /* End attack case */ @@ -1019,32 +985,6 @@ return FALSE; } - - { - struct unit *bodyguard; - if (pplayer->ai.control && - punit->ai.bodyguard > 0 && - (bodyguard = unit_list_find(&psrctile->units, punit->ai.bodyguard)) && - bodyguard->moves_left == 0) { - notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT, - _("Game: %s doesn't want to leave its bodyguard."), - unit_type(punit)->name); - return FALSE; - } - } - - /* Mao had this problem with chariots ending turns next to enemy cities. -- Syela */ - if (pplayer->ai.control && - punit->moves_left <= map_move_cost(punit, dest_x, dest_y) && - unit_type(punit)->move_rate > map_move_cost(punit, dest_x, dest_y) && - enemies_at(punit, dest_x, dest_y) && - !enemies_at(punit, punit->x, punit->y)) { - notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT, - _("Game: %s ending move early to stay out of trouble."), - unit_type(punit)->name); - return FALSE; - } - /* If there is a city it is empty. If not it would have been caught in the attack case. */ if (pcity && !pplayers_allied(city_owner(pcity), unit_owner(punit))) { @@ -1065,36 +1005,14 @@ } /******* ok now move the unit *******/ - if(can_unit_move_to_tile_with_notify(punit, dest_x, dest_y, igzoc) && - try_move_unit(punit, dest_x, dest_y)) { - int src_x = punit->x; - int src_y = punit->y; + if (can_unit_move_to_tile_with_notify(punit, dest_x, dest_y, igzoc) + && try_move_unit(punit, dest_x, dest_y)) { int move_cost = map_move_cost(punit, dest_x, dest_y); /* The ai should assign the relevant units itself, but for now leave this */ bool take_from_land = punit->activity == ACTIVITY_IDLE; - bool survived; - survived = move_unit(punit, dest_x, dest_y, TRUE, take_from_land, move_cost); - if (!survived) - return TRUE; - - /* bodyguard code */ - if(pplayer->ai.control && punit->ai.bodyguard > 0) { - struct unit *bodyguard = unit_list_find(&psrctile->units, punit->ai.bodyguard); - if (bodyguard) { - bool success; - - /* FIXME: it is stupid to set to ACTIVITY_IDLE if the unit is - ACTIVITY_FORTIFIED and the unit has no chance of moving anyway */ - handle_unit_activity_request(bodyguard, ACTIVITY_IDLE); - success = handle_unit_move_request(bodyguard, - dest_x, dest_y, igzoc, FALSE); - freelog(LOG_DEBUG, "Dragging %s from (%d,%d)->(%d,%d) (Success=%d)", - unit_type(bodyguard)->name, src_x, src_y, - dest_x, dest_y, success); - handle_unit_activity_request(bodyguard, ACTIVITY_FORTIFYING); - } - } + move_unit(punit, dest_x, dest_y, TRUE, take_from_land, move_cost); + return TRUE; } else { return FALSE;