/***************************************************************************** Find best tile the paratrooper should jump to. *****************************************************************************/ static struct tile *find_best_tile_to_paradrop_to(struct ai_type *ait, struct unit *punit) { int best = 0; int val; struct tile* best_tile = NULL; int range = unit_type(punit)->paratroopers_range; struct city* acity; struct player* pplayer = unit_owner(punit); /* First, we search for undefended cities in danger */ square_iterate(unit_tile(punit), range, ptile) { if (!map_is_known(ptile, pplayer)) { continue; } acity = tile_city(ptile); if (acity && city_owner(acity) == unit_owner(punit) && unit_list_size(ptile->units) == 0) { val = city_size_get(acity) * def_ai_city_data(acity, ait)->urgency; if (val > best) { best = val; best_tile = ptile; } } } square_iterate_end; if (best_tile != NULL) { acity = tile_city(best_tile); UNIT_LOG(LOGLEVEL_PARATROOPER, punit, "Choose to jump in order to protect allied city %s (%d %d). " "Benefit: %d", city_name(acity), TILE_XY(best_tile), best); return best_tile; } /* Second, we search for undefended enemy cities */ square_iterate(unit_tile(punit), range, ptile) { acity = tile_city(ptile); if (acity && pplayers_at_war(unit_owner(punit), city_owner(acity)) && (unit_list_size(ptile->units) == 0)) { if (!map_is_known_and_seen(ptile, pplayer, V_MAIN) && ai_handicap(pplayer, H_FOG)) { continue; } /* Prefer big cities on other continents */ val = city_size_get(acity) + (tile_continent(unit_tile(punit)) != tile_continent(ptile)); if (val > best) { best = val; best_tile = ptile; } } } square_iterate_end;
/************************************************************************* Callback from diplomat/spy dialog for "keep moving". (This should only occur when entering the tile of an allied unit.) **************************************************************************/ static void diplomat_keep_moving_unit_callback(GtkWidget *w, gpointer data) { struct unit *punit; struct unit *tunit; if ((punit = game_unit_by_number(diplomat_id)) && (tunit = game_unit_by_number(diplomat_target_id[ATK_UNIT])) && !same_pos(unit_tile(punit), unit_tile(tunit))) { request_diplomat_action(DIPLOMAT_MOVE, diplomat_id, diplomat_target_id[ATK_UNIT], ATK_UNIT); } gtk_widget_destroy(diplomat_dialog); }
/************************************************************************** Popup dialog where the user choose the name of the new city punit = (settler) unit which builds the city suggestname = suggetion of the new city's name **************************************************************************/ void popup_newcity_dialog(struct unit *punit, const char *suggestname) { input_dialog_create(GTK_WINDOW(toplevel), /*"shellnewcityname" */ _("Build New City"), _("What should we call our new city?"), suggestname, name_new_city_popup_callback, GINT_TO_POINTER(tile_index(unit_tile(punit)))); }
/************************************************************************** Callback from diplomat/spy dialog for "keep moving". (This should only occur when entering the tile of an allied unit..) **************************************************************************/ static void diplomat_keep_moving_callback_unit(Widget w, XtPointer client_data, XtPointer call_data) { struct unit *punit; struct unit *tunit; destroy_message_dialog(w); diplomat_dialog = NULL; if ((punit = game_unit_by_number(diplomat_id)) && (tunit = game_unit_by_number(diplomat_target_id)) && !same_pos(unit_tile(punit), unit_tile(tunit))) { request_diplomat_action(DIPLOMAT_MOVE, diplomat_id, diplomat_target_id, ATK_UNIT); } process_diplomat_arrival(NULL, 0); }
/**************************************************************** Callback from diplomat/spy dialog for "keep moving". (This should only occur when entering allied city.) *****************************************************************/ static void diplomat_keep_moving_callback(GtkWidget *w, gpointer data) { struct unit *punit; struct city *pcity; if ((punit = game_unit_by_number(diplomat_id)) && (pcity = game_city_by_number(diplomat_target_id)) && !same_pos(unit_tile(punit), city_tile(pcity))) { request_diplomat_action(DIPLOMAT_MOVE, diplomat_id, diplomat_target_id, 0); } gtk_widget_destroy(diplomat_dialog); }
/**************************************************************** Callback from diplomat/spy dialog for "keep moving". (This should only occur when entering allied city.) *****************************************************************/ static int diplomat_keep_moving_callback(struct widget *pWidget) { if (Main.event.button.button == SDL_BUTTON_LEFT) { struct unit *punit; struct city *pcity; if ((punit = game_unit_by_number(pDiplomat_Dlg->diplomat_id)) && (pcity = game_city_by_number(pDiplomat_Dlg->diplomat_target_id)) && !same_pos(unit_tile(punit), city_tile(pcity))) { request_diplomat_action(DIPLOMAT_MOVE, pDiplomat_Dlg->diplomat_id, pDiplomat_Dlg->diplomat_target_id, 0); } popdown_diplomat_dialog(); } return -1; }
/************************************************************************** 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; }
/************************************************************************** Popup a dialog giving a player choices when their caravan arrives at a city (other than its home city). Example: - Establish trade route. - Help build wonder. - Keep moving. **************************************************************************/ void popup_caravan_dialog(struct unit *pUnit, struct city *pHomecity, struct city *pDestcity) { struct widget *pWindow = NULL, *pBuf = NULL; SDL_String16 *pStr; struct CONTAINER *pCont; char cBuf[128]; SDL_Rect area; if (pCaravan_Dlg) { return; } caravan_unit_id=pUnit->id; caravan_city_id=pDestcity->id; pCont = fc_calloc(1, sizeof(struct CONTAINER)); pCont->id0 = pUnit->id; pCont->id1 = pDestcity->id; pCaravan_Dlg = fc_calloc(1, sizeof(struct SMALL_DLG)); is_unit_move_blocked = TRUE; fc_snprintf(cBuf, sizeof(cBuf), _("Your %s has arrived at %s"), unit_name_translation(pUnit), city_name(pDestcity)); /* window */ pStr = create_str16_from_char(cBuf, adj_font(12)); pStr->style |= TTF_STYLE_BOLD; pWindow = create_window_skeleton(NULL, pStr, 0); pWindow->action = caravan_dlg_window_callback; set_wstate(pWindow, FC_WS_NORMAL); add_to_gui_list(ID_CARAVAN_DLG_WINDOW, pWindow); pCaravan_Dlg->pEndWidgetList = pWindow; area = pWindow->area; area.h = MAX(area.h, adj_size(2)); /* ---------- */ if (can_cities_trade(pHomecity, pDestcity)) { int revenue = get_caravan_enter_city_trade_bonus(pHomecity, pDestcity); if (can_establish_trade_route(pHomecity, pDestcity)) { fc_snprintf(cBuf, sizeof(cBuf), _("Establish Trade route with %s ( %d R&G + %d trade )"), city_name(pHomecity), revenue, trade_between_cities(pHomecity, pDestcity)); } else { revenue = (revenue + 2) / 3; fc_snprintf(cBuf, sizeof(cBuf), _("Enter Marketplace ( %d R&G bonus )"), revenue); } create_active_iconlabel(pBuf, pWindow->dst, pStr, cBuf, caravan_establish_trade_callback); pBuf->data.cont = pCont; set_wstate(pBuf, FC_WS_NORMAL); add_to_gui_list(ID_LABEL, pBuf); area.w = MAX(area.w, pBuf->size.w); area.h += pBuf->size.h; } /* ---------- */ if (unit_can_help_build_wonder(pUnit, pDestcity)) { create_active_iconlabel(pBuf, pWindow->dst, pStr, _("Help build Wonder"), caravan_help_build_wonder_callback); pBuf->data.cont = pCont; set_wstate(pBuf, FC_WS_NORMAL); add_to_gui_list(ID_LABEL, pBuf); area.w = MAX(area.w, pBuf->size.w); area.h += pBuf->size.h; } /* ---------- */ create_active_iconlabel(pBuf, pWindow->dst, pStr, _("Keep moving"), exit_caravan_dlg_callback); pBuf->data.cont = pCont; set_wstate(pBuf, FC_WS_NORMAL); set_wflag(pBuf, WF_FREE_DATA); pBuf->key = SDLK_ESCAPE; add_to_gui_list(ID_LABEL, pBuf); area.w = MAX(area.w, pBuf->size.w); area.h += pBuf->size.h; /* ---------- */ pCaravan_Dlg->pBeginWidgetList = pBuf; /* setup window size and start position */ resize_window(pWindow, NULL, NULL, (pWindow->size.w - pWindow->area.w) + area.w, (pWindow->size.h - pWindow->area.h) + area.h); area = pWindow->area; auto_center_on_focus_unit(); put_window_near_map_tile(pWindow, pWindow->size.w, pWindow->size.h, unit_tile(pUnit)); /* setup widget size and start position */ pBuf = pWindow->prev; setup_vertical_widgets_position(1, area.x, area.y + 1, area.w, 0, pCaravan_Dlg->pBeginWidgetList, pBuf); /* --------------------- */ /* redraw */ redraw_group(pCaravan_Dlg->pBeginWidgetList, pWindow, 0); widget_flush(pWindow); }
/************************************************************************** Main handler for key presses **************************************************************************/ static Uint16 main_key_down_handler(SDL_keysym Key, void *pData) { static struct widget *pWidget; if ((pWidget = find_next_widget_for_key(NULL, Key)) != NULL) { return widget_pressed_action(pWidget); } else { if (Key.sym == SDLK_TAB) { /* input */ popup_input_line(); } else { if (map_event_handler(Key) && C_S_RUNNING == client_state()) { switch (Key.sym) { case SDLK_RETURN: case SDLK_KP_ENTER: if (LSHIFT || RSHIFT) { disable_focus_animation(); key_end_turn(); } else { struct unit *pUnit; struct city *pCity; if (NULL != (pUnit = head_of_units_in_focus()) && (pCity = tile_city(unit_tile(pUnit))) != NULL && city_owner(pCity) == client.conn.playing) { popup_city_dialog(pCity); } } return ID_ERROR; case SDLK_F2: units_report_dialog_popup(FALSE); return ID_ERROR; case SDLK_F4: city_report_dialog_popup(FALSE); return ID_ERROR; case SDLK_F7: send_report_request(REPORT_WONDERS_OF_THE_WORLD); return ID_ERROR; case SDLK_F8: send_report_request(REPORT_TOP_5_CITIES); return ID_ERROR; case SDLK_F9: if (meswin_dialog_is_open()) { meswin_dialog_popdown(); } else { meswin_dialog_popup(TRUE); } flush_dirty(); return ID_ERROR; case SDLK_F11: send_report_request(REPORT_DEMOGRAPHIC); return ID_ERROR; case SDLK_F12: popup_spaceship_dialog(client.conn.playing); return ID_ERROR; default: return ID_ERROR; } } } } return ID_ERROR; }
/************************************************************************** Click a link. **************************************************************************/ static gboolean event_after(GtkWidget *text_view, GdkEventButton *event) { GtkTextIter start, end, iter; GtkTextBuffer *buffer; GSList *tags, *tagp; gint x, y; struct tile *ptile = NULL; if (event->type != GDK_BUTTON_RELEASE || event->button != 1) { return FALSE; } buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view)); /* We shouldn't follow a link if the user has selected something. */ gtk_text_buffer_get_selection_bounds(buffer, &start, &end); if (gtk_text_iter_get_offset(&start) != gtk_text_iter_get_offset(&end)) { return FALSE; } gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW (text_view), GTK_TEXT_WINDOW_WIDGET, event->x, event->y, &x, &y); gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(text_view), &iter, x, y); if ((tags = gtk_text_iter_get_tags(&iter))) { for (tagp = tags; tagp; tagp = tagp->next) { GtkTextTag *tag = tagp->data; enum text_link_type type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tag), "type")); if (type != 0) { /* This is a link. */ int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tag), "id")); ptile = NULL; /* Real type is type - 1. * See comment in apply_text_tag() for g_object_set_data(). */ type--; switch (type) { case TLT_CITY: { struct city *pcity = game_city_by_number(id); if (pcity) { ptile = client_city_tile(pcity); } else { output_window_append(ftc_client, _("This city isn't known!")); } } break; case TLT_TILE: ptile = index_to_tile(id); if (!ptile) { output_window_append(ftc_client, _("This tile doesn't exist in this game!")); } break; case TLT_UNIT: { struct unit *punit = game_unit_by_number(id); if (punit) { ptile = unit_tile(punit); } else { output_window_append(ftc_client, _("This unit isn't known!")); } } break; } if (ptile) { center_tile_mapcanvas(ptile); link_mark_restore(type, id); gtk_widget_grab_focus(GTK_WIDGET(map_canvas)); } } } g_slist_free(tags); } return FALSE; }