Complete.Org: Mailing Lists: Archives: freeciv-ai: June 2004:
[freeciv-ai] (PR#8992) Building ferries
Home

[freeciv-ai] (PR#8992) Building ferries

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [freeciv-ai] (PR#8992) Building ferries
From: "Gregory Berkolaiko" <Gregory.Berkolaiko@xxxxxxxxxxxxx>
Date: Wed, 16 Jun 2004 09:26:57 -0700
Reply-to: rt@xxxxxxxxxxx

<URL: http://rt.freeciv.org/Ticket/Display.html?id=8992 >

As a warm-up to the big task of centralized ferry building I decided to
separate all ferry code into one file -- quite tired of it being split
between aiunit.c, aitools.c and aidata.c.  So like other specialised
units like diplomats, settlers and others, ferries get their own file
aiferry.[ch].

Among other things I separated initalizations of aidata fields related
to ferries into a function which is now in aiferry.c

The header file is supplied of path_finding.h-style comments too (in
addition to all the compulsory comments in the C-file).

I spent quite a bit of time on this and really afraid of the bitrot so
please commit asap.

G.

P.S. If there are no objections I will tear the explorer code out of
aiunit.c too one day.
/********************************************************************** 
 Freeciv - Copyright (C) 2002 - The Freeciv Project
   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.
***********************************************************************/
#ifndef FC__AIFERRY_H
#define FC__AIFERRY_H

#include "shared.h"             /* bool type */

struct player;
struct unit;
struct pf_path;

/* 
 * Initialize ferrybaot-related statistics in the ai data.
 */
void aiferry_init_stats(struct player *pplayer);

/* 
 * Find the nearest boat.  Can be called from inside the continents too 
 */
int aiferry_find_boat(struct unit *punit, int cap, struct pf_path **path);

/*
 * Release the boat reserved in punit's ai.ferryboat field.
 */
void aiferry_clear_boat(struct unit *punit);

/*
 * Go to the destination by hitching a ride on a boat.  Will try to find 
 * a beachhead but it works better if (dest_x, dest_y) is on the coast.
 */
bool aiferry_gobyboat(struct player *pplayer, struct unit *punit,
                      int dest_x, int dest_y);

/*
 * Main boat managing function.  Gets units on board to where they want to
 * go and then looks for new passengers or (if it fails) for a city which
 * will build a passenger soon.
 */
void ai_manage_ferryboat(struct player *pplayer, struct unit *punit);

#endif /* FC__AIFERRY_H */
/********************************************************************** 
 Freeciv - Copyright (C) 2004 - The Freeciv Project
   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.
***********************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "log.h"
#include "unit.h"

#include "path_finding.h"
#include "pf_tools.h"

#include "hand_gen.h"
#include "unithand.h"
#include "unittools.h"

#include "aidata.h"
#include "ailog.h"
#include "aitools.h"
#include "aiunit.h"

#include "aiferry.h"


/* =================== constant with special meaning =================== */

/* 
 * This one is used only by ferryboats in ai.passenger field 
 */
#define FERRY_AVAILABLE   -1      /* Boat is looking for a passenger */
/* 
 * The below is used only by passengers in ai.ferryboat field 
 */ 
#define FERRY_WANTED      -1      /* Needs a boat */
#define FERRY_NONE         0      /* Has no boa and doesn't need one */


/* =================== group log levels ================================ */

/* Logging in ferry management functions */
#define LOGLEVEL_FERRY LOG_DEBUG
/* Logging in go_by_boat functions */
#define LOGLEVEL_GOBYBOAT LOG_DEBUG
/* Logging in find_ferry functions */
#define LOGLEVEL_FINDFERRY LOG_DEBUG


/* ========= managing statistics and boat/passanger assignments ======== */

/**************************************************************************
  Call to initialize the ferryboat statistics
**************************************************************************/
void aiferry_init_stats(struct player *pplayer)
{
  struct ai_data *ai = ai_data_get(pplayer);

  ai->stats.passengers = 0;
  ai->stats.boats = 0;
  ai->stats.available_boats = 0;
 
  unit_list_iterate(pplayer->units, punit) {
    if (is_sailing_unit(punit) && is_ground_units_transport(punit)) {
      ai->stats.boats++;
      if (punit->ai.passenger == FERRY_AVAILABLE) {
        ai->stats.available_boats++;
      }
    }
    if (punit->ai.ferryboat == FERRY_WANTED) {
      UNIT_LOG(LOG_DEBUG, punit, "wants a boat.");
      ai->stats.passengers++;
    }  
  } unit_list_iterate_end;
}

/**************************************************************************
  Use on a unit which no longer needs a boat. 
**************************************************************************/
void aiferry_clear_boat(struct unit *punit)
{
  if (punit->ai.ferryboat == FERRY_WANTED) {
    struct ai_data *ai = ai_data_get(unit_owner(punit));

    ai->stats.passengers--;
  } else {
    struct unit *ferry = find_unit_by_id(punit->ai.ferryboat);
    
    if (ferry && ferry->ai.passenger == punit->id) {
      /* punit doesn't want us anymore */
      ai_data_get(unit_owner(ferry))->stats.available_boats++;
      ferry->ai.passenger = FERRY_AVAILABLE;
    }
  }

  punit->ai.ferryboat = FERRY_NONE;
}

/**************************************************************************
  Request a boat for the unit.  Should only be used if the unit is on the
  coast, otherwise ferries will not see it.
**************************************************************************/
static void aiferry_request_boat(struct unit *punit)
{
    struct ai_data *ai = ai_data_get(unit_owner(punit));

    /* First clear the previous assignments (just in case) */
    aiferry_clear_boat(punit);
    /* Now add ourselves to the list of potential passengers */
    UNIT_LOG(LOG_DEBUG, punit, "requests a boat.");
    ai->stats.passengers++;
    punit->ai.ferryboat = FERRY_WANTED;

}

/**************************************************************************
  Assign the passenger to the boat and vice versa.
**************************************************************************/
static void aiferry_psngr_meet_boat(struct unit *punit, struct unit *pferry)
{
  /* First delete the unit from the list of passengers and 
   * release its previous ferry */
  aiferry_clear_boat(punit);

  /* If ferry was available, update the stats */
  if (pferry->ai.passenger == FERRY_AVAILABLE) {
    ai_data_get(unit_owner(pferry))->stats.available_boats--;
  }

  /* Exchange the phone numbers */
  punit->ai.ferryboat = pferry->id;
  pferry->ai.passenger = punit->id;
}

/**************************************************************************
  Mark the ferry as available and update the statistics.
**************************************************************************/
static void aiferry_make_available(struct unit *pferry)
{
  if (pferry->ai.passenger != FERRY_AVAILABLE) {
    ai_data_get(unit_owner(pferry))->stats.available_boats++;
    pferry->ai.passenger = FERRY_AVAILABLE;
  }
}

/**************************************************************************
  Returns the number of available boats.  A simple accessor made to perform 
  debug checks.
**************************************************************************/
static int aiferry_avail_boats(struct player *pplayer)
{
  struct ai_data *ai = ai_data_get(pplayer);

  /* To developer: Switch this checking on when testing some new 
   * ferry code. */
#if 1
  int boats = 0;

  unit_list_iterate(pplayer->units, punit) {
    if (is_sailing_unit(punit) && is_ground_units_transport(punit) 
        && punit->ai.passenger == FERRY_AVAILABLE) {
      boats++;
    }
  } unit_list_iterate_end;

  if (boats != ai->stats.available_boats) {
    freelog(LOG_ERROR, "Boats miscounted: recorded %d but in reality %d",
            ai->stats.available_boats, boats);
  }
#endif

  return ai->stats.available_boats;
}


/* ================== functions to find a boat ========================= */

/**************************************************************************
  Combined cost function for a land unit looking for a ferry.  The path 
  finding first goes over the continent and then into the ocean where we 
  actually look for ferry.  Thus moves land-to-sea are allowed and moves
  sea-to-land are not.  A consequence is that we don't get into the cities
  on other continent, which might station boats.  This defficiency seems to
  be impossible to fix with the current PF structure, so it has to be
  accounted for in the actual ferry search function.

  For movements sea-to-sea the cost is collected via the extra cost 
  call-back.  Doesn't care for enemy/neutral tiles, these should be 
  excluded using a TB call-back.
**************************************************************************/
static int combined_land_sea_move(int x, int y, enum direction8 dir,
                                  int x1, int y1, 
                                  struct pf_parameter *param)
{
  struct tile *src_tile = map_get_tile(x, y);
  struct tile *tgt_tile = map_get_tile(x1, y1);
  int move_cost;

  if (is_ocean(tgt_tile->terrain)) {
    /* Any-to-Sea */
    move_cost = 0;
  } else if (src_tile->terrain == T_OCEAN) {
    /* Sea-to-Land */
    move_cost = PF_IMPOSSIBLE_MC;
  } else {
    /* Land-to-Land */
    move_cost = src_tile->move_cost[dir];
  }

  return move_cost;
}

/****************************************************************************
  EC callback to account for the cost of sea moves by a ferry hurrying to 
  pick our unit up.
****************************************************************************/
static int sea_move(int x, int y, enum known_type known,
                    struct pf_parameter *param)
{
  if (is_ocean(map_get_tile(x, y)->terrain)) {
    /* Approximately TURN_FACTOR / average ferry move rate 
     * we can pass a better guess of the move rate through param->data
     * but we don't know which boat we will find out there */
    return SINGLE_MOVE * PF_TURN_FACTOR / 12;
  } else {
    return 0;
  }
}

/****************************************************************************
  Proper and real PF function for finding a boat.  If you don't require
  the path to the ferry, pass path=NULL.

  WARNING: Due to the nature of this function and PF (see the comment of 
  combined_land_sea_move), the path won't lead onto the boat itself.

  FIXME: Actually check the capacity.
****************************************************************************/
int aiferry_find_boat(struct unit *punit, int cap, struct pf_path **path)
{
  int best_turns = FC_INFINITY;
  int best_id = 0;
  struct pf_parameter param;
  struct pf_map *search_map;


  UNIT_LOG(LOGLEVEL_FINDFERRY, punit, "asked find_ferry for a boat");

  if (aiferry_avail_boats(unit_owner(punit)) <= 0 
      && punit->ai.ferryboat <= 0) {
    /* No boats to be found (the second check is to ensure that we are not 
     * the ones keeping the last boat busy) */
    return 0;
  }

  pft_fill_unit_parameter(&param, punit);
  param.turn_mode = TM_WORST_TIME;
  param.get_TB = no_fights_or_unknown;
  param.get_EC = sea_move;
  param.get_MC = combined_land_sea_move;

  search_map = pf_create_map(&param);

  pf_iterator(search_map, pos) {
    int radius = (is_ocean(map_get_tile(pos.x, pos.y)->terrain) ? 1 : 0);

    if (pos.turn + pos.total_EC/PF_TURN_FACTOR > best_turns) {
      /* Won't find anything better */
      /* FIXME: This condition is somewhat dodgy */
      break;
    }
    
    square_iterate(pos.x, pos.y, radius, x, y) {
      struct tile *ptile = map_get_tile(x, y);
      
      unit_list_iterate(ptile->units, aunit) {
        if (is_ground_units_transport(aunit)
            && aunit->owner == punit->owner
            && (aunit->ai.passenger == FERRY_AVAILABLE
                || aunit->ai.passenger == punit->id)) {
          /* Turns for the unit to get to rendezvous pnt */
          int u_turns = pos.turn;
          /* Turns for the boat to get to the rendezvous pnt */
          int f_turns = ((pos.total_EC / PF_TURN_FACTOR * 16 
                          - aunit->moves_left) 
                         / unit_type(aunit)->move_rate);
          int turns = MAX(u_turns, f_turns);
          
          if (turns < best_turns) {
            UNIT_LOG(LOGLEVEL_FINDFERRY, punit, 
                     "Found a potential boat %s[%d](%d,%d)",
                     unit_type(aunit)->name, aunit->id, aunit->x, aunit->y);
            if (path) {
              *path = pf_next_get_path(search_map);
            }
            best_turns = turns;
            best_id = aunit->id;
          }
        }
      } unit_list_iterate_end;
    } square_iterate_end;
  } pf_iterator_end;
  pf_destroy_map(search_map);

  return best_id;
}

/****************************************************************************
  Find a boat within one move from us (i.e. a one we can board).

  FIXME: Actually check the capacity.
****************************************************************************/
static int aiferry_find_boat_nearby(struct unit *punit, int cap)
{
  UNIT_LOG(LOGLEVEL_FINDFERRY, punit, "asked find_ferry_nearby for a boat");

  square_iterate(punit->x, punit->y, 1, x, y) {
    unit_list_iterate(map_get_tile(x, y)->units, aunit) {
      if (is_ground_units_transport(aunit)
          && aunit->owner == punit->owner
          && (aunit->ai.passenger == FERRY_AVAILABLE
              || aunit->ai.passenger == punit->id)) {
          return aunit->id;
       }
    } unit_list_iterate_end;
  } square_iterate_end;

  return 0;
}


/* ============================= go by boat ============================== */

/****************************************************************************
  This function is to be called if punit needs to use a boat to get to the 
  destination.

  Return values: TRUE if got to or next to our destination, FALSE otherwise. 

  TODO: A big one is rendezvous points between units and boats.  When this is 
  implemented, we won't have to be at the coast to ask for a boat to come 
  to us.

  You MUST have warmap created before calling this function in order for 
  find_beachhead to work here.  This requirement should be removed.  For 
  example, we can require that (dest_x,dest_y) is on a coast.  
****************************************************************************/
bool aiferry_gobyboat(struct player *pplayer, struct unit *punit,
                      int dest_x, int dest_y)
{
  if (punit->transported_by <= 0) {
    /* We are not on a boat and we cannot walk */
    int boatid;
    struct unit *ferryboat = NULL;

    UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "will have to go to (%d,%d) by boat",
             dest_x, dest_y);

    if (!is_ocean_near_tile(punit->x, punit->y)) {
      struct pf_path *path_to_ferry = NULL;
      
      boatid = aiferry_find_boat(punit, 2, &path_to_ferry);
      if (boatid <= 0) {
        UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, 
                 "in ai_gothere cannot find any boats.");
        return FALSE;
      }

      ferryboat = find_unit_by_id(boatid);
      UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "found boat[%d](%d,%d), going there", 
               boatid, ferryboat->x, ferryboat->y);
      /* The path can be amphibious so we will stop at the coast.  
       * It might not lead _onto_ the boat. */
      if (!ai_unit_execute_path(punit, path_to_ferry)) { 
        /* Died. */
        pf_destroy_path(path_to_ferry);
        return FALSE;
      }
      pf_destroy_path(path_to_ferry);
    }

    if (!is_ocean_near_tile(punit->x, punit->y)) {
      /* Still haven't reached the coast */
      return FALSE;
    }

    /* We are on the coast, look around for a boat */
    boatid = aiferry_find_boat_nearby(punit, 2);
    if (boatid <= 0) {
      UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "requesting a boat.");
      aiferry_request_boat(punit);
      return FALSE;
    }

    /* Ok, a boat found, try boarding it */
    ferryboat = find_unit_by_id(boatid);
    UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "found a nearby boat[%d](%d,%d)",
             ferryboat->id, ferryboat->x, ferryboat->y);
    /* Setting ferry now in hope it won't run away even 
     * if we can't board it right now */
    aiferry_psngr_meet_boat(punit, ferryboat);

    if (is_tiles_adjacent(punit->x, punit->y, ferryboat->x, ferryboat->y)) {
      (void) ai_unit_move(punit, ferryboat->x, ferryboat->y);
    }

    if (!same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)) {
      /* Something prevented us from boarding */
      UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "couldn't board boat[%d](%d,%d)",
               ferryboat->id, ferryboat->x, ferryboat->y);
      return FALSE;
    }

    handle_unit_load(pplayer, punit->id, ferryboat->id);
    assert(punit->transported_by > 0);
  }

  if (punit->transported_by > 0) {
    /* We are on a boat, ride it! */
    struct unit *ferryboat = find_unit_by_id(punit->transported_by);

    /* Check if we are the passenger-in-charge */
    if (ferryboat->ai.passenger <= 0
        || ferryboat->ai.passenger == punit->id) {
      int beach_x, beach_y;     /* Destination for the boat */
      struct tile *dest_tile = map_get_tile(dest_x, dest_y);
      struct unit *bodyguard = find_unit_by_id(punit->ai.bodyguard);

      UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, 
               "got boat[%d](moves left: %d), going (%d,%d)",
               ferryboat->id, ferryboat->moves_left, dest_x, dest_y);
      aiferry_psngr_meet_boat(punit, ferryboat);

      /* If the location is not accessible directly from sea
       * or is defended and we are not marines, we will need a 
       * landing beach */
      if (!is_ocean_near_tile(dest_x, dest_y)
          ||((is_non_allied_city_tile(dest_tile, pplayer) 
              || is_non_allied_unit_tile(dest_tile, pplayer))
             && !unit_flag(punit, F_MARINES))) {
        if (!find_beachhead(punit, dest_x, dest_y, &beach_x, &beach_y)) {
          /* Nowhere to go */
          return FALSE;
        }
        UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, 
                 "Found beachhead (%d,%d)", beach_x, beach_y);
      } else {
        beach_x = dest_x;
        beach_y = dest_y;
      }

      set_goto_dest(ferryboat, beach_x, beach_y);
      set_goto_dest(punit, dest_x, dest_y);
      /* Grab bodyguard */
      if (bodyguard
          && !same_pos(punit->x, punit->y, bodyguard->x, bodyguard->y)) {
        if (!goto_is_sane(bodyguard, punit->x, punit->y, TRUE)
            || !ai_unit_goto(punit, punit->x, punit->y)) {
          /* Bodyguard can't get there or died en route */
          punit->ai.bodyguard = BODYGUARD_WANTED;
          bodyguard = NULL;
        } else if (bodyguard->moves_left <= 0) {
          /* Wait for me, I'm cooooming!! */
          UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "waiting for bodyguard");
          return FALSE;
        } else {
          /* Crap bodyguard. Got stuck somewhere. Ditch it! */
          UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "ditching useless bodyguard");
          punit->ai.bodyguard = BODYGUARD_WANTED;
          ai_unit_new_role(bodyguard, AIUNIT_NONE, -1, -1);
          bodyguard = NULL;
        }
      }
      if (bodyguard) {
        assert(same_pos(punit->x, punit->y, bodyguard->x, bodyguard->y));
        handle_unit_load(pplayer, bodyguard->id, ferryboat->id);
      }
      if (!ai_unit_goto(ferryboat, beach_x, beach_y)) {
        /* died */
        return FALSE;
      }
      if (!is_tiles_adjacent(ferryboat->x, ferryboat->y, beach_x, beach_y)
          && !same_pos(ferryboat->x, ferryboat->y, beach_x, beach_y)) {
        /* We are in still transit */
        return FALSE;
      }
    } else {
      /* Waiting for the boss to load and move us */
      UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "Cannot command boat [%d],"
               " its boss is [%d]", 
               ferryboat->id, ferryboat->ai.passenger);
      return FALSE;
    }

    UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "Our boat has arrived");
    handle_unit_activity_request(punit, ACTIVITY_IDLE);
  }

  return TRUE;
}


