/************************************************************************** Log message for bodyguards. They will appear like this 2: Polish Mech. Inf.[485] bodyguard (38,22){Riflemen:574@37,23} was ... note that these messages are likely to wrap if long. **************************************************************************/ void BODYGUARD_LOG(int level, const struct unit *punit, const char *msg) { char buffer[500]; int minlevel = MIN(LOGLEVEL_BODYGUARD, level); const struct unit *pcharge; const struct city *pcity; int id = -1; int charge_x = -1; int charge_y = -1; const char *type = "guard"; const char *s = "none"; if (punit->debug) { minlevel = LOG_TEST; } else if (minlevel > fc_log_level) { return; } pcity = game_find_city_by_number(punit->ai.charge); pcharge = game_find_unit_by_number(punit->ai.charge); if (pcharge) { charge_x = pcharge->tile->x; charge_y = pcharge->tile->y; id = pcharge->id; type = "bodyguard"; s = unit_rule_name(pcharge); } else if (pcity) { charge_x = pcity->tile->x; charge_y = pcity->tile->y; id = pcity->id; type = "cityguard"; s = city_name(pcity); } /* else perhaps the charge died */ my_snprintf(buffer, sizeof(buffer), "%s %s[%d] %s (%d,%d){%s:%d@%d,%d} ", nation_rule_name(nation_of_unit(punit)), unit_rule_name(punit), punit->id, type, TILE_XY(punit->tile), s, id, charge_x, charge_y); cat_snprintf(buffer, sizeof(buffer), "%s", msg); if (punit->debug) { notify_conn(NULL, NULL, E_AI_DEBUG, ftc_log, "%s", buffer); } freelog(minlevel, "%s", buffer); }
/********************************************************************** Find something to bomb Air-units specific victim search Returns the want for the best target. The targets are stored in the path and pptile arguments if not NULL. TODO: take counterattack dangers into account TODO: make separate handicaps for air units seeing targets IMO should be more restrictive than general H_MAP, H_FOG *********************************************************************/ static int find_something_to_bomb(struct ai_type *ait, struct unit *punit, struct pf_path **path, struct tile **pptile) { struct player *pplayer = unit_owner(punit); struct pf_parameter parameter; struct pf_map *pfm; struct tile *best_tile = NULL; int best = 0; pft_fill_unit_parameter(¶meter, punit); pfm = pf_map_new(¶meter); /* Let's find something to bomb */ pf_map_move_costs_iterate(pfm, ptile, move_cost, FALSE) { if (move_cost >= punit->moves_left) { /* Too far! */ break; } if (ai_handicap(pplayer, H_MAP) && !map_is_known(ptile, pplayer)) { /* The target tile is unknown */ continue; } if (ai_handicap(pplayer, H_FOG) && !map_is_known_and_seen(ptile, pplayer, V_MAIN)) { /* The tile is fogged */ continue; } if (is_enemy_unit_tile(ptile, pplayer) && dai_should_we_air_attack_tile(ait, punit, ptile) && can_unit_attack_tile(punit, ptile)) { int new_best = dai_evaluate_tile_for_air_attack(punit, ptile); if (new_best > best) { best_tile = ptile; best = new_best; log_debug("%s wants to attack tile (%d, %d)", unit_rule_name(punit), TILE_XY(ptile)); } } } pf_map_positions_iterate_end; /* Return the best values. */ if (pptile) { *pptile = best_tile; } if (path) { *path = best_tile ? pf_map_path(pfm, best_tile) : NULL; } pf_map_destroy(pfm); return best; }
/************************************************************************** Log unit messages, they will appear like this 2: Polish Archers[139] (5,35)->(0,0){0,0} stays to defend city where [] is unit id, ()->() are coordinates present and goto, and {,} contains bodyguard and ferryboat ids. **************************************************************************/ void UNIT_LOG(int level, const struct unit *punit, const char *msg, ...) { char buffer[500]; char buffer2[500]; va_list ap; int minlevel = MIN(LOGLEVEL_UNIT, level); int gx, gy; bool messwin = FALSE; /* output to message window */ if (punit->debug) { minlevel = LOG_TEST; } else { /* Are we a virtual unit evaluated in a debug city?. */ if (punit->id == 0) { struct city *pcity = tile_city(punit->tile); if (pcity && pcity->debug) { minlevel = LOG_TEST; messwin = TRUE; } } if (minlevel > fc_log_level) { return; } } if (punit->goto_tile) { gx = punit->goto_tile->x; gy = punit->goto_tile->y; } else { gx = gy = -1; } my_snprintf(buffer, sizeof(buffer), "%s %s[%d] %s (%d,%d)->(%d,%d){%d,%d} ", nation_rule_name(nation_of_unit(punit)), unit_rule_name(punit), punit->id, get_activity_text(punit->activity), TILE_XY(punit->tile), gx, gy, punit->ai.bodyguard, punit->ai.ferryboat); va_start(ap, msg); my_vsnprintf(buffer2, sizeof(buffer2), msg, ap); va_end(ap); cat_snprintf(buffer, sizeof(buffer), "%s", buffer2); if (punit->debug || messwin) { notify_conn(NULL, NULL, E_AI_DEBUG, ftc_log, "%s", buffer); } freelog(minlevel, "%s", buffer); }
void fc_mysql_log_unit_removed(const struct unit* punit) { fc_mysql_query( "INSERT INTO `" FC__MYSQL_UNITLOG_TABLE "` " "(" "created," "unit_name," "tile_x," "tile_y," "owner_name," "unit_hp," "unit_veteran," "unit_id," "removed) " "VALUES (" "NOW()," "%s," "%d," "%d," "%s," "%d," "%d," "%d," "1)", unit_rule_name(punit), TILE_XY(punit->tile), punit->owner->name, punit->hp, punit->veteran, punit->id ); }
/************************************************************************** Log unit messages, they will appear like this 2: Polish Archers[139] (5,35)->(0,0){0,0} stays to defend city where [] is unit id, ()->() are coordinates present and goto, and {,} contains bodyguard and ferryboat ids. **************************************************************************/ void real_unit_log(const char *file, const char *function, int line, enum log_level level, bool notify, const struct unit *punit, const char *msg, ...) { char buffer[500]; char buffer2[500]; va_list ap; int gx, gy; char aibuf[500] = "\0"; CALL_PLR_AI_FUNC(log_fragment_unit, unit_owner(punit), aibuf, sizeof(aibuf), punit); if (punit->goto_tile) { index_to_map_pos(&gx, &gy, tile_index(punit->goto_tile)); } else { gx = gy = -1; } fc_snprintf(buffer, sizeof(buffer), "%s %s[%d] %s (%d,%d)->(%d,%d){%s} ", nation_rule_name(nation_of_unit(punit)), unit_rule_name(punit), punit->id, get_activity_text(punit->activity), TILE_XY(unit_tile(punit)), gx, gy, aibuf); va_start(ap, msg); fc_vsnprintf(buffer2, sizeof(buffer2), msg, ap); va_end(ap); cat_snprintf(buffer, sizeof(buffer), "%s", buffer2); if (notify) { notify_conn(NULL, NULL, E_AI_DEBUG, ftc_log, "%s", buffer); } do_log(file, function, line, FALSE, level, "%s", buffer); }
/************************************************************************ Trying to manage bombers and stuff. If we are in the open { if moving intelligently on a valid GOTO, { carry on doing it. } else { go refuel } } else { try to attack something } TODO: distant target selection, support for fuel > 2 ***********************************************************************/ void dai_manage_airunit(struct ai_type *ait, struct player *pplayer, struct unit *punit) { struct tile *dst_tile = unit_tile(punit); /* Loop prevention */ int moves = punit->moves_left; int id = punit->id; struct pf_parameter parameter; struct pf_map *pfm; struct pf_path *path; CHECK_UNIT(punit); if (!is_unit_being_refueled(punit)) { /* We are out in the open, what shall we do? */ if (punit->activity == ACTIVITY_GOTO /* We are on a GOTO. Check if it will get us anywhere */ && NULL != punit->goto_tile && !same_pos(unit_tile(punit), punit->goto_tile) && is_airunit_refuel_point(punit->goto_tile, pplayer, unit_type(punit), FALSE)) { pfm = pf_map_new(¶meter); path = pf_map_path(pfm, punit->goto_tile); if (path) { bool alive = adv_follow_path(punit, path, punit->goto_tile); pf_path_destroy(path); pf_map_destroy(pfm); if (alive && punit->moves_left > 0) { /* Maybe do something else. */ dai_manage_airunit(ait, pplayer, punit); } return; } pf_map_destroy(pfm); } else if ((dst_tile = find_nearest_airbase(punit, &path))) { /* Go refuelling */ if (!adv_follow_path(punit, path, dst_tile)) { pf_path_destroy(path); return; /* The unit died. */ } pf_path_destroy(path); } else { if (punit->fuel == 1) { UNIT_LOG(LOG_DEBUG, punit, "Oops, fallin outta the sky"); } def_ai_unit_data(punit, ait)->done = TRUE; /* Won't help trying again */ return; } } else if (punit->fuel == unit_type(punit)->fuel) { /* We only leave a refuel point when we are on full fuel */ if (find_something_to_bomb(ait, punit, &path, &dst_tile) > 0) { /* Found target, coordinates are in punit's goto_dest. * TODO: separate attacking into a function, check for the best * tile to attack from */ fc_assert_ret(path != NULL && dst_tile != NULL); if (!adv_follow_path(punit, path, dst_tile)) { pf_path_destroy(path); return; /* The unit died. */ } pf_path_destroy(path); /* goto would be aborted: "Aborting GOTO for AI attack procedures" * now actually need to attack */ /* We could use ai_military_findvictim here, but I don't trust it... */ unit_activity_handling(punit, ACTIVITY_IDLE); if (is_tiles_adjacent(unit_tile(punit), dst_tile)) { (void) unit_move_handling(punit, dst_tile, TRUE, FALSE); } } else if ((dst_tile = dai_find_strategic_airbase(ait, punit, &path))) { log_debug("%s will fly to (%i, %i) (%s) to fight there", unit_rule_name(punit), TILE_XY(dst_tile), tile_city(dst_tile) ? city_name(tile_city(dst_tile)) : ""); def_ai_unit_data(punit, ait)->done = TRUE; /* Wait for next turn */ if (!adv_follow_path(punit, path, dst_tile)) { pf_path_destroy(path); return; /* The unit died. */ } pf_path_destroy(path); } else { log_debug("%s cannot find anything to kill and is staying put", unit_rule_name(punit)); def_ai_unit_data(punit, ait)->done = TRUE; unit_activity_handling(punit, ACTIVITY_IDLE); } } if ((punit = game_unit_by_number(id)) != NULL && punit->moves_left > 0 && punit->moves_left != moves) { /* We have moved this turn, might have ended up stuck out in the fields * so, as a safety measure, let's manage again */ dai_manage_airunit(ait, pplayer, punit); } }
/********************************************************************** Returns an estimate for the profit gained through attack. Assumes that the victim is within one day's flight **********************************************************************/ static int dai_evaluate_tile_for_air_attack(struct unit *punit, struct tile *dst_tile) { struct unit *pdefender; /* unit costs in shields */ int balanced_cost, unit_cost, victim_cost = 0; /* unit stats */ int unit_attack, victim_defence; /* final answer */ int profit; /* time spent in the air */ int sortie_time; #define PROB_MULTIPLIER 100 /* should unify with those in combat.c */ if (!can_unit_attack_tile(punit, dst_tile) || !(pdefender = get_defender(punit, dst_tile))) { return 0; } /* Ok, we can attack, but is it worth it? */ /* Cost of our unit */ unit_cost = unit_build_shield_cost(punit); /* This is to say "wait, ill unit will get better!" */ unit_cost = unit_cost * unit_type(punit)->hp / punit->hp; /* Determine cost of enemy units */ victim_cost = stack_cost(punit, pdefender); if (0 == victim_cost) { return 0; } /* Missile would die 100% so we adjust the victim_cost -- GB */ if (uclass_has_flag(unit_class(punit), UCF_MISSILE)) { victim_cost -= unit_build_shield_cost(punit); } unit_attack = (int) (PROB_MULTIPLIER * unit_win_chance(punit, pdefender)); victim_defence = PROB_MULTIPLIER - unit_attack; balanced_cost = build_cost_balanced(unit_type(punit)); sortie_time = (unit_has_type_flag(punit, UTYF_ONEATTACK) ? 1 : 0); profit = kill_desire(victim_cost, unit_attack, unit_cost, victim_defence, 1) - SHIELD_WEIGHTING + 2 * TRADE_WEIGHTING; if (profit > 0) { profit = military_amortize(unit_owner(punit), game_city_by_number(punit->homecity), profit, sortie_time, balanced_cost); log_debug("%s at (%d, %d) is a worthy target with profit %d", unit_rule_name(pdefender), TILE_XY(dst_tile), profit); } else { log_debug("%s(%d, %d): %s at (%d, %d) is unworthy with profit %d", unit_rule_name(punit), TILE_XY(unit_tile(punit)), unit_rule_name(pdefender), TILE_XY(dst_tile), profit); profit = 0; } return profit; }