Feature #1487 ยป 0089-daiferry.-ch-Improve-coding-style.patch
| ai/default/daiferry.c | ||
|---|---|---|
|
#include "daiferry.h"
|
||
|
|
||
|
/* =================== constants with special meaning =================== */
|
||
|
/*
|
||
|
* This one is used only by ferryboats in ai.passenger field
|
||
|
/*
|
||
|
* This one is used only by ferryboats in ai.passenger field
|
||
|
*/
|
||
|
#define FERRY_AVAILABLE (-1) /* Boat is looking for a passenger */
|
||
|
#define FERRY_ABANDON_BOSS (-2) /* Passenger is assigned for boat, but boat
|
||
|
* might take another passenger. Probably
|
||
|
* passenger already left the boat*/
|
||
|
/*
|
||
|
* The below is used only by passengers in ai.ferryboat field
|
||
|
*/
|
||
|
/*
|
||
|
* The below is used only by passengers in ai.ferryboat field
|
||
|
*/
|
||
|
#define FERRY_WANTED (-1) /* Needs a boat */
|
||
|
#define FERRY_NONE 0 /* Has no boat and doesn't need one */
|
||
|
/* =================== group log levels, debug options ================= */
|
||
|
/* =================== Group log levels, debug options ================= */
|
||
|
/* Logging in ferry management functions */
|
||
|
#define LOGLEVEL_FERRY LOG_DEBUG
|
||
| ... | ... | |
|
#endif
|
||
|
/* ========= managing statistics and boat/passenger assignments ======== */
|
||
|
/* ========= Managing statistics and boat/passenger assignments ======== */
|
||
|
/**********************************************************************//**
|
||
|
Call to initialize the ferryboat statistics
|
||
| ... | ... | |
|
ai->stats.passengers = 0;
|
||
|
ai->stats.boats = 0;
|
||
|
ai->stats.available_boats = 0;
|
||
|
|
||
|
unit_list_iterate(pplayer->units, punit) {
|
||
|
struct unit_ai *unit_data = def_ai_unit_data(punit, ait);
|
||
| ... | ... | |
|
#ifdef LOGLEVEL_FERRY_STATS
|
||
|
static void aiferry_print_stats(struct ai_type *ait, struct player *pplayer)
|
||
|
{
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, pplayer, NULL);
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, pplayer, nullptr);
|
||
|
int n = 1;
|
||
|
log_base(LOGLEVEL_FERRY_STATS, "Boat stats for %s[%d]",
|
||
| ... | ... | |
|
bool new_f = dai_is_ferry(ferry, ait);
|
||
|
if (old_f && !new_f) {
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, unit_owner(ferry), NULL);
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, unit_owner(ferry), nullptr);
|
||
|
struct unit_ai *unit_data = def_ai_unit_data(ferry, ait);
|
||
|
ai->stats.boats--;
|
||
| ... | ... | |
|
} else if (unit_data->passenger > 0) {
|
||
|
struct unit *passenger = game_unit_by_number(unit_data->passenger);
|
||
|
if (passenger != NULL) {
|
||
|
if (passenger != nullptr) {
|
||
|
aiferry_clear_boat(ait, passenger);
|
||
|
}
|
||
|
}
|
||
|
} else if (!old_f && new_f) {
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, unit_owner(ferry), NULL);
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, unit_owner(ferry), nullptr);
|
||
|
ai->stats.boats++;
|
||
|
ai->stats.available_boats++;
|
||
| ... | ... | |
|
struct player *pplayer = unit_owner(punit);
|
||
|
if (is_ai_data_phase_open(ait, pplayer)) {
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, pplayer, NULL);
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, pplayer, nullptr);
|
||
|
ai->stats.passengers--;
|
||
|
}
|
||
| ... | ... | |
|
struct player *pplayer = unit_owner(ferry);
|
||
|
if (is_ai_data_phase_open(ait, pplayer)) {
|
||
|
dai_plr_data_get(ait, pplayer, NULL)->stats.available_boats++;
|
||
|
dai_plr_data_get(ait, pplayer, nullptr)->stats.available_boats++;
|
||
|
}
|
||
|
ferry_data->passenger = FERRY_AVAILABLE;
|
||
|
}
|
||
| ... | ... | |
|
}
|
||
|
/**********************************************************************//**
|
||
|
Request a boat for the unit. Should only be used if the unit is on the
|
||
|
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 ai_type *ait, struct unit *punit)
|
||
|
{
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, unit_owner(punit), NULL);
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, unit_owner(punit), nullptr);
|
||
|
struct unit_ai *unit_data = def_ai_unit_data(punit, ait);
|
||
|
/* First clear the previous assignments (just in case there are).
|
||
|
/* First clear the previous assignments (just in case there are).
|
||
|
* Subtract virtual units or already counted */
|
||
|
if ((punit->id == 0) ||
|
||
|
((ai->stats.passengers > 0) &&
|
||
|
if ((punit->id == 0) ||
|
||
|
((ai->stats.passengers > 0) &&
|
||
|
(unit_data->ferryboat == FERRY_WANTED))) {
|
||
|
aiferry_clear_boat(ait, punit);
|
||
|
}
|
||
| ... | ... | |
|
fc_assert_ret(unit_owner(punit) == ferry_owner);
|
||
|
/* First delete the unit from the list of passengers and
|
||
|
/* First delete the unit from the list of passengers and
|
||
|
* release its previous ferry */
|
||
|
aiferry_clear_boat(ait, punit);
|
||
|
/* If ferry was available, update the stats */
|
||
|
if (ferry_data->passenger == FERRY_AVAILABLE) {
|
||
|
dai_plr_data_get(ait, ferry_owner, NULL)->stats.available_boats--;
|
||
|
dai_plr_data_get(ait, ferry_owner, nullptr)->stats.available_boats--;
|
||
|
}
|
||
|
/* Exchange the phone numbers */
|
||
| ... | ... | |
|
struct unit_ai *ferry_data = def_ai_unit_data(pferry, ait);
|
||
|
if (ferry_data->passenger != FERRY_AVAILABLE) {
|
||
|
dai_plr_data_get(ait, unit_owner(pferry), NULL)->stats.available_boats++;
|
||
|
dai_plr_data_get(ait, unit_owner(pferry), nullptr)->stats.available_boats++;
|
||
|
ferry_data->passenger = FERRY_AVAILABLE;
|
||
|
}
|
||
|
}
|
||
|
/**********************************************************************//**
|
||
|
Returns the number of available boats. A simple accessor made to perform
|
||
|
Returns the number of available boats. A simple accessor made to perform
|
||
|
debug checks.
|
||
|
**************************************************************************/
|
||
|
int aiferry_avail_boats(struct ai_type *ait, struct player *pplayer)
|
||
|
{
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, pplayer, NULL);
|
||
|
struct ai_plr *ai = dai_plr_data_get(ait, pplayer, nullptr);
|
||
|
/* To developer: Switch this checking on when testing some new
|
||
|
/* To developer: Switch this checking on when testing some new
|
||
|
* ferry code. */
|
||
|
#ifdef LOGLEVEL_FERRY_STATS
|
||
|
int boats = 0;
|
||
| ... | ... | |
|
/* ================== functions to find a boat ========================= */
|
||
|
/**********************************************************************//**
|
||
|
Combined cost function for a land unit looking for a ferry. The path
|
||
|
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
|
||
|
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
|
||
|
call-back. Doesn't care for enemy/neutral tiles, these should be
|
||
|
excluded using a TB call-back.
|
||
|
**************************************************************************/
|
||
|
static unsigned combined_land_sea_move(const struct tile *src_tile,
|
||
| ... | ... | |
|
}
|
||
|
/**********************************************************************//**
|
||
|
EC callback to account for the cost of sea moves by a ferry hurrying to
|
||
|
EC callback to account for the cost of sea moves by a ferry hurrying to
|
||
|
pick our unit up.
|
||
|
**************************************************************************/
|
||
|
static unsigned sea_move(const struct tile *ptile, enum known_type known,
|
||
|
const struct pf_parameter *param)
|
||
|
{
|
||
|
if (is_ocean_tile(ptile)) {
|
||
|
/* Approximately TURN_FACTOR / average ferry move rate
|
||
|
/* 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;
|
||
| ... | ... | |
|
}
|
||
|
/**********************************************************************//**
|
||
|
Proper and real PF function for finding a boat. If you don't require
|
||
|
the path to the ferry, pass path=NULL.
|
||
|
Proper and real PF function for finding a boat. If you don't require
|
||
|
the path to the ferry, pass path=nullptr.
|
||
|
Return the unit ID of the boat; punit is the passenger.
|
||
|
WARNING: Due to the nature of this function and PF (see the comment of
|
||
| ... | ... | |
|
/* currently assigned ferry */
|
||
|
int ferryboat = def_ai_unit_data(punit, ait)->ferryboat;
|
||
|
/* We may end calling pf_destroy_path for *path if it's not NULL.
|
||
|
/* We may end calling pf_destroy_path for *path if it's not nullptr.
|
||
|
* Most likely you are passing garbage or path you don't want
|
||
|
* destroyed if this assertion fails.
|
||
|
* Don't try to be clever and pass 'fallback' path that will be returned
|
||
|
* if no path is found. Instead check for NULL return value and then
|
||
|
* if no path is found. Instead check for nullptr return value and then
|
||
|
* use fallback path in calling function. */
|
||
|
fc_assert_ret_val(path == NULL || *path == NULL, 0);
|
||
|
fc_assert_ret_val(path == nullptr || *path == nullptr, 0);
|
||
|
fc_assert_ret_val(0 < ferryboat
|
||
|
|| FERRY_NONE == ferryboat
|
||
|
|| FERRY_WANTED == ferryboat, 0);
|
||
|
UNIT_LOG(LOGLEVEL_FINDFERRY, punit, "asked aiferry_find_boat for a boat");
|
||
|
if (aiferry_avail_boats(ait, pplayer) <= 0
|
||
|
if (aiferry_avail_boats(ait, pplayer) <= 0
|
||
|
&& ferryboat <= 0) {
|
||
|
/* No boats to be found (the second check is to ensure that we are not
|
||
|
/* No boats to be found (the second check is to ensure that we are not
|
||
|
* the ones keeping the last boat busy) */
|
||
|
return 0;
|
||
|
}
|
||
| ... | ... | |
|
/* FIXME: This condition is somewhat dodgy */
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
square_iterate(&(wld.map), pos.tile, radius, ptile) {
|
||
|
unit_list_iterate(ptile->units, aunit) {
|
||
|
if (is_boat_free(ait, aunit, punit, cap)) {
|
||
|
/* 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)
|
||
|
int f_turns = ((pos.total_EC / PF_TURN_FACTOR * 16
|
||
|
- aunit->moves_left)
|
||
|
/ unit_type_get(aunit)->move_rate);
|
||
|
int turns = MAX(u_turns, f_turns);
|
||
|
if (turns < best_turns) {
|
||
|
UNIT_LOG(LOGLEVEL_FINDFERRY, punit,
|
||
|
UNIT_LOG(LOGLEVEL_FINDFERRY, punit,
|
||
|
"Found a potential boat %s[%d](%d,%d)(moves left: %d)",
|
||
|
unit_rule_name(aunit), aunit->id,
|
||
|
TILE_XY(unit_tile(aunit)), aunit->moves_left);
|
||
| ... | ... | |
|
if (*path) {
|
||
|
pf_path_destroy(*path);
|
||
|
}
|
||
|
*path = pf_map_iter_path(search_map);
|
||
|
}
|
||
|
*path = pf_map_iter_path(search_map);
|
||
|
}
|
||
|
best_turns = turns;
|
||
|
best_id = aunit->id;
|
||
|
}
|
||
| ... | ... | |
|
static void dai_activate_passengers(struct ai_type *ait, struct unit *ferry)
|
||
|
{
|
||
|
struct player *ferry_owner = unit_owner(ferry);
|
||
|
|
||
|
unit_list_iterate_safe(unit_tile(ferry)->units, aunit) {
|
||
|
if (unit_transport_get(aunit) == ferry) {
|
||
|
unit_activity_handling(aunit, ACTIVITY_IDLE, ACTION_NONE);
|
||
| ... | ... | |
|
if (same_pos(unit_tile(passenger), ptile)) {
|
||
|
/* Not an error; sometimes immediate_destination instructs the unit
|
||
|
* to stay here. For example, to refuel.*/
|
||
|
send_unit_info(NULL, passenger);
|
||
|
send_unit_info(nullptr, passenger);
|
||
|
return TRUE;
|
||
|
} else if (passenger->moves_left == 0 && ferry->moves_left == 0) {
|
||
|
send_unit_info(NULL, passenger);
|
||
|
send_unit_info(nullptr, passenger);
|
||
|
return TRUE;
|
||
|
}
|
||
| ... | ... | |
|
if (!is_ocean_tile(next_tile)) {
|
||
|
int ferry_id = ferry->id;
|
||
|
UNIT_LOG(LOG_DEBUG, passenger, "Our boat has arrived "
|
||
|
"[%d](moves left: %d)", ferry->id, ferry->moves_left);
|
||
|
UNIT_LOG(LOG_DEBUG, passenger, "Disembarking to (%d,%d)",
|
||
|
TILE_XY(next_tile));
|
||
|
/* Land leg */
|
||
|
UNIT_LOG(LOG_DEBUG, passenger, "Our boat has arrived "
|
||
|
"[%d](moves left: %d)", ferry->id, ferry->moves_left);
|
||
|
UNIT_LOG(LOG_DEBUG, passenger, "Disembarking to (%d,%d)",
|
||
|
TILE_XY(next_tile));
|
||
|
/* Land leg */
|
||
|
alive = adv_follow_path(passenger, path, ptile);
|
||
|
/* Movement of the passenger outside the ferry can cause also
|
||
|
* ferry to die. That has happened at least when passenger
|
||
|
* destroyed city cutting the civ1-style channel (cities in
|
||
|
* a chain) ferry was in. */
|
||
|
if (unit_is_alive(ferry_id) && 0 < ferry->moves_left
|
||
|
if (unit_is_alive(ferry_id) && 0 < ferry->moves_left
|
||
|
&& (!alive || unit_tile(ferry) != unit_tile(passenger))) {
|
||
|
/* The passenger is no longer on the ferry,
|
||
|
* and the ferry can still act.
|
||
|
* Give a chance for another passenger to take command
|
||
|
* of the ferry.
|
||
|
*/
|
||
|
UNIT_LOG(LOG_DEBUG, ferry, "Activating passengers");
|
||
|
/* The passenger is no longer on the ferry,
|
||
|
* and the ferry can still act.
|
||
|
* Give a chance for another passenger to take command
|
||
|
* of the ferry.
|
||
|
*/
|
||
|
UNIT_LOG(LOG_DEBUG, ferry, "Activating passengers");
|
||
|
dai_activate_passengers(ait, ferry);
|
||
|
/* It is theoretically possible passenger died here due to
|
||
| ... | ... | |
|
Return FALSE iff we died.
|
||
|
**************************************************************************/
|
||
|
bool aiferry_goto_amphibious(struct ai_type *ait, struct unit *ferry,
|
||
|
struct unit *passenger, struct tile *ptile)
|
||
|
struct unit *passenger, struct tile *ptile)
|
||
|
{
|
||
|
struct pft_amphibious parameter;
|
||
|
struct adv_risk_cost land_risk_cost;
|
||
| ... | ... | |
|
/* Move as far along the path to the destination as we can;
|
||
|
* that is, ignore the presence of enemy units when computing the
|
||
|
* path */
|
||
|
parameter.combined.get_zoc = NULL;
|
||
|
parameter.combined.get_zoc = nullptr;
|
||
|
return dai_amphibious_goto_constrained(ait, ferry, passenger, ptile,
|
||
|
¶meter);
|
||
|
}
|
||
|
/**********************************************************************//**
|
||
|
This function is to be called if punit needs to use a boat to get to the
|
||
|
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.
|
||
|
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
|
||
|
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.
|
||
|
**************************************************************************/
|
||
|
bool aiferry_gobyboat(struct ai_type *ait, struct player *pplayer,
|
||
| ... | ... | |
|
if (!unit_transported(punit)) {
|
||
|
/* We are not on a boat and we cannot walk */
|
||
|
int boatid;
|
||
|
struct unit *ferryboat = NULL;
|
||
|
struct unit *ferryboat = nullptr;
|
||
|
int cap = with_bodyguard ? 2 : 1;
|
||
|
bool board_success = FALSE;
|
||
| ... | ... | |
|
TILE_XY(dest_tile));
|
||
|
if (!is_terrain_class_near_tile(nmap, unit_tile(punit), TC_OCEAN)) {
|
||
|
struct pf_path *path_to_ferry = NULL;
|
||
|
struct pf_path *path_to_ferry = nullptr;
|
||
|
boatid = aiferry_find_boat(ait, punit, cap, &path_to_ferry);
|
||
|
if (boatid <= 0) {
|
||
| ... | ... | |
|
}
|
||
|
ferryboat = game_unit_by_number(boatid);
|
||
|
UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "found boat[%d](%d,%d), going there",
|
||
|
boatid, TILE_XY(unit_tile(ferryboat)));
|
||
|
/* The path can be amphibious so we will stop at the coast.
|
||
|
UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "found boat[%d](%d,%d), going there",
|
||
|
boatid, TILE_XY(unit_tile(ferryboat)));
|
||
|
/* The path can be amphibious so we will stop at the coast.
|
||
|
* It might not lead _onto_ the boat. */
|
||
|
if (!adv_unit_execute_path(punit, path_to_ferry)) {
|
||
|
if (!adv_unit_execute_path(punit, path_to_ferry)) {
|
||
|
/* Died. */
|
||
|
pf_path_destroy(path_to_ferry);
|
||
|
pf_path_destroy(path_to_ferry);
|
||
|
return FALSE;
|
||
|
}
|
||
|
pf_path_destroy(path_to_ferry);
|
||
| ... | ... | |
|
/* Ok, a boat found, try boarding it */
|
||
|
ferryboat = game_unit_by_number(boatid);
|
||
|
UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "found a nearby boat[%d](%d,%d)",
|
||
|
ferryboat->id, TILE_XY(unit_tile(ferryboat)));
|
||
|
/* Setting ferry now in hope it won't run away even
|
||
|
ferryboat->id, TILE_XY(unit_tile(ferryboat)));
|
||
|
/* Setting ferry now in hope it won't run away even
|
||
|
* if we can't board it right now */
|
||
|
aiferry_psngr_meet_boat(ait, punit, ferryboat);
|
||
| ... | ... | |
|
/* FIXME: this is probably a serious bug, but we just skip past
|
||
|
* it and continue. */
|
||
|
UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "couldn't board boat[%d](%d,%d)",
|
||
|
ferryboat->id, TILE_XY(unit_tile(ferryboat)));
|
||
|
ferryboat->id, TILE_XY(unit_tile(ferryboat)));
|
||
|
return FALSE;
|
||
|
}
|
||
| ... | ... | |
|
* TODO: Try to predict this failure so that the units wouldn't
|
||
|
* waste turns to travel to the rendezvous point. */
|
||
|
UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "boarding boat[%d](%d,%d) not enabled",
|
||
|
ferryboat->id, TILE_XY(unit_tile(ferryboat)));
|
||
|
ferryboat->id, TILE_XY(unit_tile(ferryboat)));
|
||
|
return FALSE;
|
||
|
}
|
||
| ... | ... | |
|
if (is_boat_free(ait, ferryboat, punit, 0)) {
|
||
|
struct unit *bodyguard = aiguard_guard_of(ait, punit);
|
||
|
UNIT_LOG(LOGLEVEL_GOBYBOAT, punit,
|
||
|
"got boat[%d](moves left: %d), going (%d,%d)",
|
||
|
UNIT_LOG(LOGLEVEL_GOBYBOAT, punit,
|
||
|
"got boat[%d](moves left: %d), going (%d,%d)",
|
||
|
ferryboat->id, ferryboat->moves_left, TILE_XY(dest_tile));
|
||
|
aiferry_psngr_meet_boat(ait, punit, ferryboat);
|
||
| ... | ... | |
|
|| !dai_unit_goto(ait, bodyguard, unit_tile(punit))) {
|
||
|
/* Bodyguard can't get there or died en route */
|
||
|
aiguard_request_guard(ait, punit);
|
||
|
bodyguard = NULL;
|
||
|
bodyguard = nullptr;
|
||
|
} else if (bodyguard->moves_left <= 0) {
|
||
|
/* Wait for me, I'm cooooming!! */
|
||
|
UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "waiting for bodyguard");
|
||
| ... | ... | |
|
/* Crap bodyguard. Got stuck somewhere. Ditch it! */
|
||
|
UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "ditching useless bodyguard");
|
||
|
aiguard_request_guard(ait, punit);
|
||
|
dai_unit_new_task(ait, bodyguard, AIUNIT_NONE, NULL);
|
||
|
bodyguard = NULL;
|
||
|
dai_unit_new_task(ait, bodyguard, AIUNIT_NONE, nullptr);
|
||
|
bodyguard = nullptr;
|
||
|
}
|
||
|
}
|
||
|
if (bodyguard) {
|
||
| ... | ... | |
|
} else {
|
||
|
/* We are in still transit */
|
||
|
def_ai_unit_data(punit, ait)->done = TRUE;
|
||
|
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]",
|
||
|
" its boss is [%d]",
|
||
|
ferryboat->id, def_ai_unit_data(ferryboat, ait)->passenger);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
| ... | ... | |
|
struct pf_map *pfm;
|
||
|
struct pf_parameter parameter;
|
||
|
int passengers = dai_plr_data_get(ait, unit_owner(pferry),
|
||
|
NULL)->stats.passengers;
|
||
|
nullptr)->stats.passengers;
|
||
|
struct player *pplayer;
|
||
|
const struct civ_map *nmap = &(wld.map);
|
||
| ... | ... | |
|
unit_list_iterate(ptile->units, aunit) {
|
||
|
struct unit_ai *unit_data = def_ai_unit_data(aunit, ait);
|
||
|
if (unit_owner(pferry) == unit_owner(aunit)
|
||
|
if (unit_owner(pferry) == unit_owner(aunit)
|
||
|
&& (unit_data->ferryboat == FERRY_WANTED
|
||
|
|| unit_data->ferryboat == pferry->id)) {
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, pferry,
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, pferry,
|
||
|
"Found a potential cargo %s[%d](%d,%d), going there",
|
||
|
unit_rule_name(aunit),
|
||
|
aunit->id,
|
||
|
TILE_XY(unit_tile(aunit)));
|
||
|
pferry->goto_tile = unit_tile(aunit);
|
||
|
pferry->goto_tile = unit_tile(aunit);
|
||
|
/* Exchange phone numbers */
|
||
|
aiferry_psngr_meet_boat(ait, aunit, pferry);
|
||
|
pf_map_destroy(pfm);
|
||
|
return TRUE;
|
||
|
}
|
||
|
} unit_list_iterate_end;
|
||
| ... | ... | |
|
UNIT_LOG(LOGLEVEL_FERRY, pferry,
|
||
|
"AI Passengers counting reported false positive %d", passengers);
|
||
|
pf_map_destroy(pfm);
|
||
|
return FALSE;
|
||
|
}
|
||
|
/**********************************************************************//**
|
||
|
A helper for ai_manage_ferryboat. Finds a city that wants a ferry. It
|
||
|
might signal for the ferry using pcity->server.ai.choice.need_boat field or
|
||
|
it might simply be building a ferry of it's own.
|
||
|
A helper for ai_manage_ferryboat. Finds a city that wants a ferry.
|
||
|
It might signal for the ferry using pcity->server.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").
|
||
|
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 are passengers that can not be reached ("false positive").
|
||
|
**************************************************************************/
|
||
|
static bool aiferry_find_interested_city(struct ai_type *ait,
|
||
|
struct unit *pferry)
|
||
| ... | ... | |
|
}
|
||
|
pcity = tile_city(pos.tile);
|
||
|
|
||
|
if (pcity && city_owner(pcity) == unit_owner(pferry)
|
||
|
&& (def_ai_city_data(pcity, ait)->choice.need_boat
|
||
|
&& (def_ai_city_data(pcity, ait)->choice.need_boat
|
||
|
|| (VUT_UTYPE == pcity->production.kind
|
||
|
&& utype_has_role(pcity->production.value.utype,
|
||
|
L_FERRYBOAT)))) {
|
||
|
&& utype_has_role(pcity->production.value.utype,
|
||
|
L_FERRYBOAT)))) {
|
||
|
bool really_needed = TRUE;
|
||
|
int turns = city_production_turns_to_build(pcity, TRUE);
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s (%d, %d) looks promising...",
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s (%d, %d) looks promising...",
|
||
|
city_name_get(pcity), TILE_XY(pcity->tile));
|
||
|
if (pos.turn > turns
|
||
| ... | ... | |
|
}
|
||
|
unit_list_iterate(pos.tile->units, aunit) {
|
||
|
if (aunit != pferry && unit_owner(aunit) == unit_owner(pferry)
|
||
|
if (aunit != pferry && unit_owner(aunit) == unit_owner(pferry)
|
||
|
&& unit_has_type_role(aunit, L_FERRYBOAT)) {
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, pferry, "%s is NOT suitable: "
|
||
|
"has another ferry",
|
||
|
city_name_get(pcity));
|
||
|
really_needed = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
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",
|
||
|
city_name_get(pcity));
|
||
|
pferry->goto_tile = pos.tile;
|
||
|
pferry->goto_tile = pos.tile;
|
||
|
turns_horizon = turns;
|
||
|
needed = TRUE;
|
||
|
}
|
||
| ... | ... | |
|
} pf_map_positions_iterate_end;
|
||
|
pf_map_destroy(pfm);
|
||
|
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.
|
||
|
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,
|
||
|
If there is no one aboard, look for potential cargo. If none found,
|
||
|
explore and then go to the nearest port.
|
||
|
**************************************************************************/
|
||
|
void dai_manage_ferryboat(struct ai_type *ait, struct player *pplayer,
|
||
| ... | ... | |
|
/* Try to recover hitpoints if we are in a city, before we do anything */
|
||
|
if (punit->hp < unit_type_get(punit)->hp
|
||
|
&& (pcity = tile_city(unit_tile(punit)))) {
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit, "waiting in %s to recover hitpoints",
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit, "waiting in %s to recover hitpoints",
|
||
|
city_name_get(pcity));
|
||
|
def_ai_unit_data(punit, ait)->done = TRUE;
|
||
|
return;
|
||
| ... | ... | |
|
/* Check if we are an empty barbarian boat and so not needed */
|
||
|
if (is_barbarian(pplayer) && get_transporter_occupancy(punit) == 0) {
|
||
|
wipe_unit(punit, ULR_RETIRED, NULL);
|
||
|
wipe_unit(punit, ULR_RETIRED, nullptr);
|
||
|
return;
|
||
|
}
|
||
| ... | ... | |
|
if (unit_data->passenger > 0) {
|
||
|
struct unit *psngr = game_unit_by_number(unit_data->passenger);
|
||
|
/* If the passenger-in-charge is adjacent, we should wait for it to
|
||
|
/* If the passenger-in-charge is adjacent, we should wait for it to
|
||
|
* board. We will pass control to it later. */
|
||
|
if (!psngr
|
||
|
|| real_map_distance(unit_tile(punit), unit_tile(psngr)) > 1) {
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit,
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit,
|
||
|
"recorded passenger[%d] is not on board, checking for "
|
||
|
"others", unit_data->passenger);
|
||
|
unit_data->passenger = FERRY_ABANDON_BOSS;
|
||
| ... | ... | |
|
if (unit_data->passenger == FERRY_AVAILABLE
|
||
|
|| unit_data->passenger == FERRY_ABANDON_BOSS) {
|
||
|
struct unit *candidate = NULL;
|
||
|
struct unit *candidate = nullptr;
|
||
|
/* Try to select passenger-in-charge from among our passengers */
|
||
|
unit_list_iterate(punit->transporting, aunit) {
|
||
| ... | ... | |
|
candidate = aunit;
|
||
|
}
|
||
|
} unit_list_iterate_end;
|
||
|
|
||
|
if (candidate) {
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit,
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit,
|
||
|
"appointed %s[%d] our passenger-in-charge",
|
||
|
unit_rule_name(candidate),
|
||
|
candidate->id);
|
||
| ... | ... | |
|
if (unit_data->passenger > 0) {
|
||
|
struct unit *boss = game_unit_by_number(bossid);
|
||
|
fc_assert_ret(NULL != boss);
|
||
|
fc_assert_ret(boss != nullptr);
|
||
|
if (unit_has_type_flag(boss, UTYF_WORKERS)
|
||
|
|| unit_is_cityfounder(boss)) {
|
||
| ... | ... | |
|
}
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit, "passing control to %s[%d]",
|
||
|
unit_rule_name(boss),
|
||
|
boss->id);
|
||
|
unit_rule_name(boss),
|
||
|
boss->id);
|
||
|
dai_manage_unit(ait, pplayer, boss);
|
||
|
|
||
|
if (!game_unit_by_number(sanity) || punit->moves_left <= 0) {
|
||
|
return;
|
||
|
}
|
||
|
if (game_unit_by_number(bossid)) {
|
||
|
if (same_pos(unit_tile(punit), unit_tile(boss))) {
|
||
|
/* The boss decided to stay put on the ferry. We aren't moving. */
|
||
|
if (same_pos(unit_tile(punit), unit_tile(boss))) {
|
||
|
/* The boss decided to stay put on the ferry. We aren't moving. */
|
||
|
UNIT_LOG(LOG_DEBUG, boss, "drove ferry - done for now");
|
||
|
def_ai_unit_data(boss, ait)->done = TRUE;
|
||
|
return;
|
||
| ... | ... | |
|
ptype = unit_type_get(punit);
|
||
|
if (IS_ATTACKER(ptype) && punit->moves_left > 0) {
|
||
|
/* AI used to build frigates to attack and then use them as ferries
|
||
|
/* AI used to build frigates to attack and then use them as ferries
|
||
|
* -- Syela */
|
||
|
dai_unit_new_task(ait, punit, AIUNIT_NONE, NULL);
|
||
|
dai_unit_new_task(ait, punit, AIUNIT_NONE, nullptr);
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit, "passing ferry over to attack code");
|
||
|
dai_manage_military(ait, nmap, pplayer, punit);
|
||
|
return;
|
||
|
}
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit, "Ferryboat is not carrying anyone "
|
||
|
"(moves left: %d).", punit->moves_left);
|
||
|
"(moves left: %d).", punit->moves_left);
|
||
|
aiferry_make_available(ait, punit);
|
||
|
unit_activity_handling(punit, ACTIVITY_IDLE, ACTION_NONE);
|
||
|
dai_unit_new_task(ait, punit, AIUNIT_NONE, NULL);
|
||
|
dai_unit_new_task(ait, punit, AIUNIT_NONE, nullptr);
|
||
|
CHECK_UNIT(punit);
|
||
|
/* Try to find passengers */
|
||
|
if (aiferry_findcargo(ait, punit)) {
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit, "picking up cargo (moves left: %d)",
|
||
|
punit->moves_left);
|
||
|
punit->moves_left);
|
||
|
if (dai_unit_goto(ait, punit, punit->goto_tile)) {
|
||
|
if (is_tiles_adjacent(unit_tile(punit), punit->goto_tile)
|
||
|
|| same_pos(unit_tile(punit), punit->goto_tile)) {
|
||
| ... | ... | |
|
dai_manage_unit(ait, pplayer, cargo);
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
| ... | ... | |
|
if (same_pos(unit_tile(punit), punit->goto_tile)) {
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit, "staying in city that needs us");
|
||
|
unit_data->done = TRUE;
|
||
|
return;
|
||
|
} else {
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit, "going to city that needs us");
|
||
|
if (dai_unit_goto(ait, punit, punit->goto_tile)
|
||
|
&& same_pos(unit_tile(punit), punit->goto_tile)) {
|
||
|
unit_data->done = TRUE; /* save some CPU */
|
||
|
unit_data->done = TRUE; /* Save some CPU */
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
| ... | ... | |
|
UNIT_LOG(LOGLEVEL_FERRY, punit, "Passing control of ferry to explorer code");
|
||
|
switch (manage_auto_explorer(punit)) {
|
||
|
case MR_DEATH:
|
||
|
/* don't use punit! */
|
||
|
/* Don't use punit! */
|
||
|
return;
|
||
|
case MR_OK:
|
||
|
/* FIXME: continue moving? */
|
||
|
/* FIXME: Continue moving? */
|
||
|
break;
|
||
|
default:
|
||
|
unit_data->done = TRUE;
|
||
| ... | ... | |
|
if (punit->moves_left > 0) {
|
||
|
struct city *safe_city = find_nearest_safe_city(punit);
|
||
|
if (safe_city != NULL) {
|
||
|
if (safe_city != nullptr) {
|
||
|
punit->goto_tile = safe_city->tile;
|
||
|
UNIT_LOG(LOGLEVEL_FERRY, punit, "No work, going home");
|
||
|
unit_data->done = TRUE;
|
||
|
dai_unit_new_task(ait, punit, AIUNIT_NONE, NULL);
|
||
|
dai_unit_new_task(ait, punit, AIUNIT_NONE, nullptr);
|
||
|
(void) dai_unit_goto(ait, punit, safe_city->tile);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
| ai/default/daiferry.h | ||
|---|---|---|
|
#ifndef FC__DAIFERRY_H
|
||
|
#define FC__DAIFERRY_H
|
||
|
/* utility */
|
||
|
#include "support.h" /* bool type */
|
||
|
/* common */
|
||
|
#include "fc_types.h"
|
||
|
struct pf_path;
|
||
| ... | ... | |
|
bool dai_is_ferry_type(const struct unit_type *pferry, struct ai_type *ait);
|
||
|
bool dai_is_ferry(struct unit *pferry, struct ai_type *ait);
|
||
|
/*
|
||
|
/*
|
||
|
* Initialize ferrybaot-related statistics in the ai data.
|
||
|
*/
|
||
|
void aiferry_init_stats(struct ai_type *ait, struct player *pplayer);
|
||
|
/*
|
||
|
* Find the nearest boat. Can be called from inside the continents too
|
||
|
/*
|
||
|
* Find the nearest boat. Can be called from inside the continents too
|
||
|
*/
|
||
|
int aiferry_find_boat(struct ai_type *ait, struct unit *punit,
|
||
|
int cap, struct pf_path **path);
|
||
|
/*
|
||
|
* How many boats are available
|
||
|
* How many boats are available
|
||
|
*/
|
||
|
int aiferry_avail_boats(struct ai_type *ait, struct player *pplayer);
|
||
|
/*
|
||
|
/*
|
||
|
* Initializes aiferry stats for a new unit
|
||
|
*/
|
||
|
void dai_ferry_init_ferry(struct ai_type *ait, struct unit *ferry);
|
||
| ... | ... | |
|
void aiferry_clear_boat(struct ai_type *ait, struct unit *punit);
|
||
|
/*
|
||
|
* Go to the destination by hitching a ride on a boat. Will try to find
|
||
|
* Go to the destination by hitching a ride on a boat. Will try to find
|
||
|
* a beachhead but it works better if dst_tile is on the coast.
|
||
|
* Loads a bodyguard too, if necessary.
|
||
|
*/
|
||
| ... | ... | |
|
struct unit *punit, struct tile *dst_tile,
|
||
|
bool with_bodyguard);
|
||
|
/*
|
||
|
* Go to the destination on a particular boat. Will try to find
|
||
|
* Go to the destination on a particular boat. Will try to find
|
||
|
* a beachhead but it works better if ptile is on the coast.
|
||
|
*/
|
||
|
bool aiferry_goto_amphibious(struct ai_type *ait, struct unit *ferry,
|
||
|
struct unit *passenger, struct tile *ptile);
|
||
|
struct unit *passenger, struct tile *ptile);
|
||
|
bool dai_amphibious_goto_constrained(struct ai_type *ait,
|
||
|
struct unit *ferry,
|
||
| ... | ... | |
|
bool is_boss_of_boat(struct ai_type *ait, struct unit *punit);
|
||
|
/*
|
||
|
* Main boat managing function. Gets units on board to where they want to
|
||
|
* 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.
|
||
|
*/
|
||