/* ===================== boat management ================================= */

/****************************************************************************
  A helper for ai_manage_ferryboat.  Finds a passenger for the ferry.
  Potential passengers signal the boats by setting their ai.ferry field to
  FERRY_WANTED.

  TODO: lift the path off the map
****************************************************************************/
static bool aiferry_findcargo(struct unit *punit)
{
  /* Path-finding stuff */
  struct pf_map *map;
  struct pf_parameter parameter;
  int passengers = ai_data_get(unit_owner(punit))->stats.passengers;

  if (passengers <= 0) {
    /* No passangers anywhere */
    return FALSE;
  }

  UNIT_LOG(LOGLEVEL_FERRY, punit, "Ferryboat is looking for cargo.");

  pft_fill_unit_overlap_param(&parameter, punit);
  /* If we have omniscience, we use it, since paths to some places
   * might be "blocked" by unknown.  We don't want to fight though */
  parameter.get_TB = no_fights;
  
  map = pf_create_map(&parameter);
  while (pf_next(map)) {
    struct pf_position pos;

    pf_next_get_position(map, &pos);
    
    unit_list_iterate(map_get_tile(pos.x, pos.y)->units, aunit) {
      if (punit->owner == aunit->owner 
          && (aunit->ai.ferryboat == FERRY_WANTED
              || aunit->ai.ferryboat == punit->id)) {
        UNIT_LOG(LOGLEVEL_FERRY, punit, 
                 "Found a potential cargo %s[%d](%d,%d), going there",
                 unit_type(aunit)->name, aunit->id, aunit->x, aunit->y);
        set_goto_dest(punit, aunit->x, aunit->y);
        /* Exchange phone numbers */
        aiferry_psngr_meet_boat(punit, aunit);
        pf_destroy_map(map);
        return TRUE;
      }
    } unit_list_iterate_end;
  }

  /* False positive can happen if we cannot find a route to the passenger
   * because of an internal sea or enemy blocking the route */
  UNIT_LOG(LOGLEVEL_FERRY, punit,
           "AI Passengers counting reported false positive %d", passengers);
  pf_destroy_map(map);
  return FALSE;
}

/****************************************************************************
  A helper for ai_manage_ferryboat.  Finds a city that wants a ferry.  It
  might signal for the ferry using pcity->ai.choice.need_boat field or
  it might simply be building a ferry of it's own.

  The city found will be set as the goto destination.

  TODO: lift the path off the map
  TODO (possible): put this and ai_ferry_findcargo into one PF-loop.  This 
  will save some code lines but will be faster in the rare cases when there
  passengers that can not be reached ("false positive").
****************************************************************************/
static bool aiferry_find_interested_city(struct unit *pferry)
{
  /* Path-finding stuff */
  struct pf_map *map;
  struct pf_parameter parameter;
  /* Early termination condition */
  int turns_horizon = FC_INFINITY;
  /* Future return value */
  bool needed = FALSE;

  UNIT_LOG(LOGLEVEL_FERRY, pferry, "Ferry looking for a city that needs it");

  pft_fill_unit_parameter(&parameter, pferry);
  /* We are looking for our own cities, no need to look into the unknown */
  parameter.get_TB = no_fights_or_unknown;
  parameter.omniscience = FALSE;
  
  map = pf_create_map(&parameter);
  parameter.turn_mode = TM_WORST_TIME;

  /* We want to consider the place we are currently in too, hence the 
   * do-while loop */
  do { 
    struct pf_position pos;
    struct city *pcity;

    pf_next_get_position(map, &pos);
    if (pos.turn >= turns_horizon) {
      /* Won't be able to find anything better than what we have */
      break;
    }

    pcity = map_get_city(pos.x, pos.y);
    
    if (pcity && pcity->owner == pferry->owner
        && (pcity->ai.choice.need_boat 
            || (pcity->is_building_unit
                && unit_has_role(pcity->currently_building, L_FERRYBOAT)))) {
      bool really_needed = TRUE;
      int turns = city_turns_to_build(pcity, pcity->currently_building,
                                      pcity->is_building_unit, TRUE);

      UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s (%d, %d) looks promising...", 
               pcity->name, pcity->x, pcity->y);

      if (pos.turn > turns && pcity->is_building_unit
          && unit_has_role(pcity->currently_building, L_FERRYBOAT)) {
        UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s is NOT suitable: "
                 "will finish building its own ferry too soon", pcity->name);
        continue;
      }

      if (turns >= turns_horizon) {
        UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s is NOT suitable: "
                 "has just started building", pcity->name);
        continue;
      }

      unit_list_iterate(map_get_tile(pos.x, pos.y)->units, aunit) {
        if (aunit != pferry && aunit->owner == pferry->owner
            && unit_has_role(aunit->type, L_FERRYBOAT)) {

          UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s is NOT suitable: "
                   "has another ferry", pcity->name);
          really_needed = FALSE;
          break;
        }
      } unit_list_iterate_end;

      if (really_needed) {
        UNIT_LOG(LOGLEVEL_FERRY, pferry, "will go to %s unless we "
                 "find something better", pcity->name);
        set_goto_dest(pferry, pos.x, pos.y);
        turns_horizon = turns;
        needed = TRUE;
      }
    }
  } while (pf_next(map));

  pf_destroy_map(map);
  return needed;
}

/****************************************************************************
  It's about 12 feet square and has a capacity of almost 1000 pounds.
  It is well constructed of teak, and looks seaworthy.

  Manage ferryboat.  If there is a passenger-in-charge, we let it drive the 
  boat.  If there isn't, appoint one from those we have on board.

  If there is no one aboard, look for potential cargo.  If none found, 
  explore and then go to the nearest port.
****************************************************************************/
void ai_manage_ferryboat(struct player *pplayer, struct unit *punit)
{
  struct city *pcity;

  CHECK_UNIT(punit);

  /* Try to recover hitpoints if we are in a city, before we do anything */
  if (punit->hp < unit_type(punit)->hp 
      && (pcity = map_get_city(punit->x, punit->y))) {
    UNIT_LOG(LOGLEVEL_FERRY, punit, "waiting in %s to recover hitpoints", 
             pcity->name);
    return;
  }

  /* Check if we are an empty barbarian boat and so not needed */
  if (is_barbarian(pplayer) && !punit->occupy) {
    wipe_unit(punit);
    return;
  }

  do {
    /* Do we have the passenger-in-charge on board? */
    struct tile *ptile = map_get_tile(punit->x, punit->y);

    if (punit->ai.passenger > 0) {
      struct unit *psngr = find_unit_by_id(punit->ai.passenger);
      
      /* If the passenger-in-charge is adjacent, we should wait for it to 
       * board.  We will pass control to it later.
       * FIXME: A possible side-effect: a boat will linger near a passenger 
       * which already landed. */
      if (!psngr 
          || real_map_distance(punit->x, punit->y, psngr->x, psngr->y) > 1) {
        UNIT_LOG(LOGLEVEL_FERRY, punit, 
                 "recorded passenger[%d] is not on board, checking for others",
                 punit->ai.passenger);
        punit->ai.passenger = 0;
      }
    }

    if (punit->ai.passenger <= 0) {
      struct unit *candidate = NULL;
    
      /* Try to select passanger-in-charge from among our passengers */
      unit_list_iterate(ptile->units, aunit) {
        if (aunit->ai.ferryboat != punit->id 
            && aunit->ai.ferryboat != FERRY_WANTED) {
          continue;
        }
      
        if (aunit->ai.ai_role != AIUNIT_ESCORT) {
          candidate = aunit;
          break;
        } else {
          /* Bodyguards shouldn't be in charge of boats so continue looking */
          candidate = aunit;
        }
      } unit_list_iterate_end;
      
      if (candidate) {
        UNIT_LOG(LOGLEVEL_FERRY, punit, 
                 "appointed %s[%d] our passenger-in-charge",
                 unit_type(candidate)->name, candidate->id);
        aiferry_psngr_meet_boat(candidate, punit);
      }
    }

    if (punit->ai.passenger > 0) {
      int bossid = punit->ai.passenger;    /* Loop prevention */
      struct unit *boss = find_unit_by_id(punit->ai.passenger);
      int id = punit->id;                  /* To check if survived */

      assert(boss != NULL);

      if (unit_flag(boss, F_SETTLERS) || unit_flag(boss, F_CITIES)) {
        /* Temporary hack: settlers all go in the end, forcing them 
         * earlier might mean uninitialised cache, so just wait for them */
        return;
      }

      UNIT_LOG(LOGLEVEL_FERRY, punit, "passing control to %s[%d]",
                unit_type(boss)->name, boss->id);
      ai_manage_unit(pplayer, boss);
    
      if (!find_unit_by_id(id) || punit->moves_left <= 0) {
        return;
      }
      if (find_unit_by_id(bossid) 
          && same_pos(punit->x, punit->y, boss->x, boss->y)) {
        /* The boss decided to stay put on the ferry. We aren't moving. */
        return;
      }
    } else {
      /* Cannot select a passenger-in-charge */
      break;
    }
  } while (punit->occupy != 0);

  /* Not carrying anyone, even the ferryman */

  if (IS_ATTACKER(punit) && punit->moves_left > 0) {
     /* AI used to build frigates to attack and then use them as ferries 
      * -- Syela */
     UNIT_LOG(LOGLEVEL_FERRY, punit, "passing ferry over to attack code");
     ai_manage_military(pplayer, punit);
     return;
  }

  UNIT_LOG(LOGLEVEL_FERRY, punit, "Ferryboat is not carrying anyone.");
  aiferry_make_available(punit);
  handle_unit_activity_request(punit, ACTIVITY_IDLE);
  ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
  CHECK_UNIT(punit);

  /* Try to find passengers */
  if (aiferry_findcargo(punit)) {
    UNIT_LOG(LOGLEVEL_FERRY, punit, "picking up cargo");
    ai_unit_goto(punit, goto_dest_x(punit), goto_dest_y(punit));
    return;
  }

  /* Try to find a city that needs a ferry */
  if (aiferry_find_interested_city(punit)) {
    if (same_pos(punit->x, punit->y, goto_dest_x(punit), goto_dest_y(punit))) {
      UNIT_LOG(LOGLEVEL_FERRY, punit, "staying in city that needs us");
      return;
    } else {
      UNIT_LOG(LOGLEVEL_FERRY, punit, "going to city that needs us");
      (void) ai_unit_goto(punit, goto_dest_x(punit), goto_dest_y(punit));
      return;
    }
  }

  UNIT_LOG(LOGLEVEL_FERRY, punit, "Passing control of ferry to explorer code");
  (void) ai_manage_explorer(punit);

  if (punit->moves_left > 0) {
    struct city *pcity = find_nearest_safe_city(punit);
    if (pcity) {
      set_goto_dest(punit, pcity->x, pcity->y);
      UNIT_LOG(LOGLEVEL_FERRY, punit, "No work, going home");
      (void) ai_unit_goto(punit, pcity->x, pcity->y);
    }
  }
 
  return;
}
? ttt.gz
? ai/aiferry.c
? ai/aiferry.h
? ai/aisettler.c
? ai/aisettler.h
? data/flags/Makefile
? data/flags/Makefile.in
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 16:23:52 -0000
@@ -21,6 +21,8 @@
                aicity.h        \
                aidata.c        \
                aidata.h        \
+               aiferry.c       \
+               aiferry.h       \
                aihand.c        \
                aihand.h        \
                aihunt.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 16:23:52 -0000
@@ -37,6 +37,7 @@
 #include "advdiplomacy.h"
 #include "advmilitary.h"
 #include "aicity.h"
+#include "aiferry.h"
 #include "aihand.h"
 #include "ailog.h"
 #include "aitools.h"
@@ -191,10 +192,7 @@
 
   ai->stats.workers = fc_calloc(ai->num_continents + 1, sizeof(int));
   ai->stats.cities = fc_calloc(ai->num_continents + 1, sizeof(int));
-  ai->stats.passengers = 0;
   ai->stats.average_production = 0;
-  ai->stats.boats = 0;
-  ai->stats.available_boats = 0;
   city_list_iterate(pplayer->cities, pcity) {
     ai->stats.cities[(int)map_get_continent(pcity->x, pcity->y)]++;
     ai->stats.average_production += pcity->shield_surplus;
@@ -204,16 +202,6 @@
   unit_list_iterate(pplayer->units, punit) {
     struct tile *ptile = map_get_tile(punit->x, punit->y);
 
-    if (is_sailing_unit(punit) && is_ground_units_transport(punit)) {
-      ai->stats.boats++;
-      if (punit->ai.passenger == FERRY_AVAILABLE) {
-        ai->stats.available_boats++;
-      }
-    }
-    if (punit->ai.ferryboat == FERRY_WANTED) {
-      UNIT_LOG(LOG_DEBUG, punit, "want a boat.");
-      ai->stats.passengers++;
-    }
     if (!is_ocean(ptile->terrain) && unit_flag(punit, F_SETTLERS)) {
       ai->stats.workers[(int)map_get_continent(punit->x, punit->y)]++;
     }
@@ -226,6 +214,7 @@
       }
     }
   } unit_list_iterate_end;
+  aiferry_init_stats(pplayer);
 
   /*** Diplomacy ***/
 
@@ -395,104 +384,6 @@
 }
 
 /**************************************************************************
-  Use this wrapper to correctly update the statistics. Use ferry=NULL to
-  request a ferry.  Should be used in conjunction with ai_set_passenger
-  if ferry!=NULL.
-**************************************************************************/
-void ai_set_ferry(struct unit *punit, struct unit *ferry)
-{
-  /* First delete unit from the list of passengers and release its ferry */
-  ai_clear_ferry(punit);
-
-  if (!ferry) {
-    struct ai_data *ai = ai_data_get(unit_owner(punit));
-
-    UNIT_LOG(LOG_DEBUG, punit, "requests a boat.");
-    ai->stats.passengers++;
-    punit->ai.ferryboat = FERRY_WANTED;
-  } else {
-    punit->ai.ferryboat = ferry->id;
-  }
-}
-
-/**************************************************************************
-  Use this wrapper to correctly update the statistics.  Should be used 
-  before a unit stops to wait for a boat.
-**************************************************************************/
-void ai_clear_ferry(struct unit *punit)
-{
-  if (punit->ai.ferryboat == FERRY_WANTED) {
-    struct ai_data *ai = ai_data_get(unit_owner(punit));
-
-    ai->stats.passengers--;
-  } else {
-    struct unit *ferry = find_unit_by_id(punit->ai.ferryboat);
-    
-    if (ferry && ferry->ai.passenger == punit->id) {
-      struct ai_data *ai = ai_data_get(unit_owner(punit));
-
-      /* punit doesn't want us anymore */
-      ferry->ai.passenger = FERRY_AVAILABLE;
-      ai->stats.available_boats++;
-    }
-  }
-
-  punit->ai.ferryboat = FERRY_NONE;
-}
-
-
-/**************************************************************************
-  Use this wrapper to correctly update the statistics. Use NULL to
-  unregister any passenger that might be there.
-**************************************************************************/
-void ai_set_passenger(struct unit *punit, struct unit *passenger)
-{
-  if (!passenger && punit->ai.passenger != FERRY_AVAILABLE) {
-    struct ai_data *ai = ai_data_get(unit_owner(punit));
-
-    ai->stats.available_boats++;
-    punit->ai.passenger = FERRY_AVAILABLE;
-  } else if (passenger) {
-    if (punit->ai.passenger == FERRY_AVAILABLE) {
-      struct ai_data *ai = ai_data_get(unit_owner(punit));
-      ai->stats.available_boats--;
-    }
-    punit->ai.passenger = passenger->id;
-  }
-}
-
-/**************************************************************************
-  Returns the number of available boats.  A simple accessor made to perform 
-  debug checks.
-**************************************************************************/
-int ai_available_boats(struct player *pplayer)
-{
-  struct ai_data *ai = ai_data_get(pplayer);
-
-  /* To developer: Switch this checking on when testing some new 
-   * ferry code. */
-#if 0
-  int boats = 0;
-
-  unit_list_iterate(pplayer->units, punit) {
-    struct tile *ptile = map_get_tile(punit->x, punit->y);
-
-    if (is_sailing_unit(punit) && is_ground_units_transport(punit) 
-       && punit->ai.passenger == FERRY_AVAILABLE) {
-      boats++;
-    }
-  } unit_list_iterate_end;
-
-  if (boats != ai->stats.available_boats) {
-    freelog(LOG_ERROR, "Boats miscounted: recorded %d but in reality %d",
-           ai->stats.available_boats, boats);
-  }
-#endif
-
-  return ai->stats.available_boats;
-}
-
-/**************************************************************************
   Deinitialize data
 **************************************************************************/
 void ai_data_done(struct player *pplayer)
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 16:23:52 -0000
@@ -135,10 +135,5 @@
 
 struct ai_data *ai_data_get(struct player *pplayer);
 
-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);
-int ai_available_boats(struct player *pplayer);
-
 
 #endif
Index: ai/aidiplomat.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidiplomat.c,v
retrieving revision 1.33
diff -u -r1.33 aidiplomat.c
--- ai/aidiplomat.c     25 Feb 2004 20:23:49 -0000      1.33
+++ ai/aidiplomat.c     16 Jun 2004 16:23:52 -0000
@@ -506,7 +506,7 @@
       struct pf_path *path;
 
       MAPSTEP(x, y, pos.x, pos.y, DIR_REVERSE(pos.dir_to_here));
-      path = pf_get_path(map, pos.x, pos.y);
+      path = pf_get_path(map, x, y);
       if (!path || !ai_unit_execute_path(punit, path) 
           || punit->moves_left <= 0) {
         pf_destroy_path(path);
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 16:23:52 -0000
@@ -46,6 +46,7 @@
 
 #include "aicity.h"
 #include "aidata.h"
+#include "aiferry.h"
 #include "ailog.h"
 #include "aiunit.h"
 
@@ -198,160 +199,6 @@
   /* What if we have a bodyguard, but don't need one? */
 }
 
-/****************************************************************************
-  Combined cost function for a land unit looking for a ferry.  The path 
-  finding first goes over the continent and then into the ocean where we 
-  actually look for ferry.  Thus moves land-to-sea are allowed and moves
-  sea-to-land are not.  A consequence is that we don't get into the cities
-  on other continent, which might station boats.  This defficiency seems to
-  be impossible to fix with the current PF structure, so it has to be
-  accounted for in the actual ferry search function.
-
-  For movements sea-to-sea the cost is collected via the extra cost 
-  call-back.  Doesn't care for enemy/neutral tiles, these should be excluded
-  using a TB call-back.
-****************************************************************************/
-static int combined_land_sea_move(int x, int y, enum direction8 dir,
-                                  int x1, int y1, 
-                                  struct pf_parameter *param)
-{
-  struct tile *src_tile = map_get_tile(x, y);
-  struct tile *tgt_tile = map_get_tile(x1, y1);
-  int move_cost;
-
-  if (is_ocean(tgt_tile->terrain)) {
-    /* Any-to-Sea */
-    move_cost = 0;
-  } else if (src_tile->terrain == T_OCEAN) {
-    /* Sea-to-Land */
-    move_cost = PF_IMPOSSIBLE_MC;
-  } else {
-    /* Land-to-Land */
-    move_cost = src_tile->move_cost[dir];
-  }
-
-  return move_cost;
-}
-
-/****************************************************************************
-  EC callback to account for the cost of sea moves by a ferry hurrying to 
-  pick our unit up.
-****************************************************************************/
-static int sea_move(int x, int y, enum known_type known,
-                    struct pf_parameter *param)
-{
-  if (is_ocean(map_get_tile(x, y)->terrain)) {
-    /* Approximately TURN_FACTOR / average ferry move rate 
-     * we can pass a better guess of the move rate through param->data
-     * but we don't know which boat we will find out there */
-    return SINGLE_MOVE * PF_TURN_FACTOR / 12;
-  } else {
-    return 0;
-  }
-}
-
-#define LOGLEVEL_FINDFERRY LOG_DEBUG
-/****************************************************************************
-  Proper and real PF function for finding a boat.  If you don't require
-  the path to the ferry, pass path=NULL.
-
-  WARNING: Due to the nature of this function and PF (see the comment of 
-  combined_land_sea_move), the path won't lead onto the boat itself.
-
-  FIXME: Actually check the capacity.
-****************************************************************************/
-int find_ferry(struct unit *punit, int cap, struct pf_path **path)
-{
-  int best_turns = FC_INFINITY;
-  int best_id = 0;
-  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 
-      && punit->ai.ferryboat <= 0) {
-    /* No boats to be found (the second check is to ensure that we are not 
-     * the ones keeping the last boat busy) */
-    return 0;
-  }
-
-  pft_fill_unit_parameter(&param, punit);
-  param.turn_mode = TM_WORST_TIME;
-  param.get_TB = no_fights_or_unknown;
-  param.get_EC = sea_move;
-  param.get_MC = combined_land_sea_move;
-
-  search_map = pf_create_map(&param);
-
-  pf_iterator(search_map, pos) {
-    int radius = (is_ocean(map_get_tile(pos.x, pos.y)->terrain) ? 1 : 0);
-
-    if (pos.turn + pos.total_EC/PF_TURN_FACTOR > best_turns) {
-      /* Won't find anything better */
-      /* FIXME: This condition is somewhat dodgy */
-      break;
-    }
-    
-    square_iterate(pos.x, pos.y, radius, x, y) {
-      struct tile *ptile = map_get_tile(x, y);
-      
-      unit_list_iterate(ptile->units, aunit) {
-        if (is_ground_units_transport(aunit)
-            && aunit->owner == punit->owner
-            && (aunit->ai.passenger == FERRY_AVAILABLE
-                || aunit->ai.passenger == punit->id)) {
-          /* Turns for the unit to get to rendezvous pnt */
-          int u_turns = pos.turn;
-          /* Turns for the boat to get to the rendezvous pnt */
-          int f_turns = ((pos.total_EC / PF_TURN_FACTOR * 16 
-                          - aunit->moves_left) 
-                         / unit_type(aunit)->move_rate);
-          int turns = MAX(u_turns, f_turns);
-          
-          if (turns < best_turns) {
-            UNIT_LOG(LOGLEVEL_FINDFERRY, punit, 
-                     "Found a potential boat %s[%d](%d,%d)",
-                     unit_type(aunit)->name, aunit->id, aunit->x, aunit->y);
-           if (path) {
-             *path = pf_next_get_path(search_map);
-           }
-            best_turns = turns;
-            best_id = aunit->id;
-          }
-        }
-      } unit_list_iterate_end;
-    } square_iterate_end;
-  } pf_iterator_end;
-  pf_destroy_map(search_map);
-
-  return best_id;
-}
-
-/****************************************************************************
-  Find a boat within one move from us (i.e. a one we can board).
-
-  FIXME: Actually check the capacity.
-****************************************************************************/
-static int find_ferry_nearby(struct unit *punit, int cap)
-{
-  UNIT_LOG(LOGLEVEL_FINDFERRY, punit, "asked find_ferry_nearby for a boat");
-
-  square_iterate(punit->x, punit->y, 1, x, y) {
-    unit_list_iterate(map_get_tile(x, y)->units, aunit) {
-      if (is_ground_units_transport(aunit)
-         && aunit->owner == punit->owner
-          && (aunit->ai.passenger == FERRY_AVAILABLE
-              || aunit->ai.passenger == punit->id)) {
-         return aunit->id;
-       }
-    } unit_list_iterate_end;
-  } square_iterate_end;
-
-  return 0;
-}
-
 #define LOGLEVEL_GOTHERE LOG_DEBUG
 /****************************************************************************
   This is ferry-enabled goto.  Should not normally be used for non-ferried 
@@ -368,8 +215,6 @@
 bool ai_gothere(struct player *pplayer, struct unit *punit,
                 int dest_x, int dest_y)
 {
-  struct unit *bodyguard = find_unit_by_id(punit->ai.bodyguard);
-
   CHECK_UNIT(punit);
 
   if (same_pos(dest_x, dest_y, punit->x, punit->y)) {
@@ -381,153 +226,12 @@
   /* FIXME: If bodyguard is _really_ necessary, don't go anywhere */
   ai_gothere_bodyguard(punit, dest_x, dest_y);
 
-  if (punit->transported_by <= 0 
-      && !goto_is_sane(punit, dest_x, dest_y, TRUE)) {
-    /* We are not on a boat and we cannot walk */
-    int boatid;
-    struct unit *ferryboat = NULL;
-
-    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "will have to go to (%d,%d) by boat",
-             dest_x, dest_y);
-
-    if (!is_ocean_near_tile(punit->x, punit->y)) {
-      struct pf_path *path_to_ferry = NULL;
-      
-      boatid = find_ferry(punit, 2, &path_to_ferry);
-      if (boatid <= 0) {
-        UNIT_LOG(LOGLEVEL_GOTHERE, punit, 
-                "in ai_gothere cannot find any boats.");
-        return FALSE;
-      }
-
-      ferryboat = find_unit_by_id(boatid);
-      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "found boat[%d](%d,%d), going there", 
-              boatid, ferryboat->x, ferryboat->y);
-      /* The path can be amphibious so we will stop at the coast.  
-       * It might not lead _onto_ the boat. */
-      if (!ai_unit_execute_path(punit, path_to_ferry)) { 
-        /* Died. */
-       pf_destroy_path(path_to_ferry);
-        return FALSE;
-      }
-      pf_destroy_path(path_to_ferry);
-    }
-
-    if (!is_ocean_near_tile(punit->x, punit->y)) {
-      /* Still haven't reached the coast */
-      return FALSE;
-    }
-
-    /* We are on the coast, look around for a boat */
-    boatid = find_ferry_nearby(punit, 2);
-    if (boatid <= 0) {
-      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "requesting a boat.");
-      ai_set_ferry(punit, NULL);
-      return FALSE;
-    }
-
-    /* Ok, a boat found, try boarding it */
-    ferryboat = find_unit_by_id(boatid);
-    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "found a nearby boat[%d](%d,%d)",
-            ferryboat->id, ferryboat->x, ferryboat->y);
-    /* Setting ferry now in hope it won't run away even 
-     * if we can't board it right now */
-    ai_set_ferry(punit, ferryboat);
-    ai_set_passenger(ferryboat, punit);
-
-    if (is_tiles_adjacent(punit->x, punit->y, ferryboat->x, ferryboat->y)) {
-      (void) ai_unit_move(punit, ferryboat->x, ferryboat->y);
-    }
-
-    if (!same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)) {
-      /* Something prevented us from boarding */
-      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "couldn't board boat[%d](%d,%d)",
-              ferryboat->id, ferryboat->x, ferryboat->y);
+  if (punit->transported_by > 0 
+      || !goto_is_sane(punit, dest_x, dest_y, TRUE)) {
+    /* Must go by boat, call an aiferryboat function */
+    if (!aiferry_gobyboat(pplayer, punit, dest_x, dest_y)) {
       return FALSE;
     }
-
-    handle_unit_load(pplayer, punit->id, ferryboat->id);
-    assert(punit->transported_by > 0);
-  }
-
-  if (punit->transported_by > 0) {
-    /* We are on a boat, ride it! */
-    struct unit *ferryboat = find_unit_by_id(punit->transported_by);
-
-    /* Check if we are the passenger-in-charge */
-    if (ferryboat->ai.passenger <= 0
-        || ferryboat->ai.passenger == punit->id) {
-      int beach_x, beach_y;     /* Destination for the boat */
-      struct tile *dest_tile = map_get_tile(dest_x, dest_y);
-
-      UNIT_LOG(LOGLEVEL_GOTHERE, punit, 
-              "got boat[%d](moves left: %d), going (%d,%d)",
-               ferryboat->id, ferryboat->moves_left, dest_x, dest_y);
-      ai_set_passenger(ferryboat, punit);
-
-      /* If the location is not accessible directly from sea
-       * or is defended and we are not marines, we will need a 
-       * landing beach */
-      if (!is_ocean_near_tile(dest_x, dest_y)
-          ||((is_non_allied_city_tile(dest_tile, pplayer) 
-              || is_non_allied_unit_tile(dest_tile, pplayer))
-             && !unit_flag(punit, F_MARINES))) {
-        if (!find_beachhead(punit, dest_x, dest_y, &beach_x, &beach_y)) {
-          /* Nowhere to go */
-          return FALSE;
-        }
-        UNIT_LOG(LOGLEVEL_GOTHERE, punit, 
-                 "Found beachhead (%d,%d)", beach_x, beach_y);
-      } else {
-        beach_x = dest_x;
-        beach_y = dest_y;
-      }
-
-      set_goto_dest(ferryboat, beach_x, beach_y);
-      set_goto_dest(punit, dest_x, dest_y);
-      /* Grab bodyguard */
-      if (bodyguard
-          && !same_pos(punit->x, punit->y, bodyguard->x, bodyguard->y)) {
-        if (!goto_is_sane(bodyguard, punit->x, punit->y, TRUE)
-            || !ai_unit_goto(punit, punit->x, punit->y)) {
-          /* Bodyguard can't get there or died en route */
-          punit->ai.bodyguard = BODYGUARD_WANTED;
-          bodyguard = NULL;
-        } else if (bodyguard->moves_left <= 0) {
-          /* Wait for me, I'm cooooming!! */
-          UNIT_LOG(LOGLEVEL_GOTHERE, punit, "waiting for bodyguard");
-          return TRUE;
-        } else {
-          /* Crap bodyguard. Got stuck somewhere. Ditch it! */
-          UNIT_LOG(LOGLEVEL_GOTHERE, punit, "ditching useless bodyguard");
-          punit->ai.bodyguard = BODYGUARD_WANTED;
-          ai_unit_new_role(bodyguard, AIUNIT_NONE, -1, -1);
-          bodyguard = NULL;
-        }
-      }
-      if (bodyguard) {
-        assert(same_pos(punit->x, punit->y, bodyguard->x, bodyguard->y));
-       handle_unit_load(pplayer, bodyguard->id, ferryboat->id);
-      }
-      if (!ai_unit_goto(ferryboat, beach_x, beach_y)) {
-        /* died */
-        return FALSE;
-      }
-      if (!is_tiles_adjacent(ferryboat->x, ferryboat->y, beach_x, beach_y)
-          && !same_pos(ferryboat->x, ferryboat->y, beach_x, beach_y)) {
-        /* We are in still transit */
-        return FALSE;
-      }
-    } else {
-      /* Waiting for the boss to load and move us */
-      UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Cannot command boat [%d],"
-               " its boss is [%d]", 
-               ferryboat->id, ferryboat->ai.passenger);
-      return FALSE;
-    }
-
-    UNIT_LOG(LOGLEVEL_GOTHERE, punit, "Our boat has arrived");
-    handle_unit_activity_request(punit, ACTIVITY_IDLE);
   }
 
   /* Go where we should be going if we can, and are at our destination 
@@ -547,7 +251,7 @@
 
   if (punit->ai.ferryboat > 0 && punit->transported_by <= 0) {
     /* We probably just landed, release our boat */
-    ai_clear_ferry(punit);
+    aiferry_clear_boat(punit);
   }
   
   /* Dead unit shouldn't reach this point */
@@ -612,7 +316,7 @@
 
   /* Free our ferry.  Most likely it has been done already. */
   if (task == AIUNIT_NONE || task == AIUNIT_DEFEND_HOME) {
-    ai_clear_ferry(punit);
+    aiferry_clear_boat(punit);
   }
 
   if (punit->activity == ACTIVITY_GOTO) {
Index: ai/aitools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.h,v
retrieving revision 1.43
diff -u -r1.43 aitools.h
--- ai/aitools.h        11 Jun 2004 14:29:45 -0000      1.43
+++ ai/aitools.h        16 Jun 2004 16:23:52 -0000
@@ -38,14 +38,6 @@
   BODYGUARD_NONE
 };
 
-#define FERRY_WANTED      -1      /* For passengers in need of a boat */
-#define FERRY_AVAILABLE   -1      /* For boats looking for a passenger */
-/* The below is used by both passengers and ferries.  Passengers use it to 
- * say they don't have a ferry and don't need one.  Ferries use it to say 
- * that they do not have a passenger-in-charge, but are not available for 
- * bookings yet (might carry some passengers still). */
-#define FERRY_NONE         0
-
 int military_amortize(struct player *pplayer, struct city *pcity, 
                       int value, int delay, int build_cost);
 int stack_cost(struct unit *pdef);
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.319
diff -u -r1.319 aiunit.c
--- ai/aiunit.c 14 Jun 2004 10:37:52 -0000      1.319
+++ ai/aiunit.c 16 Jun 2004 16:23:53 -0000
@@ -53,6 +53,7 @@
 #include "aicity.h"
 #include "aidata.h"
 #include "aidiplomat.h"
+#include "aiferry.h"
 #include "aihand.h"
 #include "aihunt.h"
 #include "ailog.h"
@@ -62,8 +63,6 @@
 
 #define LOGLEVEL_RECOVERY LOG_DEBUG
 
-static void ai_manage_unit(struct player *pplayer, struct unit *punit);
-static void ai_manage_military(struct player *pplayer,struct unit *punit);
 static void ai_manage_caravan(struct player *pplayer, struct unit *punit);
 static void ai_manage_barbarian_leader(struct player *pplayer,
                                       struct unit *leader);
@@ -2019,7 +2018,7 @@
   decision not easily taken, since we also want to protect unsafe
   cities, at least most of the time.
 ***********************************************************************/
-static struct city *find_nearest_safe_city(struct unit *punit)
+struct city *find_nearest_safe_city(struct unit *punit)
 {
   struct player *pplayer = unit_owner(punit);
   struct city *acity = NULL;
@@ -2255,320 +2254,6 @@
   }
 }
 
-#define LOGLEVEL_FERRY LOG_DEBUG
-/****************************************************************************
-  A helper for ai_manage_ferryboat.  Finds a passenger for the ferry.
-  Potential passengers signal the boats by setting their ai.ferry field to
-  FERRY_WANTED.
-
-  TODO: lift the path off the map
-****************************************************************************/
-static bool ai_ferry_findcargo(struct unit *punit)
-{
-  /* Path-finding stuff */
-  struct pf_map *map;
-  struct pf_parameter parameter;
-  int passengers = ai_data_get(unit_owner(punit))->stats.passengers;
-
-  if (passengers <= 0) {
-    /* No passangers anywhere */
-    return FALSE;
-  }
-
-  UNIT_LOG(LOGLEVEL_FERRY, punit, "Ferryboat is looking for cargo.");
-
-  pft_fill_unit_overlap_param(&parameter, punit);
-  /* If we have omniscience, we use it, since paths to some places
-   * might be "blocked" by unknown.  We don't want to fight though */
-  parameter.get_TB = no_fights;
-  
-  map = pf_create_map(&parameter);
-  while (pf_next(map)) {
-    struct pf_position pos;
-
-    pf_next_get_position(map, &pos);
-    
-    unit_list_iterate(map_get_tile(pos.x, pos.y)->units, aunit) {
-      if (punit->owner == aunit->owner 
-         && (aunit->ai.ferryboat == FERRY_WANTED
-             || aunit->ai.ferryboat == punit->id)) {
-        UNIT_LOG(LOGLEVEL_FERRY, punit, 
-                 "Found a potential cargo %s[%d](%d,%d), going there",
-                 unit_type(aunit)->name, aunit->id, aunit->x, aunit->y);
-        set_goto_dest(punit, aunit->x, aunit->y);
-        /* Exchange phone numbers */
-        ai_set_passenger(punit, aunit);
-        ai_set_ferry(aunit, punit);
-        pf_destroy_map(map);
-        return TRUE;
-      }
-    } unit_list_iterate_end;
-  }
-
-  /* False positive can happen if we cannot find a route to the passenger
-   * because of an internal sea or enemy blocking the route */
-  UNIT_LOG(LOGLEVEL_FERRY, punit,
-           "AI Passengers counting reported false positive %d", passengers);
-  pf_destroy_map(map);
-  return FALSE;
-}
-
-/****************************************************************************
-  A helper for ai_manage_ferryboat.  Finds a city that wants a ferry.  It
-  might signal for the ferry using pcity->ai.choice.need_boat field or
-  it might simply be building a ferry of it's own.
-
-  The city found will be set as the goto destination.
-
-  TODO: lift the path off the map
-  TODO (possible): put this and ai_ferry_findcargo into one PF-loop.  This 
-  will save some code lines but will be faster in the rare cases when there
-  passengers that can not be reached ("false positive").
-****************************************************************************/
-static bool ai_ferry_find_interested_city(struct unit *pferry)
-{
-  /* Path-finding stuff */
-  struct pf_map *map;
-  struct pf_parameter parameter;
-  /* Early termination condition */
-  int turns_horizon = FC_INFINITY;
-  /* Future return value */
-  bool needed = FALSE;
-
-  UNIT_LOG(LOGLEVEL_FERRY, pferry, "Ferry looking for a city that needs it");
-
-  pft_fill_unit_parameter(&parameter, pferry);
-  /* We are looking for our own cities, no need to look into the unknown */
-  parameter.get_TB = no_fights_or_unknown;
-  parameter.omniscience = FALSE;
-  
-  map = pf_create_map(&parameter);
-  parameter.turn_mode = TM_WORST_TIME;
-
-  /* We want to consider the place we are currently in too, hence the 
-   * do-while loop */
-  do { 
-    struct pf_position pos;
-    struct city *pcity;
-
-    pf_next_get_position(map, &pos);
-    if (pos.turn >= turns_horizon) {
-      /* Won't be able to find anything better than what we have */
-      break;
-    }
-
-    pcity = map_get_city(pos.x, pos.y);
-    
-    if (pcity && pcity->owner == pferry->owner
-        && (pcity->ai.choice.need_boat 
-            || (pcity->is_building_unit
-               && unit_has_role(pcity->currently_building, L_FERRYBOAT)))) {
-      bool really_needed = TRUE;
-      int turns = city_turns_to_build(pcity, pcity->currently_building,
-                                      pcity->is_building_unit, TRUE);
-
-      UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s (%d, %d) looks promising...", 
-               pcity->name, pcity->x, pcity->y);
-
-      if (pos.turn > turns && pcity->is_building_unit
-          && unit_has_role(pcity->currently_building, L_FERRYBOAT)) {
-        UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s is NOT suitable: "
-                 "will finish building its own ferry too soon", pcity->name);
-        continue;
-      }
-
-      if (turns >= turns_horizon) {
-        UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s is NOT suitable: "
-                 "has just started building", pcity->name);
-        continue;
-      }
-
-      unit_list_iterate(map_get_tile(pos.x, pos.y)->units, aunit) {
-       if (aunit != pferry && aunit->owner == pferry->owner
-            && unit_has_role(aunit->type, L_FERRYBOAT)) {
-
-          UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s is NOT suitable: "
-                   "has another ferry", pcity->name);
-         really_needed = FALSE;
-         break;
-       }
-      } unit_list_iterate_end;
-
-      if (really_needed) {
-        UNIT_LOG(LOGLEVEL_FERRY, pferry, "will go to %s unless we "
-                 "find something better", pcity->name);
-        set_goto_dest(pferry, pos.x, pos.y);
-        turns_horizon = turns;
-        needed = TRUE;
-      }
-    }
-  } while (pf_next(map));
-
-  pf_destroy_map(map);
-  return needed;
-}
-
-/****************************************************************************
-  It's about 12 feet square and has a capacity of almost 1000 pounds.
-  It is well constructed of teak, and looks seaworthy.
-
-  Manage ferryboat.  If there is a passenger-in-charge, we let it drive the 
-  boat.  If there isn't, appoint one from those we have on board.
-
-  If there is no one aboard, look for potential cargo.  If none found, 
-  explore and then go to the nearest port.
-****************************************************************************/
-static void ai_manage_ferryboat(struct player *pplayer, struct unit *punit)
-{
-  struct city *pcity;
-
-  CHECK_UNIT(punit);
-
-  /* Try to recover hitpoints if we are in a city, before we do anything */
-  if (punit->hp < unit_type(punit)->hp 
-      && (pcity = map_get_city(punit->x, punit->y))) {
-    UNIT_LOG(LOGLEVEL_FERRY, punit, "waiting in %s to recover hitpoints", 
-             pcity->name);
-    return;
-  }
-
-  /* Check if we are an empty barbarian boat and so not needed */
-  if (is_barbarian(pplayer) && !punit->occupy) {
-    wipe_unit(punit);
-    return;
-  }
-
-  do {
-    /* Do we have the passenger-in-charge on board? */
-    struct tile *ptile = map_get_tile(punit->x, punit->y);
-
-    if (punit->ai.passenger > 0) {
-      struct unit *psngr = find_unit_by_id(punit->ai.passenger);
-      
-      /* If the passenger-in-charge is adjacent, we should wait for it to 
-       * board.  We will pass control to it later.
-       * FIXME: A possible side-effect: a boat will linger near a passenger 
-       * which already landed. */
-      if (!psngr 
-         || real_map_distance(punit->x, punit->y, psngr->x, psngr->y) > 1) {
-       UNIT_LOG(LOGLEVEL_FERRY, punit, 
-                "recorded passenger[%d] is not on board, checking for others",
-                punit->ai.passenger);
-       punit->ai.passenger = 0;
-      }
-    }
-
-    if (punit->ai.passenger <= 0) {
-      struct unit *candidate = NULL;
-    
-      /* Try to select passanger-in-charge from among our passengers */
-      unit_list_iterate(ptile->units, aunit) {
-        if (aunit->ai.ferryboat != punit->id 
-            && aunit->ai.ferryboat != FERRY_WANTED) {
-          continue;
-        }
-      
-        if (aunit->ai.ai_role != AIUNIT_ESCORT) {
-          candidate = aunit;
-          break;
-        } else {
-          /* Bodyguards shouldn't be in charge of boats so continue looking */
-          candidate = aunit;
-        }
-      } unit_list_iterate_end;
-      
-      if (candidate) {
-        UNIT_LOG(LOGLEVEL_FERRY, punit, 
-                 "appointed %s[%d] our passenger-in-charge",
-                 unit_type(candidate)->name, candidate->id);
-        if (candidate->ai.ferryboat == FERRY_WANTED) {
-          ai_set_ferry(candidate, punit);
-        }
-        ai_set_passenger(punit, candidate);
-      }
-    }
-
-    if (punit->ai.passenger > 0) {
-      int bossid = punit->ai.passenger;    /* Loop prevention */
-      struct unit *boss = find_unit_by_id(punit->ai.passenger);
-      int id = punit->id;                  /* To check if survived */
-
-      assert(boss != NULL);
-
-      if (unit_flag(boss, F_SETTLERS) || unit_flag(boss, F_CITIES)) {
-        /* Temporary hack: settlers all go in the end, forcing them 
-         * earlier might mean uninitialised cache, so just wait for them */
-        return;
-      }
-
-      UNIT_LOG(LOGLEVEL_FERRY, punit, "passing control to %s[%d]",
-               unit_type(boss)->name, boss->id);
-      ai_manage_unit(pplayer, boss);
-    
-      if (!find_unit_by_id(id) || punit->moves_left <= 0) {
-        return;
-      }
-      if (find_unit_by_id(bossid) 
-         && same_pos(punit->x, punit->y, boss->x, boss->y)) {
-       /* The boss decided to stay put on the ferry. We aren't moving. */
-       return;
-      }
-    } else {
-      /* Cannot select a passenger-in-charge */
-      break;
-    }
-  } while (punit->occupy != 0);
-
-  /* Not carrying anyone, even the ferryman */
-
-  if (IS_ATTACKER(punit) && punit->moves_left > 0) {
-     /* AI used to build frigates to attack and then use them as ferries 
-      * -- Syela */
-     UNIT_LOG(LOGLEVEL_FERRY, punit, "passing ferry over to attack code");
-     ai_manage_military(pplayer, punit);
-     return;
-  }
-
-  UNIT_LOG(LOGLEVEL_FERRY, punit, "Ferryboat is not carrying anyone.");
-  ai_set_passenger(punit, NULL);
-  handle_unit_activity_request(punit, ACTIVITY_IDLE);
-  ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
-  CHECK_UNIT(punit);
-
-  /* Try to find passengers */
-  if (ai_ferry_findcargo(punit)) {
-    UNIT_LOG(LOGLEVEL_FERRY, punit, "picking up cargo");
-    ai_unit_goto(punit, goto_dest_x(punit), goto_dest_y(punit));
-    return;
-  }
-
-  /* Try to find a city that needs a ferry */
-  if (ai_ferry_find_interested_city(punit)) {
-    if (same_pos(punit->x, punit->y, goto_dest_x(punit), goto_dest_y(punit))) {
-      UNIT_LOG(LOGLEVEL_FERRY, punit, "staying in city that needs us");
-      return;
-    } else {
-      UNIT_LOG(LOGLEVEL_FERRY, punit, "going to city that needs us");
-      (void) ai_unit_goto(punit, goto_dest_x(punit), goto_dest_y(punit));
-      return;
-    }
-  }
-
-  UNIT_LOG(LOGLEVEL_FERRY, punit, "Passing control of ferry to explorer code");
-  (void) ai_manage_explorer(punit);
-
-  if (punit->moves_left > 0) {
-    struct city *pcity = find_nearest_safe_city(punit);
-    if (pcity) {
-      set_goto_dest(punit, pcity->x, pcity->y);
-      UNIT_LOG(LOGLEVEL_FERRY, punit, "No work, going home");
-      (void) ai_unit_goto(punit, pcity->x, pcity->y);
-    }
-  }
- 
-  return;
-}
-
 /*************************************************************************
  This function goes wait a unit in a city for the hitpoints to recover. 
  If something is attacking our city, kill it yeahhh!!!.
@@ -2631,7 +2316,7 @@
   Decide what to do with a military unit. It will be managed once only.
   It is up to the caller to try again if it has moves left.
 **************************************************************************/
-static void ai_manage_military(struct player *pplayer, struct unit *punit)
+void ai_manage_military(struct player *pplayer, struct unit *punit)
 {
   int id = punit->id;
 
@@ -2653,7 +2338,7 @@
 
   /* Since military units re-evaluate their actions every turn,
      we must make sure that previously reserved ferry is freed. */
-  ai_clear_ferry(punit);
+  aiferry_clear_boat(punit);
 
   ai_military_findjob(pplayer, punit);
 
@@ -2737,7 +2422,7 @@
  several flags the first one in order of appearance in this function
  will be used.
 **************************************************************************/
-static void ai_manage_unit(struct player *pplayer, struct unit *punit)
+void ai_manage_unit(struct player *pplayer, struct unit *punit)
 {
   struct unit *bodyguard = find_unit_by_id(punit->ai.bodyguard);
 
Index: ai/aiunit.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.h,v
retrieving revision 1.51
diff -u -r1.51 aiunit.h
--- ai/aiunit.h 29 Apr 2004 19:59:21 -0000      1.51
+++ ai/aiunit.h 16 Jun 2004 16:23:53 -0000
@@ -50,6 +50,9 @@
 extern Unit_Type_id simple_ai_types[U_LAST];
 
 void ai_manage_units(struct player *pplayer); 
+void ai_manage_unit(struct player *pplayer, struct unit *punit);
+void ai_manage_military(struct player *pplayer,struct unit *punit);
+struct city *find_nearest_safe_city(struct unit *punit);
 int could_unit_move_to_tile(struct unit *punit, int dest_x, int dest_y);
 int look_for_charge(struct player *pplayer, struct unit *punit,
                     struct unit **aunit, struct city **acity);

[Prev in Thread] Current Thread [Next in Thread]