[Freeciv-Dev] Re: (PR#14365) battlegroups...
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
<URL: http://bugs.freeciv.org/Ticket/Display.html?id=14365 >
This version adds drag-and-select clicking.
There is a monolithic amount of work left to do to make all operations
work the way you'd expect, and integrtate the code fully. However I
think it should now be quite playable and possibly ready for committing.
-jason
Index: utility/genlist.c
===================================================================
--- utility/genlist.c (revision 11168)
+++ utility/genlist.c (working copy)
@@ -39,6 +39,24 @@
}
/************************************************************************
+ Returns a new genlist that's a copy of the existing one.
+************************************************************************/
+struct genlist *genlist_copy(struct genlist *pgenlist)
+{
+ struct genlist *pcopy = genlist_new();
+
+ if (pgenlist) {
+ struct genlist_link *plink;
+
+ for (plink = pgenlist->head_link; plink; plink = plink->next) {
+ genlist_append(pcopy, plink->dataptr);
+ }
+ }
+
+ return pcopy;
+}
+
+/************************************************************************
Remove a genlist. The list must be empty first!
************************************************************************/
void genlist_free(struct genlist *pgenlist)
Index: utility/genlist.h
===================================================================
--- utility/genlist.h (revision 11168)
+++ utility/genlist.h (working copy)
@@ -73,6 +73,7 @@
int genlist_size(const struct genlist *pgenlist);
void *genlist_get(const struct genlist *pgenlist, int idx);
struct genlist *genlist_new(void);
+struct genlist *genlist_copy(struct genlist *pgenlist);
void genlist_unlink_all(struct genlist *pgenlist);
void genlist_free(struct genlist *pgenlist);
void genlist_append(struct genlist *pgenlist, void *data);
Index: utility/speclist.h
===================================================================
--- utility/speclist.h (revision 11168)
+++ utility/speclist.h (working copy)
@@ -86,6 +86,15 @@
return speclist;
}
+static inline SPECLIST_LIST *SPECLIST_FOO(_list_copy) (SPECLIST_LIST *plist)
+{
+ SPECLIST_LIST *newlist = (SPECLIST_LIST *)fc_malloc(sizeof(*newlist));
+
+ newlist->list = genlist_copy(plist ? plist->list : NULL);
+
+ return newlist;
+}
+
static inline void SPECLIST_FOO(_list_prepend) (SPECLIST_LIST *tthis,
SPECLIST_TYPE *pfoo)
{
genlist_prepend(tthis->list, pfoo);
Index: utility/shared.h
===================================================================
--- utility/shared.h (revision 11168)
+++ utility/shared.h (working copy)
@@ -46,7 +46,8 @@
: ((value) >= (range) ? (value) % (range) : (value)))
#define BOOL_VAL(x) ((x) != 0)
-#define XOR(p, q) (!(p) != !(q))
+#define XOR(p, q) (BOOL_VAL(p) != BOOL_VAL(q))
+#define EQ(p, q) (BOOL_VAL(p) == BOOL_VAL(q))
/*
* DIVIDE() divides and rounds down, rather than just divides and
Index: server/unittools.c
===================================================================
--- server/unittools.c (revision 11168)
+++ server/unittools.c (working copy)
@@ -1766,6 +1766,7 @@
packet->transported_by = punit->transported_by;
}
packet->occupy = get_transporter_occupancy(punit);
+ packet->battlegroup = punit->battlegroup;
packet->has_orders = punit->has_orders;
if (punit->has_orders) {
int i;
Index: server/unithand.c
===================================================================
--- server/unithand.c (revision 11168)
+++ server/unithand.c (working copy)
@@ -1423,6 +1423,22 @@
}
/**************************************************************************
+ Assign the unit to the battlegroup.
+
+ Battlegroups are handled entirely by the client, so all we have to
+ do here is save the battlegroup ID so that it'll be persistent.
+**************************************************************************/
+void handle_unit_battlegroup(struct player *pplayer,
+ int unit_id, int battlegroup)
+{
+ struct unit *punit = player_find_unit_by_id(pplayer, unit_id);
+
+ if (punit) {
+ punit->battlegroup = CLIP(-1, battlegroup, MAX_NUM_BATTLEGROUPS);
+ }
+}
+
+/**************************************************************************
...
**************************************************************************/
void handle_unit_autosettlers(struct player *pplayer, int unit_id)
Index: server/savegame.c
===================================================================
--- server/savegame.c (revision 11168)
+++ server/savegame.c (working copy)
@@ -1468,6 +1468,10 @@
punit->done_moving = secfile_lookup_bool_default(file,
(punit->moves_left == 0), "player%d.u%d.done_moving", plrno, i);
+ punit->battlegroup
+ = secfile_lookup_int_default(file, BATTLEGROUP_NONE,
+ "player%d.u%d.battlegroup", plrno, i);
+
/* Load the goto information. Older savegames will not have the
* "go" field, so we just load the goto destination by default. */
if (secfile_lookup_bool_default(file, TRUE,
@@ -2637,6 +2641,8 @@
plrno, i);
secfile_insert_int(file, punit->fuel, "player%d.u%d.fuel",
plrno, i);
+ secfile_insert_int(file, punit->battlegroup,
+ "player%d.u%d.battlegroup", plrno, i);
if (punit->goto_tile) {
secfile_insert_bool(file, TRUE, "player%d.u%d.go", plrno, i);
Index: common/unit.c
===================================================================
--- common/unit.c (revision 11168)
+++ common/unit.c (working copy)
@@ -1498,6 +1498,7 @@
punit->ord_city = 0;
set_unit_activity(punit, ACTIVITY_IDLE);
punit->occupy = 0;
+ punit->battlegroup = BATTLEGROUP_NONE;
punit->client.colored = FALSE;
punit->server.vision = NULL; /* No vision. */
punit->has_orders = FALSE;
Index: common/unit.h
===================================================================
--- common/unit.h (revision 11168)
+++ common/unit.h (working copy)
@@ -164,6 +164,12 @@
int transported_by;
int occupy; /* number of units that occupy transporter */
+
+ /* The battlegroup ID: defined by the client but stored by the server. */
+#define MAX_NUM_BATTLEGROUPS (4)
+#define BATTLEGROUP_NONE (-1)
+ int battlegroup;
+
struct {
/* Equivalent to pcity->client.color. Only for F_CITIES units. */
bool colored;
Index: common/packets.def
===================================================================
--- common/packets.def (revision 11168)
+++ common/packets.def (working copy)
@@ -241,7 +241,7 @@
Spaceship
Ruleset
-The last used packet number is 116.
+The last used packet number is 117.
****************************************************/
@@ -727,6 +727,8 @@
ACTIVITY activity;
SPECIAL activity_target;
+ SINT8 battlegroup;
+
BOOL has_orders;
UINT16 orders_length, orders_index;
BOOL orders_repeat, orders_vigilant;
@@ -786,6 +788,11 @@
UNIT unit_id;
end
+PACKET_UNIT_BATTLEGROUP=117;cs,dsend
+ UNIT unit_id;
+ SINT8 battlegroup;
+end
+
PACKET_UNIT_HELP_BUILD_WONDER=57;cs,dsend
UNIT unit_id;
end
Index: client/control.c
===================================================================
--- client/control.c (revision 11168)
+++ client/control.c (working copy)
@@ -47,19 +47,21 @@
int num_units_below = MAX_NUM_UNITS_BELOW;
/* unit_focus points to the current unit in focus */
-static struct unit *punit_focus = NULL;
+static struct unit_list *pfocus_units;
/* The previously focused unit. Focus can generally be recalled on this
* unit with keypad 5. FIXME: this is not reset when the client
* disconnects. */
-static int previous_focus_id = -1;
+static struct unit_list *previous_focus;
/* These should be set via set_hover_state() */
-int hover_unit = 0; /* id of unit hover_state applies to */
+struct unit_list *hover_units;
enum cursor_hover_state hover_state = HOVER_NONE;
enum unit_activity connect_activity;
enum unit_orders goto_last_order; /* Last order for goto */
+static struct unit_list *battlegroups[MAX_NUM_BATTLEGROUPS];
+
/* units involved in current combat */
static struct unit *punit_attacking = NULL;
static struct unit *punit_defending = NULL;
@@ -86,8 +88,16 @@
**************************************************************************/
void control_init(void)
{
+ int i;
+
caravan_arrival_queue = genlist_new();
diplomat_arrival_queue = genlist_new();
+ hover_units = unit_list_new();
+ pfocus_units = unit_list_new();
+ previous_focus = unit_list_new();
+ for (i = 0; i < MAX_NUM_BATTLEGROUPS; i++) {
+ battlegroups[i] = unit_list_new();
+ }
}
/**************************************************************************
@@ -95,41 +105,138 @@
**************************************************************************/
void control_done(void)
{
+ int i;
+
genlist_free(caravan_arrival_queue);
genlist_free(diplomat_arrival_queue);
+ unit_list_free(hover_units);
+ unit_list_free(pfocus_units);
+ unit_list_free(previous_focus);
+ for (i = 0; i < MAX_NUM_BATTLEGROUPS; i++) {
+ unit_list_free(battlegroups[i]);
+ }
}
/**************************************************************************
+ Called when a unit is killed; this removes it from the control lists.
+**************************************************************************/
+void control_unit_killed(struct unit *punit)
+{
+ int i;
+
+ unit_list_unlink(get_units_in_focus(), punit);
+ unit_list_unlink(hover_units, punit);
+ unit_list_unlink(previous_focus, punit);
+ for (i = 0; i < MAX_NUM_BATTLEGROUPS; i++) {
+ unit_list_unlink(battlegroups[i], punit);
+ }
+}
+
+/**************************************************************************
+ Change the battlegroup for this unit.
+**************************************************************************/
+void unit_change_battlegroup(struct unit *punit, int battlegroup)
+{
+ if (battlegroup < 0 || battlegroup >= MAX_NUM_BATTLEGROUPS) {
+ battlegroup = BATTLEGROUP_NONE;
+ }
+
+ if (punit->battlegroup != battlegroup) {
+ if (battlegroup != BATTLEGROUP_NONE) {
+ unit_list_append(battlegroups[battlegroup], punit);
+ }
+ if (punit->battlegroup != BATTLEGROUP_NONE) {
+ unit_list_unlink(battlegroups[punit->battlegroup], punit);
+ }
+ punit->battlegroup = battlegroup;
+ }
+}
+
+/**************************************************************************
+ Call this on new units to enter them in the battlegroup lists.
+**************************************************************************/
+void unit_register_battlegroup(struct unit *punit)
+{
+ if (punit->battlegroup < 0 || punit->battlegroup >= MAX_NUM_BATTLEGROUPS) {
+ punit->battlegroup = BATTLEGROUP_NONE;
+ } else {
+ unit_list_append(battlegroups[punit->battlegroup], punit);
+ }
+}
+
+/**************************************************************************
Enter the given hover state.
activity => The connect activity (ACTIVITY_ROAD, etc.)
order => The last order (ORDER_BUILD_CITY, ORDER_LAST, etc.)
**************************************************************************/
-void set_hover_state(struct unit *punit, enum cursor_hover_state state,
+void set_hover_state(struct unit_list *punits, enum cursor_hover_state state,
enum unit_activity activity,
enum unit_orders order)
{
- assert(punit != NULL || state == HOVER_NONE);
+ assert((punits && unit_list_size(punits) > 0) || state == HOVER_NONE);
assert(state == HOVER_CONNECT || activity == ACTIVITY_LAST);
assert(state == HOVER_GOTO || order == ORDER_LAST);
- if (punit)
- hover_unit = punit->id;
- else
- hover_unit = 0;
+ unit_list_unlink_all(hover_units);
+ if (punits) {
+ unit_list_iterate(punits, punit) {
+ unit_list_append(hover_units, punit);
+ } unit_list_iterate_end;
+ }
hover_state = state;
connect_activity = activity;
goto_last_order = order;
exit_goto_state();
}
+/****************************************************************************
+ Return TRUE iff this unit is in focus.
+****************************************************************************/
+bool unit_is_in_focus(const struct unit *punit)
+{
+ return unit_list_search(get_units_in_focus(), punit);
+}
+
+/****************************************************************************
+ Return TRUE iff a unit on this tile is in focus.
+****************************************************************************/
+struct unit *get_focus_unit_on_tile(const struct tile *ptile)
+{
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (punit->tile == ptile) {
+ return punit;
+ }
+ } unit_list_iterate_end;
+
+ return NULL;
+}
+
+/****************************************************************************
+ Finds a single focus unit that we can center on. May return NULL.
+****************************************************************************/
+static struct tile *find_a_focus_unit_tile_to_center_on(void)
+{
+ struct unit *punit;
+
+ if ((punit = get_focus_unit_on_tile(get_center_tile_mapcanvas()))) {
+ return punit->tile;
+ } else if (get_num_units_in_focus() > 0) {
+ return unit_list_get(get_units_in_focus(), 0)->tile;
+ } else {
+ return NULL;
+ }
+}
+
/**************************************************************************
Center on the focus unit, if off-screen and auto_center_on_unit is true.
**************************************************************************/
void auto_center_on_focus_unit(void)
{
- if (punit_focus && auto_center_on_unit &&
- !tile_visible_and_not_on_border_mapcanvas(punit_focus->tile)) {
- center_tile_mapcanvas(punit_focus->tile);
+ struct tile *ptile = find_a_focus_unit_tile_to_center_on();
+
+ if (ptile && auto_center_on_unit &&
+ !tile_visible_and_not_on_border_mapcanvas(ptile)) {
+ center_tile_mapcanvas(ptile);
}
}
@@ -143,21 +250,28 @@
**************************************************************************/
void set_unit_focus(struct unit *punit)
{
- struct unit *punit_old_focus = punit_focus;
-
if (punit && game.player_ptr && punit->owner != game.player_ptr) {
/* Callers should make sure this never happens. */
return;
}
- if (punit != punit_focus) {
+ /* FIXME: this won't work quite right; for instance activating a
+ * battlegroup twice in a row will store the focus erronously. The only
+ * solution would be a set_units_focus() */
+ if (!(get_num_units_in_focus() == 1
+ && punit == unit_list_get(get_units_in_focus(), 0))) {
store_focus();
}
- /*
- * This should be the ONLY place we _modify_ punit_focus.
- */
- punit_focus = punit;
+ /* Redraw the old focus unit (to fix blinking or remove the selection
+ * circle). */
+ unit_list_iterate(pfocus_units, punit_old) {
+ refresh_unit_mapcanvas(punit_old, punit_old->tile, TRUE, FALSE);
+ } unit_list_iterate_end;
+ unit_list_unlink_all(pfocus_units);
+ if (punit) {
+ unit_list_append(pfocus_units, punit);
+ }
if (!can_client_change_view()) {
/* This function can be called to set the focus to NULL when
@@ -182,16 +296,40 @@
request_new_unit_activity(punit, ACTIVITY_IDLE);
}
}
-
- /* Redraw the old focus unit (to fix blinking or remove the selection
- * circle). */
- if (punit_old_focus
- && (!punit || !same_pos(punit_old_focus->tile, punit->tile))) {
- refresh_unit_mapcanvas(punit_old_focus, punit_old_focus->tile,
- TRUE, FALSE);
+
+ update_unit_info_label(pfocus_units);
+ update_menus();
+}
+
+/**************************************************************************
+ Adds this unit to the list of units in focus.
+**************************************************************************/
+void add_unit_focus(struct unit *punit)
+{
+ if (punit && game.player_ptr && punit->owner != game.player_ptr) {
+ /* Callers should make sure this never happens. */
+ return;
}
+ if (!punit || !can_client_change_view()) {
+ return;
+ }
+ if (unit_is_in_focus(punit)) {
+ return;
+ }
- update_unit_info_label(punit);
+ unit_list_append(pfocus_units, punit);
+ punit->focus_status = FOCUS_AVAIL;
+ refresh_unit_mapcanvas(punit, punit->tile, TRUE, FALSE);
+ if (unit_has_orders(punit)) {
+ /* Clear the focus unit's orders. */
+ request_orders_cleared(punit);
+ }
+ if (punit->activity != ACTIVITY_IDLE || punit->ai.control) {
+ punit->ai.control = FALSE;
+ refresh_unit_city_dialogs(punit);
+ request_new_unit_activity(punit, ACTIVITY_IDLE);
+ }
+ update_unit_info_label(pfocus_units);
update_menus();
}
@@ -212,8 +350,11 @@
**************************************************************************/
static void store_focus(void)
{
- if (punit_focus) {
- previous_focus_id = punit_focus->id;
+ if (get_num_units_in_focus() > 0) {
+ unit_list_unlink_all(previous_focus);
+ unit_list_iterate(get_units_in_focus(), punit) {
+ unit_list_append(previous_focus, punit);
+ } unit_list_iterate_end;
}
}
@@ -228,25 +369,46 @@
if (!game.player_ptr || !can_client_change_view()) {
return;
}
- if (!punit_focus
- || (punit_focus->activity != ACTIVITY_IDLE
- && !unit_has_orders(punit_focus)
- && punit_focus->activity != ACTIVITY_GOTO)
- || punit_focus->done_moving
- || punit_focus->moves_left == 0
- || punit_focus->ai.control) {
+ if (get_num_units_in_focus() == 0) {
advance_unit_focus();
+ } else {
+ bool alldone = TRUE;
+
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if ((punit->activity != ACTIVITY_IDLE
+ && !unit_has_orders(punit)
+ && punit->activity != ACTIVITY_GOTO)
+ || punit->done_moving
+ || punit->moves_left == 0
+ || punit->ai.control) {
+
+ } else {
+ alldone = FALSE;
+ break;
+ }
+ } unit_list_iterate_end;
+ if (alldone) {
+ advance_unit_focus();
+ }
}
}
/**************************************************************************
...
**************************************************************************/
-struct unit *get_unit_in_focus(void)
+struct unit_list *get_units_in_focus(void)
{
- return punit_focus;
+ return pfocus_units;
}
+/****************************************************************************
+ Return the number of units currently in focus (0 or more).
+****************************************************************************/
+int get_num_units_in_focus(void)
+{
+ return unit_list_size(pfocus_units);
+}
+
/**************************************************************************
This function may be called from packhand.c, via update_unit_focus(),
as a result of packets indicating change in activity for a unit. Also
@@ -256,7 +418,7 @@
**************************************************************************/
void advance_unit_focus(void)
{
- struct unit *punit_old_focus = punit_focus;
+ const int num_units_in_old_focus = get_num_units_in_focus();
struct unit *candidate = find_best_focus_candidate(FALSE);
if (!game.player_ptr || !can_client_change_view()) {
@@ -265,10 +427,18 @@
}
set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
- if (!can_client_change_view()) {
- return;
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ /*
+ * Is the unit which just lost focus a non-AI unit? If yes this
+ * enables the auto end turn.
+ */
+ if (!punit->ai.control) {
+ non_ai_unit_focus = TRUE;
+ break;
+ }
+ } unit_list_iterate_end;
+
if(!candidate) {
/* First try for "waiting" units. */
unit_list_iterate(game.player_ptr->units, punit) {
@@ -287,20 +457,15 @@
set_unit_focus(candidate);
/*
- * Is the unit which just lost focus a non-AI unit? If yes this
- * enables the auto end turn.
- */
- if (punit_old_focus && !punit_old_focus->ai.control) {
- non_ai_unit_focus = TRUE;
- }
-
- /*
* Handle auto-turn-done mode: If a unit was in focus (did move),
* but now none are (no more to move) and there was at least one
* non-AI unit this turn which was focused, then fake a Turn Done
* keypress.
*/
- if (auto_turn_done && punit_old_focus && !punit_focus && non_ai_unit_focus) {
+ if (auto_turn_done
+ && num_units_in_old_focus > 0
+ && get_num_units_in_focus() == 0
+ && non_ai_unit_focus) {
key_end_turn();
}
}
@@ -312,9 +477,7 @@
**************************************************************************/
static struct unit *find_best_focus_candidate(bool accept_current)
{
- struct unit *best_candidate;
- int best_dist = 99999;
- struct tile *ptile;
+ struct tile *ptile = get_center_tile_mapcanvas();
if (!game.player_ptr
|| !is_player_phase(game.player_ptr, game.info.phase)) {
@@ -322,29 +485,30 @@
return NULL;
}
- if (punit_focus) {
- ptile = punit_focus->tile;
- } else {
- ptile = get_center_tile_mapcanvas();
+ if (!get_focus_unit_on_tile(ptile)) {
+ struct unit *pfirst = unit_list_get(get_units_in_focus(), 0);
+
+ if (pfirst) {
+ ptile = pfirst->tile;
+ }
}
- best_candidate = NULL;
- unit_list_iterate(game.player_ptr->units, punit) {
- if ((punit != punit_focus || accept_current)
- && punit->focus_status == FOCUS_AVAIL
- && punit->activity == ACTIVITY_IDLE
- && !unit_has_orders(punit)
- && punit->moves_left > 0
- && !punit->done_moving
- && !punit->ai.control) {
- int d = sq_map_distance(punit->tile, ptile);
- if (d < best_dist) {
- best_candidate = punit;
- best_dist = d;
- }
- }
- } unit_list_iterate_end;
- return best_candidate;
+ iterate_outward(ptile, FC_INFINITY, ptile2) {
+ unit_list_iterate(ptile2->units, punit) {
+ if ((!unit_is_in_focus(punit) || accept_current)
+ && punit->owner == game.player_ptr
+ && punit->focus_status == FOCUS_AVAIL
+ && punit->activity == ACTIVITY_IDLE
+ && !unit_has_orders(punit)
+ && punit->moves_left > 0
+ && !punit->done_moving
+ && !punit->ai.control) {
+ return punit;
+ }
+ } unit_list_iterate_end;
+ } iterate_outward_end;
+
+ return NULL;
}
/**************************************************************************
@@ -353,6 +517,7 @@
struct unit *find_visible_unit(struct tile *ptile)
{
struct unit *panyowned = NULL, *panyother = NULL, *ptptother = NULL;
+ struct unit *pfocus;
/* If no units here, return nothing. */
if (unit_list_size(ptile->units)==0) {
@@ -374,10 +539,8 @@
}
/* If the unit in focus is at this tile, show that on top */
- if (punit_focus && same_pos(punit_focus->tile, ptile)) {
- unit_list_iterate(ptile->units, punit)
- if(punit == punit_focus) return punit;
- unit_list_iterate_end;
+ if ((pfocus = get_focus_unit_on_tile(ptile))) {
+ return pfocus;
}
/* If a city is here, return nothing (unit hidden by city). */
@@ -418,30 +581,23 @@
**************************************************************************/
double blink_active_unit(void)
{
- static struct unit *pblinking_unit;
static struct timer *blink_timer = NULL;
-
const double blink_time = get_focus_unit_toggle_timeout(tileset);
- struct unit *punit = punit_focus;
- bool need_update = FALSE;
- if (punit) {
- if (punit != pblinking_unit) {
- pblinking_unit = punit;
- reset_focus_unit_state(tileset);
- need_update = TRUE;
- } else {
- if (read_timer_seconds(blink_timer) > blink_time) {
- toggle_focus_unit_state(tileset);
- need_update = TRUE;
- }
- }
- if (need_update) {
+ if (get_num_units_in_focus() > 0) {
+ if (!blink_timer || read_timer_seconds(blink_timer) > blink_time) {
+ toggle_focus_unit_state(tileset);
+
/* If we lag, we don't try to catch up. Instead we just start a
* new blink_time on every update. */
blink_timer = renew_timer_start(blink_timer, TIMER_USER, TIMER_ACTIVE);
- refresh_unit_mapcanvas(punit, punit->tile, FALSE, TRUE);
+ unit_list_iterate(get_units_in_focus(), punit) {
+ /* We flush to screen directly here. This is most likely faster
+ * since these drawing operations are all small but may be spread
+ * out widely. */
+ refresh_unit_mapcanvas(punit, punit->tile, FALSE, TRUE);
+ } unit_list_iterate_end;
}
return blink_time - read_timer_seconds(blink_timer);
@@ -501,18 +657,21 @@
be enough information to know whether to redraw -- instead redraw every
time. (Could store enough info to know, but is it worth it?)
**************************************************************************/
-void update_unit_pix_label(struct unit *punit)
+void update_unit_pix_label(struct unit_list *punitlist)
{
int i;
/* Check for any change in the unit's state. This assumes that a unit's
* orders cannot be changed directly but must be removed and then reset. */
- if (punit && get_client_state() != CLIENT_GAME_OVER_STATE) {
+ if (unit_list_size(punitlist) > 0
+ && get_client_state() != CLIENT_GAME_OVER_STATE) {
/* There used to be a complicated and bug-prone check here to see if
* the unit had actually changed. This was misguided since the stacked
* units (below) are redrawn in any case. Unless we write a general
* system for unit updates here we might as well just redraw it every
* time. */
+ struct unit *punit = unit_list_get(punitlist, 0);
+
set_unit_icon(-1, punit);
i = 0; /* index into unit_below_canvas */
@@ -550,7 +709,8 @@
punit_attacking = pattacker;
punit_defending = pdefender;
- if (punit_attacking == punit_focus || punit_defending == punit_focus) {
+ if (unit_is_in_focus(punit_attacking)
+ || unit_is_in_focus(punit_defending)) {
/* If one of the units is the focus unit, make sure hidden-focus is
* disabled. We don't just do this as a check later because then
* with a blinking unit it would just disappear again right after the
@@ -675,15 +835,15 @@
**************************************************************************/
void request_unit_goto(enum unit_orders last_order)
{
- struct unit *punit = punit_focus;
+ struct unit_list *punits = get_units_in_focus();
- if (!punit)
+ if (unit_list_size(punits) == 0)
return;
if (hover_state != HOVER_GOTO) {
- set_hover_state(punit, HOVER_GOTO, ACTIVITY_LAST, last_order);
- update_unit_info_label(punit);
- enter_goto_state(punit);
+ set_hover_state(punits, HOVER_GOTO, ACTIVITY_LAST, last_order);
+ update_unit_info_label(punits);
+ enter_goto_state(punits);
create_line_at_mouse_pos();
} else {
assert(goto_is_active());
@@ -761,16 +921,17 @@
**************************************************************************/
void request_unit_connect(enum unit_activity activity)
{
- if (!punit_focus || !can_unit_do_connect(punit_focus, activity)) {
+ if (!can_units_do_connect(get_units_in_focus(), activity)) {
return;
}
if (hover_state != HOVER_CONNECT || connect_activity != activity) {
/* Enter or change the hover connect state. */
- set_hover_state(punit_focus, HOVER_CONNECT, activity, ORDER_LAST);
- update_unit_info_label(punit_focus);
+ set_hover_state(get_units_in_focus(), HOVER_CONNECT,
+ activity, ORDER_LAST);
+ update_unit_info_label(get_units_in_focus());
- enter_goto_state(punit_focus);
+ enter_goto_state(get_units_in_focus());
create_line_at_mouse_pos();
} else {
assert(goto_is_active());
@@ -806,6 +967,7 @@
}
} unit_list_iterate_end;
+
if (plast) {
/* Unfocus the ship, and advance the focus to the last unloaded unit.
* If there is no unit unloaded (which shouldn't happen, but could if
@@ -857,6 +1019,25 @@
wakeup_sentried_units(punit->tile);
}
+/****************************************************************************
+ Select all units of the same type as the given unit.
+****************************************************************************/
+void request_unit_select_same_type(struct unit_list *punits)
+{
+ if (can_client_change_view()) {
+ unit_list_iterate(punits, punit) {
+ unit_list_iterate(unit_owner(punit)->units, punit2) {
+ if (punit2->type == punit->type
+ && !unit_list_search(punits, punit2)
+ && punit2->activity == ACTIVITY_IDLE
+ && !unit_has_orders(punit2)) {
+ add_unit_focus(punit2);
+ }
+ } unit_list_iterate_end;
+ } unit_list_iterate_end;
+ }
+}
+
/**************************************************************************
Request a diplomat to do a specific action.
- action : The action to be requested.
@@ -1065,36 +1246,59 @@
/**************************************************************************
Explode nuclear at a tile without enemy units
**************************************************************************/
-void request_unit_nuke(struct unit *punit)
+void request_unit_nuke(struct unit_list *punits)
{
- if(!unit_flag(punit, F_NUCLEAR)) {
- create_event(punit->tile, E_BAD_COMMAND,
- _("Only nuclear units can do this."));
+ bool can = FALSE;
+ struct tile *offender = NULL;
+
+ if (unit_list_size(punits) == 0) {
return;
}
- if(punit->moves_left == 0)
- do_unit_nuke(punit);
- else {
- set_hover_state(punit, HOVER_NUKE, ACTIVITY_LAST, ORDER_LAST);
- update_unit_info_label(punit);
+ unit_list_iterate(punits, punit) {
+ if (unit_flag(punit, F_NUCLEAR)) {
+ can = TRUE;
+ break;
+ }
+ if (!offender) { /* Take first offender tile/unit */
+ offender = punit->tile;
+ }
+ } unit_list_iterate_end;
+ if (can) {
+ set_hover_state(punits, HOVER_NUKE, ACTIVITY_LAST, ORDER_LAST);
+ update_unit_info_label(punits);
+ } else {
+ create_event(offender, E_BAD_COMMAND,
+ _("Only nuclear units can do this."));
}
}
/**************************************************************************
...
**************************************************************************/
-void request_unit_paradrop(struct unit *punit)
+void request_unit_paradrop(struct unit_list *punits)
{
- if(!unit_flag(punit, F_PARATROOPERS)) {
- create_event(punit->tile, E_BAD_COMMAND,
- _("Only paratrooper units can do this."));
+ bool can = FALSE;
+ struct tile *offender = NULL;
+
+ if (unit_list_size(punits) == 0) {
return;
}
- if(!can_unit_paradrop(punit))
- return;
-
- set_hover_state(punit, HOVER_PARADROP, ACTIVITY_LAST, ORDER_LAST);
- update_unit_info_label(punit);
+ unit_list_iterate(punits, punit) {
+ if (can_unit_paradrop(punit)) {
+ can = TRUE;
+ break;
+ }
+ if (!offender) { /* Take first offender tile/unit */
+ offender = punit->tile;
+ }
+ } unit_list_iterate_end;
+ if (can) {
+ set_hover_state(punits, HOVER_PARADROP, ACTIVITY_LAST, ORDER_LAST);
+ update_unit_info_label(punits);
+ } else {
+ create_event(offender, E_BAD_COMMAND,
+ _("Only paratrooper units can do this."));
+ }
}
/**************************************************************************
@@ -1102,15 +1306,16 @@
**************************************************************************/
void request_unit_patrol(void)
{
- struct unit *punit = punit_focus;
+ struct unit_list *punits = get_units_in_focus();
- if (!punit)
+ if (unit_list_size(punits) == 0) {
return;
+ }
if (hover_state != HOVER_PATROL) {
- set_hover_state(punit, HOVER_PATROL, ACTIVITY_LAST, ORDER_LAST);
- update_unit_info_label(punit);
- enter_goto_state(punit);
+ set_hover_state(punits, HOVER_PATROL, ACTIVITY_LAST, ORDER_LAST);
+ update_unit_info_label(punits);
+ enter_goto_state(punits);
create_line_at_mouse_pos();
} else {
assert(goto_is_active());
@@ -1407,20 +1612,23 @@
**************************************************************************/
void request_center_focus_unit(void)
{
- if (punit_focus) {
- center_tile_mapcanvas(punit_focus->tile);
+ struct tile *ptile = find_a_focus_unit_tile_to_center_on();
+
+ if (ptile) {
+ center_tile_mapcanvas(ptile);
}
}
/**************************************************************************
...
**************************************************************************/
-void request_unit_wait(struct unit *punit)
+void request_units_wait(struct unit_list *punits)
{
- punit->focus_status=FOCUS_WAIT;
- if (punit == punit_focus) {
+ unit_list_iterate(punits, punit) {
+ punit->focus_status = FOCUS_WAIT;
+ } unit_list_iterate_end;
+ if (punits == get_units_in_focus()) {
advance_unit_focus();
- /* set_unit_focus(punit_focus); */ /* done in advance_unit_focus */
}
}
@@ -1429,10 +1637,11 @@
**************************************************************************/
void request_unit_move_done(void)
{
- if (punit_focus) {
- punit_focus->focus_status = FOCUS_DONE;
+ if (get_num_units_in_focus() > 0) {
+ unit_list_iterate(get_units_in_focus(), punit) {
+ punit->focus_status = FOCUS_DONE;
+ } unit_list_iterate_end;
advance_unit_focus();
- /* set_unit_focus(punit_focus); */ /* done in advance_unit_focus */
}
}
@@ -1504,7 +1713,9 @@
update_city_description(dst_tile->city);
}
- if (punit_focus == punit) update_menus();
+ if (unit_is_in_focus(punit)) {
+ update_menus();
+ }
}
/**************************************************************************
@@ -1513,10 +1724,12 @@
void do_map_click(struct tile *ptile, enum quickselect_type qtype)
{
struct city *pcity = tile_get_city(ptile);
- struct unit *punit = player_find_unit_by_id(game.player_ptr, hover_unit);
+ struct unit_list *punits = hover_units;
bool maybe_goto = FALSE;
+ bool possible = FALSE;
+ struct tile *offender = NULL;
- if (punit && hover_state != HOVER_NONE) {
+ if (unit_list_size(punits) > 0 && hover_state != HOVER_NONE) {
switch (hover_state) {
case HOVER_NONE:
die("well; shouldn't get here :)");
@@ -1524,32 +1737,44 @@
do_unit_goto(ptile);
break;
case HOVER_NUKE:
- if (SINGLE_MOVE * real_map_distance(punit->tile, ptile)
- > punit->moves_left) {
- create_event(punit->tile, E_BAD_COMMAND, _("Too far for this unit."));
+ unit_list_iterate(punits, punit) {
+ if (SINGLE_MOVE * real_map_distance(punit->tile, ptile)
+ <= punit->moves_left) {
+ possible = TRUE;
+ break;
+ }
+ offender = punit->tile;
+ } unit_list_iterate_end;
+ if (!possible) {
+ create_event(offender, E_BAD_COMMAND, _("Too far for this unit."));
} else {
do_unit_goto(ptile);
- /* note that this will be executed by the server after the goto */
- if (!pcity)
- do_unit_nuke(punit);
+ if (!pcity) {
+ unit_list_iterate(punits, punit) {
+ /* note that this will be executed by the server after the goto */
+ do_unit_nuke(punit);
+ } unit_list_iterate_end;
+ }
}
break;
case HOVER_PARADROP:
- do_unit_paradrop_to(punit, ptile);
+ unit_list_iterate(punits, punit) {
+ do_unit_paradrop_to(punit, ptile);
+ } unit_list_iterate_end;
break;
case HOVER_CONNECT:
- do_unit_connect(punit, ptile, connect_activity);
+ do_unit_connect(ptile, connect_activity);
break;
case HOVER_PATROL:
- do_unit_patrol_to(punit, ptile);
+ do_unit_patrol_to(ptile);
break;
}
set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
- update_unit_info_label(punit);
+ update_unit_info_label(get_units_in_focus());
}
/* Bypass stack or city popup if quickselect is specified. */
- else if (qtype) {
+ else if (qtype != SELECT_POPUP && qtype != SELECT_APPEND) {
struct unit *qunit = quickselect(ptile, qtype);
if (qunit) {
set_unit_focus_and_select(qunit);
@@ -1561,7 +1786,7 @@
popup_city_dialog(pcity);
}
else if (unit_list_size(ptile->units) == 0 && !pcity
- && punit_focus) {
+ && get_num_units_in_focus() > 0) {
maybe_goto = keyboardless_goto;
}
else if (unit_list_size(ptile->units) == 1
@@ -1571,7 +1796,11 @@
if (game.player_ptr == punit->owner) {
if(can_unit_do_activity(punit, ACTIVITY_IDLE)) {
maybe_goto = keyboardless_goto;
- set_unit_focus_and_select(punit);
+ if (qtype == SELECT_APPEND) {
+ add_unit_focus(punit);
+ } else {
+ set_unit_focus_and_select(punit);
+ }
}
} else if (pcity) {
/* Don't hide the unit in the city. */
@@ -1630,10 +1859,10 @@
* Any unit
*/
- unit_list_iterate(ptile->units, punit) {
- if (game.player_ptr != punit->owner || punit == punit_focus) {
- continue;
- }
+ unit_list_iterate(ptile->units, punit) {
+ if (game.player_ptr != punit->owner || unit_is_in_focus(punit)) {
+ continue;
+ }
if (qtype == SELECT_SEA) {
/* Transporter. */
if (get_transporter_capacity(punit)) {
@@ -1718,22 +1947,19 @@
**************************************************************************/
void do_unit_goto(struct tile *ptile)
{
- struct unit *punit = player_find_unit_by_id(game.player_ptr, hover_unit);
+ struct tile *dest_tile;
- if (hover_unit == 0 || hover_state != HOVER_GOTO)
+ if (hover_state != HOVER_GOTO) {
return;
+ }
- if (punit) {
- struct tile *dest_tile;
-
- draw_line(ptile);
- dest_tile = get_line_dest();
- if (ptile == dest_tile) {
- send_goto_route(punit);
- } else {
- create_event(punit->tile, E_BAD_COMMAND,
- _("Didn't find a route to the destination!"));
- }
+ draw_line(ptile);
+ dest_tile = get_line_dest();
+ if (ptile == dest_tile) {
+ send_goto_route();
+ } else {
+ create_event(ptile, E_BAD_COMMAND,
+ _("Didn't find a route to the destination!"));
}
set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
@@ -1758,17 +1984,17 @@
/**************************************************************************
Patrol to a location.
**************************************************************************/
-void do_unit_patrol_to(struct unit *punit, struct tile *ptile)
+void do_unit_patrol_to(struct tile *ptile)
{
struct tile *dest_tile;
draw_line(ptile);
dest_tile = get_line_dest();
if (ptile == dest_tile
- && !is_non_allied_unit_tile(ptile, unit_owner(punit))) {
- send_patrol_route(punit);
+ && !is_non_allied_unit_tile(ptile, game.player_ptr)) {
+ send_patrol_route();
} else {
- create_event(punit->tile, E_BAD_COMMAND,
+ create_event(dest_tile, E_BAD_COMMAND,
_("Didn't find a route to the destination!"));
}
@@ -1778,23 +2004,18 @@
/**************************************************************************
"Connect" to the given location.
**************************************************************************/
-void do_unit_connect(struct unit *punit, struct tile *ptile,
+void do_unit_connect(struct tile *ptile,
enum unit_activity activity)
{
- if (is_air_unit(punit)) {
- create_event(punit->tile, E_BAD_COMMAND,
- _("Sorry, airunit connect not yet implemented."));
+ struct tile *dest_tile;
+
+ draw_line(ptile);
+ dest_tile = get_line_dest();
+ if (same_pos(dest_tile, ptile)) {
+ send_connect_route(activity);
} else {
- struct tile *dest_tile;
-
- draw_line(ptile);
- dest_tile = get_line_dest();
- if (same_pos(dest_tile, ptile)) {
- send_connect_route(punit, activity);
- } else {
- create_event(punit->tile, E_BAD_COMMAND,
- _("Didn't find a route to the destination!"));
- }
+ create_event(ptile, E_BAD_COMMAND,
+ _("Didn't find a route to the destination!"));
}
set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
@@ -1814,10 +2035,8 @@
}
if (hover_state != HOVER_NONE && !popped) {
- struct unit *punit = player_find_unit_by_id(game.player_ptr, hover_unit);
-
set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
- update_unit_info_label(punit);
+ update_unit_info_label(hover_units);
keyboardless_goto_button_down = FALSE;
keyboardless_goto_active = FALSE;
@@ -1855,11 +2074,19 @@
**************************************************************************/
void key_recall_previous_focus_unit(void)
{
- struct unit *punit = player_find_unit_by_id(game.player_ptr,
- previous_focus_id);
- if (punit) {
- set_unit_focus_and_select(punit);
- }
+ int i = 0;
+
+ /* Could use unit_list_copy here instead. Just having safe genlists
+ * wouldn't be sufficient since we don't want to skip units already
+ * removed from focus... */
+ unit_list_iterate_safe(previous_focus, punit) {
+ if (i == 0) {
+ set_unit_focus(punit);
+ } else {
+ add_unit_focus(punit);
+ }
+ i++;
+ } unit_list_iterate_safe_end;
}
/**************************************************************************
@@ -1868,10 +2095,11 @@
**************************************************************************/
void key_unit_move(enum direction8 gui_dir)
{
- if (punit_focus) {
+ unit_list_iterate(get_units_in_focus(), punit) {
enum direction8 map_dir = gui_to_map_dir(gui_dir);
- request_move_unit_direction(punit_focus, map_dir);
- }
+
+ request_move_unit_direction(punit, map_dir);
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -1879,9 +2107,9 @@
**************************************************************************/
void key_unit_build_city(void)
{
- if (punit_focus) {
- request_unit_build_city(punit_focus);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ request_unit_build_city(punit);
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -1889,9 +2117,11 @@
**************************************************************************/
void key_unit_build_wonder(void)
{
- if (punit_focus && unit_flag(punit_focus, F_HELP_WONDER)) {
- request_unit_caravan_action(punit_focus, PACKET_UNIT_HELP_BUILD_WONDER);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (unit_flag(punit, F_HELP_WONDER)) {
+ request_unit_caravan_action(punit, PACKET_UNIT_HELP_BUILD_WONDER);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -1899,9 +2129,7 @@
**************************************************************************/
void key_unit_connect(enum unit_activity activity)
{
- if (punit_focus) {
- request_unit_connect(activity);
- }
+ request_unit_connect(activity);
}
/**************************************************************************
@@ -1910,13 +2138,17 @@
void key_unit_diplomat_actions(void)
{
struct city *pcity; /* need pcity->id */
- if (punit_focus
- && is_diplomat_unit(punit_focus)
- && (pcity = tile_get_city(punit_focus->tile))
- && diplomat_handled_in_diplomat_dialog() != -1 /* confusing otherwise?
*/
- && diplomat_can_do_action(punit_focus, DIPLOMAT_ANY_ACTION,
- punit_focus->tile))
- process_diplomat_arrival(punit_focus, pcity->id);
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (is_diplomat_unit(punit)
+ && (pcity = tile_get_city(punit->tile))
+ && diplomat_handled_in_diplomat_dialog() != -1 /* confusing
otherwise? */
+ && diplomat_can_do_action(punit, DIPLOMAT_ANY_ACTION,
+ punit->tile)) {
+ process_diplomat_arrival(punit, pcity->id);
+ return;
+ /* FIXME: diplomat dialog for more than one unit at a time. */
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -1924,9 +2156,7 @@
**************************************************************************/
void key_unit_done(void)
{
- if (punit_focus) {
- request_unit_move_done();
- }
+ request_unit_move_done();
}
/**************************************************************************
@@ -1934,9 +2164,7 @@
**************************************************************************/
void key_unit_goto(void)
{
- if (punit_focus) {
- request_unit_goto(ORDER_LAST);
- }
+ request_unit_goto(ORDER_LAST);
}
/**************************************************************************
@@ -1944,9 +2172,7 @@
**************************************************************************/
void key_unit_nuke(void)
{
- if (punit_focus) {
- request_unit_nuke(punit_focus);
- }
+ request_unit_nuke(get_units_in_focus());
}
/**************************************************************************
@@ -1954,9 +2180,7 @@
**************************************************************************/
void key_unit_paradrop(void)
{
- if (punit_focus && can_unit_paradrop(punit_focus)) {
- request_unit_paradrop(punit_focus);
- }
+ request_unit_paradrop(get_units_in_focus());
}
/**************************************************************************
@@ -1964,9 +2188,7 @@
**************************************************************************/
void key_unit_patrol(void)
{
- if (punit_focus) {
- request_unit_patrol();
- }
+ request_unit_patrol();
}
/**************************************************************************
@@ -1974,9 +2196,11 @@
**************************************************************************/
void key_unit_traderoute(void)
{
- if (punit_focus && unit_flag(punit_focus, F_TRADE_ROUTE)) {
- request_unit_caravan_action(punit_focus, PACKET_UNIT_ESTABLISH_TRADE);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (unit_flag(punit, F_TRADE_ROUTE)) {
+ request_unit_caravan_action(punit, PACKET_UNIT_ESTABLISH_TRADE);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -1984,9 +2208,9 @@
**************************************************************************/
void key_unit_unload_all(void)
{
- if (punit_focus) {
- request_unit_unload_all(punit_focus);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ request_unit_unload_all(punit);
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -1994,9 +2218,7 @@
**************************************************************************/
void key_unit_wait(void)
{
- if (punit_focus) {
- request_unit_wait(punit_focus);
- }
+ request_units_wait(get_units_in_focus());
}
/**************************************************************************
@@ -2004,9 +2226,9 @@
***************************************************************************/
void key_unit_wakeup_others(void)
{
- if (punit_focus) {
- request_unit_wakeup(punit_focus);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ request_unit_wakeup(punit);
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2014,10 +2236,11 @@
**************************************************************************/
void key_unit_airbase(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_AIRBASE)) {
- request_new_unit_activity(punit_focus, ACTIVITY_AIRBASE);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_AIRBASE)) {
+ request_new_unit_activity(punit, ACTIVITY_AIRBASE);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2025,10 +2248,11 @@
**************************************************************************/
void key_unit_auto_explore(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_EXPLORE)) {
- request_new_unit_activity(punit_focus, ACTIVITY_EXPLORE);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_EXPLORE)) {
+ request_new_unit_activity(punit, ACTIVITY_EXPLORE);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2037,9 +2261,11 @@
**************************************************************************/
void key_unit_auto_settle(void)
{
- if (punit_focus && can_unit_do_autosettlers(punit_focus)) {
- request_unit_autosettlers(punit_focus);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_autosettlers(punit)) {
+ request_unit_autosettlers(punit);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2047,9 +2273,9 @@
**************************************************************************/
void key_unit_disband(void)
{
- if (punit_focus) {
- request_unit_disband(punit_focus);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ request_unit_disband(punit);
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2057,10 +2283,11 @@
**************************************************************************/
void key_unit_fallout(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_FALLOUT)) {
- request_new_unit_activity(punit_focus, ACTIVITY_FALLOUT);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_FALLOUT)) {
+ request_new_unit_activity(punit, ACTIVITY_FALLOUT);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2068,10 +2295,11 @@
**************************************************************************/
void key_unit_fortify(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_FORTIFYING)) {
- request_new_unit_activity(punit_focus, ACTIVITY_FORTIFYING);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_FORTIFYING)) {
+ request_new_unit_activity(punit, ACTIVITY_FORTIFYING);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2079,10 +2307,11 @@
**************************************************************************/
void key_unit_fortress(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_FORTRESS)) {
- request_new_unit_activity(punit_focus, ACTIVITY_FORTRESS);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_FORTRESS)) {
+ request_new_unit_activity(punit, ACTIVITY_FORTRESS);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2090,9 +2319,9 @@
**************************************************************************/
void key_unit_homecity(void)
{
- if (punit_focus) {
- request_unit_change_homecity(punit_focus);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ request_unit_change_homecity(punit);
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2100,10 +2329,11 @@
**************************************************************************/
void key_unit_irrigate(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_IRRIGATE)) {
- request_new_unit_activity(punit_focus, ACTIVITY_IRRIGATE);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_IRRIGATE)) {
+ request_new_unit_activity(punit, ACTIVITY_IRRIGATE);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2111,10 +2341,11 @@
**************************************************************************/
void key_unit_mine(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_MINE)) {
- request_new_unit_activity(punit_focus, ACTIVITY_MINE);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_MINE)) {
+ request_new_unit_activity(punit, ACTIVITY_MINE);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2122,10 +2353,11 @@
**************************************************************************/
void key_unit_pillage(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_PILLAGE)) {
- request_unit_pillage(punit_focus);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_PILLAGE)) {
+ request_unit_pillage(punit);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2133,10 +2365,11 @@
**************************************************************************/
void key_unit_pollution(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_POLLUTION)) {
- request_new_unit_activity(punit_focus, ACTIVITY_POLLUTION);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_POLLUTION)) {
+ request_new_unit_activity(punit, ACTIVITY_POLLUTION);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2144,12 +2377,13 @@
**************************************************************************/
void key_unit_road(void)
{
- if (punit_focus) {
- if(can_unit_do_activity(punit_focus, ACTIVITY_ROAD))
- request_new_unit_activity(punit_focus, ACTIVITY_ROAD);
- else if(can_unit_do_activity(punit_focus, ACTIVITY_RAILROAD))
- request_new_unit_activity(punit_focus, ACTIVITY_RAILROAD);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_ROAD)) {
+ request_new_unit_activity(punit, ACTIVITY_ROAD);
+ } else if (can_unit_do_activity(punit, ACTIVITY_RAILROAD)) {
+ request_new_unit_activity(punit, ACTIVITY_RAILROAD);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2157,10 +2391,11 @@
**************************************************************************/
void key_unit_sentry(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_SENTRY)) {
- request_new_unit_activity(punit_focus, ACTIVITY_SENTRY);
- }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_SENTRY)) {
+ request_new_unit_activity(punit, ACTIVITY_SENTRY);
+ }
+ } unit_list_iterate_end;
}
/**************************************************************************
@@ -2168,12 +2403,76 @@
**************************************************************************/
void key_unit_transform(void)
{
- if (punit_focus &&
- can_unit_do_activity(punit_focus, ACTIVITY_TRANSFORM)) {
- request_new_unit_activity(punit_focus, ACTIVITY_TRANSFORM);
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (can_unit_do_activity(punit, ACTIVITY_TRANSFORM)) {
+ request_new_unit_activity(punit, ACTIVITY_TRANSFORM);
+ }
+ } unit_list_iterate_end;
+}
+
+/****************************************************************************
+ Assign all focus units to this battlegroup.
+****************************************************************************/
+void key_unit_assign_battlegroup(int battlegroup, bool append)
+{
+ if (game.player_ptr && can_client_issue_orders()
+ && battlegroups >= 0 && battlegroup < MAX_NUM_BATTLEGROUPS) {
+ if (!append) {
+ unit_list_iterate_safe(battlegroups[battlegroup], punit) {
+ if (!unit_is_in_focus(punit)) {
+ punit->battlegroup = BATTLEGROUP_NONE;
+ dsend_packet_unit_battlegroup(&aconnection,
+ punit->id, BATTLEGROUP_NONE);
+ refresh_unit_mapcanvas(punit, punit->tile, TRUE, FALSE);
+ unit_list_unlink(battlegroups[battlegroup], punit);
+ }
+ } unit_list_iterate_safe_end;
+ }
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (punit->battlegroup != battlegroup) {
+ if (punit->battlegroup >= 0
+ && punit->battlegroup < MAX_NUM_BATTLEGROUPS) {
+ unit_list_unlink(battlegroups[punit->battlegroup], punit);
+ }
+ punit->battlegroup = battlegroup;
+ dsend_packet_unit_battlegroup(&aconnection,
+ punit->id, battlegroup);
+ unit_list_append(battlegroups[battlegroup], punit);
+ refresh_unit_mapcanvas(punit, punit->tile, TRUE, FALSE);
+ }
+ } unit_list_iterate_end;
+ unit_list_iterate(battlegroups[battlegroup], punit) {
+ add_unit_focus(punit);
+ } unit_list_iterate_end;
}
}
+/****************************************************************************
+ Bring this battlegroup into focus.
+****************************************************************************/
+void key_unit_select_battlegroup(int battlegroup, bool append)
+{
+ if (game.player_ptr && can_client_change_view()
+ && battlegroups >= 0 && battlegroup < MAX_NUM_BATTLEGROUPS) {
+ int i = 0;
+
+ if (unit_list_size(battlegroups[battlegroup]) == 0 && !append) {
+ set_unit_focus(NULL);
+ return;
+ }
+
+ /* FIXME: this is very inefficient and can be improved. */
+ unit_list_iterate(battlegroups[battlegroup], punit) {
+ if (i == 0 && !append) {
+ set_unit_focus(punit);
+ } else {
+ add_unit_focus(punit);
+ }
+ i++;
+ } unit_list_iterate_end;
+ }
+}
+
/**************************************************************************
Toggle drawing of city outlines.
**************************************************************************/
@@ -2324,10 +2623,9 @@
**************************************************************************/
void key_quickselect(enum quickselect_type qtype)
{
- struct unit *punit;
+ unit_list_iterate(get_units_in_focus(), punit) {
+ struct unit *punit2 = quickselect(punit->tile, qtype);
- if(punit_focus) {
- punit = quickselect(punit_focus->tile, qtype);
- set_unit_focus_and_select(punit);
- }
+ set_unit_focus_and_select(punit2);
+ } unit_list_iterate_end;
}
Index: client/gui-gtk-2.0/gotodlg.c
===================================================================
--- client/gui-gtk-2.0/gotodlg.c (revision 11168)
+++ client/gui-gtk-2.0/gotodlg.c (working copy)
@@ -82,11 +82,9 @@
struct city *pdestcity = get_selected_city();
if (pdestcity) {
- struct unit *punit = get_unit_in_focus();
-
- if (punit) {
+ unit_list_iterate(get_units_in_focus(), punit) {
request_unit_airlift(punit, pdestcity);
- }
+ } unit_list_iterate_end;
}
}
break;
@@ -96,11 +94,9 @@
struct city *pdestcity = get_selected_city();
if (pdestcity) {
- struct unit *punit = get_unit_in_focus();
-
- if (punit) {
+ unit_list_iterate(get_units_in_focus(), punit) {
send_goto_tile(punit, pdestcity->tile);
- }
+ } unit_list_iterate_end;
}
}
break;
@@ -206,7 +202,7 @@
*****************************************************************/
void popup_goto_dialog(void)
{
- if (!can_client_issue_orders() || !get_unit_in_focus()) {
+ if (!can_client_issue_orders() || get_num_units_in_focus() == 0) {
return;
}
@@ -270,9 +266,17 @@
struct city *pdestcity;
if((pdestcity = get_selected_city())) {
- struct unit *punit = get_unit_in_focus();
+ bool can_airlift = FALSE;
+
+ unit_list_iterate(get_units_in_focus(), punit) {
+ if (unit_can_airlift_to(punit, pdestcity)) {
+ can_airlift = TRUE;
+ break;
+ }
+ } unit_list_iterate_end;
+
center_tile_mapcanvas(pdestcity->tile);
- if(punit && unit_can_airlift_to(punit, pdestcity)) {
+ if (can_airlift) {
gtk_dialog_set_response_sensitive(GTK_DIALOG(dshell), CMD_AIRLIFT, TRUE);
return;
}
Index: client/gui-gtk-2.0/mapview.c
===================================================================
--- client/gui-gtk-2.0/mapview.c (revision 11168)
+++ client/gui-gtk-2.0/mapview.c (working copy)
@@ -166,21 +166,35 @@
Also calls update_unit_pix_label() to update the icons for units on this
square.
**************************************************************************/
-void update_unit_info_label(struct unit *punit)
+void update_unit_info_label(struct unit_list *punitlist)
{
GtkWidget *label;
label = gtk_frame_get_label_widget(GTK_FRAME(unit_info_frame));
gtk_label_set_text(GTK_LABEL(label),
- get_unit_info_label_text1(punit));
+ get_unit_info_label_text1(punitlist));
gtk_label_set_text(GTK_LABEL(unit_info_label),
- get_unit_info_label_text2(punit));
+ get_unit_info_label_text2(punitlist));
- if(punit) {
- if (hover_unit != punit->id)
- set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
+ if (unit_list_size(punitlist) > 0) {
+#if 0 /* I sure as hell hope this hack isn't needed */
+ if (unit_list_size(hover_units) > 0) {
+ bool is_hover = FALSE;
+ unit_list_iterate(hover_units, punit2) {
+ if (punit2 == punit) {
+ is_hover = TRUE;
+ break;
+ }
+ } unit_list_iterate_end;
+
+ if (!is_hover) {
+ set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
+ }
+ }
+#endif
+
switch (hover_state) {
case HOVER_NONE:
gdk_window_set_cursor (root_window, NULL);
@@ -202,7 +216,7 @@
} else {
gdk_window_set_cursor(root_window, NULL);
}
- update_unit_pix_label(punit);
+ update_unit_pix_label(punitlist);
}
Index: client/gui-gtk-2.0/gui_main.c
===================================================================
--- client/gui-gtk-2.0/gui_main.c (revision 11168)
+++ client/gui-gtk-2.0/gui_main.c (working copy)
@@ -440,7 +440,57 @@
}
}
+ assert(MAX_NUM_BATTLEGROUPS == 4);
+#define BATTLEGROUP_CASE(num) \
+ if (ev->state & GDK_CONTROL_MASK) { \
+ key_unit_assign_battlegroup((num), (ev->state & GDK_SHIFT_MASK) != 0); \
+ } else { \
+ key_unit_select_battlegroup((num), (ev->state & GDK_SHIFT_MASK) != 0); \
+ }
+#define BATTLEGROUP_SHIFT_CASE(num) \
+ if (ev->state & GDK_CONTROL_MASK) { \
+ key_unit_assign_battlegroup((num), TRUE); \
+ } else { \
+ key_unit_select_battlegroup((num), TRUE); \
+ }
+
switch (ev->keyval) {
+ case GDK_1:
+ BATTLEGROUP_CASE(0);
+ break;
+
+ case GDK_2:
+ BATTLEGROUP_CASE(1);
+ break;
+
+ case GDK_3:
+ BATTLEGROUP_CASE(2);
+ break;
+
+ case GDK_4:
+ BATTLEGROUP_CASE(3);
+ break;
+
+ case GDK_exclam:
+ /* Shift + 1 */
+ BATTLEGROUP_SHIFT_CASE(0);
+ break;
+
+ case GDK_at:
+ /* Shift + 2 */
+ BATTLEGROUP_SHIFT_CASE(1);
+ break;
+
+ case GDK_numbersign:
+ /* Shift + 3 */
+ BATTLEGROUP_SHIFT_CASE(2);
+ break;
+
+ case GDK_dollar:
+ /* Shift + 4 */
+ BATTLEGROUP_SHIFT_CASE(3);
+ break;
+
case GDK_KP_Up:
case GDK_8:
case GDK_KP_8:
@@ -460,25 +510,21 @@
break;
case GDK_KP_Page_Down:
- case GDK_3:
case GDK_KP_3:
key_unit_move(DIR8_SOUTHEAST);
break;
case GDK_KP_Down:
- case GDK_2:
case GDK_KP_2:
key_unit_move(DIR8_SOUTH);
break;
case GDK_KP_End:
- case GDK_1:
case GDK_KP_1:
key_unit_move(DIR8_SOUTHWEST);
break;
case GDK_KP_Left:
- case GDK_4:
case GDK_KP_4:
key_unit_move(DIR8_WEST);
break;
@@ -691,8 +737,12 @@
* but we have to make the *internal* state consistent). */
gtk_widget_hide(more_arrow_pixmap);
set_unit_icons_more_arrow(FALSE);
- set_unit_icon(-1, get_unit_in_focus());
- update_unit_pix_label(get_unit_in_focus());
+ if (get_num_units_in_focus() == 1) {
+ set_unit_icon(-1, unit_list_get(get_units_in_focus(), 0));
+ } else {
+ set_unit_icon(-1, NULL);
+ }
+ update_unit_pix_label(get_units_in_focus());
}
/**************************************************************************
@@ -1488,7 +1538,7 @@
struct unit *punit;
if (i == -1) {
- if ((punit = get_unit_in_focus())) {
+ if (unit_is_in_focus(punit)) {
/* Clicking on the currently selected unit will center it. */
center_tile_mapcanvas(punit->tile);
}
Index: client/gui-gtk-2.0/menu.c
===================================================================
--- client/gui-gtk-2.0/menu.c (revision 11168)
+++ client/gui-gtk-2.0/menu.c (working copy)
@@ -143,6 +143,7 @@
MENU_ORDER_DISBAND,
MENU_ORDER_DIPLOMAT_DLG,
MENU_ORDER_NUKE,
+ MENU_ORDER_SELECT_SAME_TYPE,
MENU_ORDER_WAIT,
MENU_ORDER_DONE,
@@ -395,25 +396,31 @@
guint callback_action, GtkWidget *widget)
{
switch(callback_action) {
- case MENU_ORDER_BUILD_CITY:
- if (get_unit_in_focus()) {
- struct unit *punit = get_unit_in_focus();
+ case MENU_ORDER_SELECT_SAME_TYPE:
+ request_unit_select_same_type(get_units_in_focus());
+ break;
+ case MENU_ORDER_BUILD_CITY:
+ unit_list_iterate(get_units_in_focus(), punit) {
+ /* FIXME: this can provide different actions for different units...
+ * not good! */
/* Enable the button for adding to a city in all cases, so we
get an eventual error message from the server if we try. */
if (can_unit_add_or_build_city(punit)) {
- key_unit_build_city();
+ request_unit_build_city(punit);
} else {
- key_unit_build_wonder();
+ request_unit_caravan_action(punit, PACKET_UNIT_HELP_BUILD_WONDER);
}
- }
+ } unit_list_iterate_end;
break;
- case MENU_ORDER_ROAD:
- if (get_unit_in_focus()) {
- if (unit_can_est_traderoute_here(get_unit_in_focus()))
+ case MENU_ORDER_ROAD:
+ unit_list_iterate(get_units_in_focus(), punit) {
+ /* FIXME: this can provide different actions for different units...
+ * not good! */
+ if (unit_can_est_traderoute_here(punit))
key_unit_traderoute();
else
key_unit_road();
- }
+ } unit_list_iterate_end;
break;
case MENU_ORDER_IRRIGATE:
key_unit_irrigate();
@@ -424,24 +431,28 @@
case MENU_ORDER_TRANSFORM:
key_unit_transform();
break;
- case MENU_ORDER_FORTRESS:
- if (get_unit_in_focus()) {
- if (can_unit_do_activity(get_unit_in_focus(), ACTIVITY_FORTRESS))
+ case MENU_ORDER_FORTRESS:
+ unit_list_iterate(get_units_in_focus(), punit) {
+ /* FIXME: this can provide different actions for different units...
+ * not good! */
+ if (can_unit_do_activity(punit, ACTIVITY_FORTRESS))
key_unit_fortress();
else
key_unit_fortify();
- }
+ } unit_list_iterate_end;
break;
case MENU_ORDER_AIRBASE:
key_unit_airbase();
break;
case MENU_ORDER_POLLUTION:
- if (get_unit_in_focus()) {
- if (can_unit_paradrop(get_unit_in_focus()))
+ unit_list_iterate(get_units_in_focus(), punit) {
+ /* FIXME: this can provide different actions for different units...
+ * not good! */
+ if (can_unit_paradrop(punit))
key_unit_paradrop();
else
key_unit_pollution();
- }
+ } unit_list_iterate_end;
break;
case MENU_ORDER_FALLOUT:
key_unit_fallout();
@@ -459,16 +470,22 @@
key_unit_unload_all();
break;
case MENU_ORDER_LOAD:
- request_unit_load(get_unit_in_focus(), NULL);
+ unit_list_iterate(get_units_in_focus(), punit) {
+ request_unit_load(punit, NULL);
+ } unit_list_iterate_end;
break;
case MENU_ORDER_UNLOAD:
- request_unit_unload(get_unit_in_focus());
+ unit_list_iterate(get_units_in_focus(), punit) {
+ request_unit_unload(punit);
+ } unit_list_iterate_end;
break;
case MENU_ORDER_WAKEUP_OTHERS:
key_unit_wakeup_others();
break;
- case MENU_ORDER_AUTO_SETTLER:
- request_unit_autosettlers(get_unit_in_focus());
+ case MENU_ORDER_AUTO_SETTLER:
+ unit_list_iterate(get_units_in_focus(), punit) {
+ request_unit_autosettlers(punit);
+ } unit_list_iterate_end;
break;
case MENU_ORDER_AUTO_EXPLORE:
key_unit_auto_explore();
@@ -491,15 +508,16 @@
case MENU_ORDER_GOTO:
key_unit_goto();
break;
- case MENU_ORDER_GOTO_CITY:
- if(get_unit_in_focus())
+ case MENU_ORDER_GOTO_CITY:
+ if (get_num_units_in_focus() > 0) {
popup_goto_dialog();
- break;
- case MENU_ORDER_RETURN:
- if (get_unit_in_focus()) {
- request_unit_return(get_unit_in_focus());
}
break;
+ case MENU_ORDER_RETURN:
+ unit_list_iterate(get_units_in_focus(), punit) {
+ request_unit_return(punit);
+ } unit_list_iterate_end;
+ break;
case MENU_ORDER_DISBAND:
key_unit_disband();
break;
@@ -853,6 +871,8 @@
orders_menu_callback, MENU_ORDER_NUKE
},
{ "/" N_("Orders") "/sep5", NULL,
NULL, 0,
"<Separator>" },
+ { "/" N_("Orders") "/" N_("Select same type"), "y",
+ orders_menu_callback, MENU_ORDER_SELECT_SAME_TYPE },
{ "/" N_("Orders") "/" N_("_Wait"), "w",
orders_menu_callback, MENU_ORDER_WAIT
},
{ "/" N_("Orders") "/" N_("Done"), "space",
@@ -1162,6 +1182,131 @@
return tile_get_info_text(&newtile);
}
+/****************************************************************************
+ Return TRUE if the function returns true for any of the units.
+****************************************************************************/
+static bool can_units_do(struct unit_list *punits,
+ bool (can_fn)(const struct unit *punit))
+{
+ unit_list_iterate(punits, punit) {
+ if (can_fn(punit)) {
+ return TRUE;
+ }
+ } unit_list_iterate_end;
+
+ return FALSE;
+}
+
+/****************************************************************************
+ Returns TRUE if any of the units can do the activity.
+****************************************************************************/
+static bool can_units_do_activity(struct unit_list *punits,
+ enum unit_activity activity)
+{
+ unit_list_iterate(punits, punit) {
+ if (can_unit_do_activity(punit, activity)) {
+ return TRUE;
+ }
+ } unit_list_iterate_end;
+
+ return FALSE;
+}
+
+/****************************************************************************
+ Returns TRUE if any of the units can do the activity.
+****************************************************************************/
+static bool can_units_do_diplomat_action(struct unit_list *punits,
+ enum diplomat_actions action)
+{
+ unit_list_iterate(punits, punit) {
+ if (is_diplomat_unit(punit)
+ && diplomat_can_do_action(punit, action, punit->tile)) {
+ return TRUE;
+ }
+ } unit_list_iterate_end;
+
+ return FALSE;
+}
+
+/****************************************************************************
+ If has_flag is true, returns true iff any of the units have the flag.
+
+ If has_flag is false, returns true iff any of the units don't have the
+ flag.
+****************************************************************************/
+static bool units_have_flag(struct unit_list *punits,
+ enum unit_flag_id flag,
+ bool has_flag)
+{
+ unit_list_iterate(punits, punit) {
+ if (EQ(has_flag, unit_flag(punit, flag))) {
+ return TRUE;
+ }
+ } unit_list_iterate_end;
+
+ return FALSE;
+}
+
+/****************************************************************************
+
+****************************************************************************/
+static bool units_are_occupied(struct unit_list *punits)
+{
+ unit_list_iterate(punits, punit) {
+ if (get_transporter_occupancy(punit) > 0) {
+ return TRUE;
+ }
+ } unit_list_iterate_end;
+
+ return FALSE;
+}
+
+/****************************************************************************
+ Return TRUE iff any of these units can load.
+****************************************************************************/
+static bool units_can_load(struct unit_list *punits)
+{
+ unit_list_iterate(punits, punit) {
+ if (can_unit_load(punit,
+ find_transporter_for_unit(punit, punit->tile))) {
+ return TRUE;
+ }
+ } unit_list_iterate_end;
+
+ return FALSE;
+}
+
+/****************************************************************************
+ Return TRUE iff any of these units can unload.
+****************************************************************************/
+static bool units_can_unload(struct unit_list *punits)
+{
+ unit_list_iterate(punits, punit) {
+ if (can_unit_unload(punit, find_unit_by_id(punit->transported_by))
+ && can_unit_exist_at_tile(punit, punit->tile)) {
+ return TRUE;
+ }
+ } unit_list_iterate_end;
+
+ return FALSE;
+}
+
+/****************************************************************************
+ Return TRUE iff any of the units' tiles have the activity running
+ on them.
+****************************************************************************/
+static bool units_have_activity_on_tile(struct unit_list *punits,
+ enum unit_activity activity)
+{
+ unit_list_iterate(punits, punit) {
+ if (is_unit_activity_on_tile(activity, punit->tile)) {
+ return TRUE;
+ }
+ } unit_list_iterate_end;
+
+ return FALSE;
+}
+
/****************************************************************
Note: the menu strings should contain underscores as in the
menu_items struct. The underscores will be removed elsewhere if
@@ -1190,7 +1335,6 @@
menus_set_sensitive("<main>/_View", FALSE);
menus_set_sensitive("<main>/_Orders", FALSE);
} else {
- struct unit *punit;
const char *path =
menu_path_remove_uline("<main>/_Government/_Change Government");
GtkWidget *parent = gtk_item_factory_get_widget(item_factory, path);
@@ -1282,13 +1426,14 @@
return;
}
- if((punit=get_unit_in_focus())) {
+ if (get_num_units_in_focus() > 0) {
const char *irrfmt = _("Change to %s (_I)");
const char *minfmt = _("Change to %s (_M)");
const char *transfmt = _("Transf_orm to %s");
char irrtext[128], mintext[128], transtext[128];
const char *roadtext;
struct terrain *pterrain;
+ struct unit_list *punits = get_units_in_focus();
sz_strlcpy(irrtext, _("Build _Irrigation"));
sz_strlcpy(mintext, _("Build _Mine"));
@@ -1297,67 +1442,77 @@
/* Enable the button for adding to a city in all cases, so we
get an eventual error message from the server if we try. */
menus_set_sensitive("<main>/_Orders/_Build City",
- can_unit_add_or_build_city(punit) ||
- unit_can_help_build_wonder_here(punit));
+ (can_units_do(punits, can_unit_add_or_build_city)
+ || can_units_do(punits, unit_can_help_build_wonder_here)));
menus_set_sensitive("<main>/_Orders/Build _Road",
- (can_unit_do_activity(punit, ACTIVITY_ROAD) ||
- can_unit_do_activity(punit, ACTIVITY_RAILROAD) ||
- unit_can_est_traderoute_here(punit)));
+ (can_units_do_activity(punits, ACTIVITY_ROAD)
+ || can_units_do_activity(punits, ACTIVITY_RAILROAD)
+ || can_units_do(punits,
+ unit_can_est_traderoute_here)));
menus_set_sensitive("<main>/_Orders/Build _Irrigation",
- can_unit_do_activity(punit, ACTIVITY_IRRIGATE));
+ can_units_do_activity(punits, ACTIVITY_IRRIGATE));
menus_set_sensitive("<main>/_Orders/Build _Mine",
- can_unit_do_activity(punit, ACTIVITY_MINE));
+ can_units_do_activity(punits, ACTIVITY_MINE));
menus_set_sensitive("<main>/_Orders/Transf_orm Terrain",
- can_unit_do_activity(punit, ACTIVITY_TRANSFORM));
+ can_units_do_activity(punits, ACTIVITY_TRANSFORM));
menus_set_sensitive("<main>/_Orders/Build _Fortress",
- (can_unit_do_activity(punit, ACTIVITY_FORTRESS) ||
- can_unit_do_activity(punit, ACTIVITY_FORTIFYING)));
+ (can_units_do_activity(punits, ACTIVITY_FORTRESS)
+ || can_units_do_activity(punits,
+ ACTIVITY_FORTIFYING)));
menus_set_sensitive("<main>/_Orders/Build Airbas_e",
- can_unit_do_activity(punit, ACTIVITY_AIRBASE));
+ can_units_do_activity(punits, ACTIVITY_AIRBASE));
menus_set_sensitive("<main>/_Orders/Clean _Pollution",
- (can_unit_do_activity(punit, ACTIVITY_POLLUTION) ||
- can_unit_paradrop(punit)));
+ (can_units_do_activity(punits, ACTIVITY_POLLUTION)
+ || can_units_do(punits, can_unit_paradrop)));
menus_set_sensitive("<main>/_Orders/Clean _Nuclear Fallout",
- can_unit_do_activity(punit, ACTIVITY_FALLOUT));
+ can_units_do_activity(punits, ACTIVITY_FALLOUT));
menus_set_sensitive("<main>/_Orders/_Sentry",
- can_unit_do_activity(punit, ACTIVITY_SENTRY));
+ can_units_do_activity(punits, ACTIVITY_SENTRY));
menus_set_sensitive("<main>/_Orders/Pillage",
- can_unit_do_activity(punit, ACTIVITY_PILLAGE));
+ can_units_do_activity(punits, ACTIVITY_PILLAGE));
menus_set_sensitive("<main>/_Orders/_Disband Unit",
- !unit_flag(punit, F_UNDISBANDABLE));
+ units_have_flag(punits, F_UNDISBANDABLE, FALSE));
menus_set_sensitive("<main>/_Orders/Make _Homecity",
- can_unit_change_homecity(punit));
+ can_units_do(punits, can_unit_change_homecity));
menus_set_sensitive("<main>/_Orders/_Unload Transporter",
- get_transporter_occupancy(punit) > 0);
+ units_are_occupied(punits));
menus_set_sensitive("<main>/_Orders/_Load",
- can_unit_load(punit, find_transporter_for_unit(punit,
- punit->tile)));
+ units_can_load(punits));
menus_set_sensitive("<main>/_Orders/_Unload",
- (can_unit_unload(punit, find_unit_by_id(punit->transported_by))
- && can_unit_exist_at_tile(punit, punit->tile)));
+ units_can_unload(punits));
menus_set_sensitive("<main>/_Orders/Wake up o_thers",
- is_unit_activity_on_tile(ACTIVITY_SENTRY,
- punit->tile));
+ units_have_activity_on_tile(punits,
+ ACTIVITY_SENTRY));
menus_set_sensitive("<main>/_Orders/_Auto Settler",
- can_unit_do_autosettlers(punit));
+ can_units_do(punits, can_unit_do_autosettlers));
menus_set_sensitive("<main>/_Orders/Auto E_xplore",
- can_unit_do_activity(punit, ACTIVITY_EXPLORE));
+ can_units_do_activity(punits, ACTIVITY_EXPLORE));
menus_set_sensitive("<main>/_Orders/_Connect/_Road",
- can_unit_do_connect(punit, ACTIVITY_ROAD));
+ can_units_do_connect(punits, ACTIVITY_ROAD));
menus_set_sensitive("<main>/_Orders/_Connect/_Rail",
- can_unit_do_connect(punit, ACTIVITY_RAILROAD));
+ can_units_do_connect(punits, ACTIVITY_RAILROAD));
menus_set_sensitive("<main>/_Orders/_Connect/_Irrigate",
- can_unit_do_connect(punit, ACTIVITY_IRRIGATE));
+ can_units_do_connect(punits, ACTIVITY_IRRIGATE));
menus_set_sensitive("<main>/_Orders/Diplomat\\/Spy Actions",
- (is_diplomat_unit(punit)
- && diplomat_can_do_action(punit,
DIPLOMAT_ANY_ACTION,
- punit->tile)));
+ can_units_do_diplomat_action(punits,
+ DIPLOMAT_ANY_ACTION));
menus_set_sensitive("<main>/_Orders/Explode Nuclear",
- unit_flag(punit, F_NUCLEAR));
- if (unit_flag(punit, F_HELP_WONDER))
+ units_have_flag(punits, F_NUCLEAR, TRUE));
+ if (units_have_flag(punits, F_HELP_WONDER, TRUE))
menus_rename("<main>/_Orders/_Build City", _("Help _Build Wonder"));
- else if (unit_flag(punit, F_CITIES)) {
- if (tile_get_city(punit->tile))
+ else if (units_have_flag(punits, F_CITIES, TRUE)) {
+ bool can_build = FALSE;
+
+ /* FIXME: this overloading doesn't work well with multiple focus
+ * units. */
+ unit_list_iterate(punits, punit) {
+ if (!punit->tile->city) {
+ can_build = TRUE;
+ break;
+ }
+ } unit_list_iterate_end;
+
+ if (can_build)
menus_rename("<main>/_Orders/_Build City", _("Add to City (_B)"));
else
menus_rename("<main>/_Orders/_Build City", _("_Build City"));
@@ -1365,14 +1520,24 @@
else
menus_rename("<main>/_Orders/_Build City", _("_Build City"));
- if (unit_flag(punit, F_TRADE_ROUTE))
+ if (units_have_flag(punits, F_TRADE_ROUTE, TRUE))
menus_rename("<main>/_Orders/Build _Road", _("Make Trade _Route"));
- else if (unit_flag(punit, F_SETTLERS)) {
- if (tile_has_special(punit->tile, S_ROAD)) {
+ else if (units_have_flag(punits, F_SETTLERS, TRUE)) {
+ bool has_road = FALSE;
+
+ /* FIXME: this overloading doesn't work well with multiple focus
+ * units. */
+ unit_list_iterate(punits, punit) {
+ if (tile_has_special(punit->tile, S_ROAD)) {
+ has_road = TRUE;
+ break;
+ }
+ } unit_list_iterate_end;
+
+ if (has_road) {
roadtext = _("Build _Railroad");
road_activity=ACTIVITY_RAILROAD;
- }
- else {
+ } else {
roadtext = _("Build _Road");
road_activity=ACTIVITY_ROAD;
}
@@ -1381,40 +1546,44 @@
else
menus_rename("<main>/_Orders/Build _Road", _("Build _Road"));
- pterrain = punit->tile->terrain;
- if (pterrain->irrigation_result != T_NONE
- && pterrain->irrigation_result != pterrain) {
- my_snprintf(irrtext, sizeof(irrtext), irrfmt,
- get_tile_change_menu_text(punit->tile,
- ACTIVITY_IRRIGATE));
- } else if (tile_has_special(punit->tile, S_IRRIGATION)
- && player_knows_techs_with_flag(punit->owner,
- TF_FARMLAND)) {
- sz_strlcpy(irrtext, _("Bu_ild Farmland"));
+ if (unit_list_size(punits) == 1) {
+ struct unit *punit = unit_list_get(punits, 0);
+
+ pterrain = punit->tile->terrain;
+ if (pterrain->irrigation_result != T_NONE
+ && pterrain->irrigation_result != pterrain) {
+ my_snprintf(irrtext, sizeof(irrtext), irrfmt,
+ get_tile_change_menu_text(punit->tile,
+ ACTIVITY_IRRIGATE));
+ } else if (tile_has_special(punit->tile, S_IRRIGATION)
+ && player_knows_techs_with_flag(punit->owner,
+ TF_FARMLAND)) {
+ sz_strlcpy(irrtext, _("Bu_ild Farmland"));
+ }
+ if (pterrain->mining_result != T_NONE
+ && pterrain->mining_result != pterrain) {
+ my_snprintf(mintext, sizeof(mintext), minfmt,
+ get_tile_change_menu_text(punit->tile, ACTIVITY_MINE));
+ }
+ if (pterrain->transform_result != T_NONE
+ && pterrain->transform_result != pterrain) {
+ my_snprintf(transtext, sizeof(transtext), transfmt,
+ get_tile_change_menu_text(punit->tile,
+ ACTIVITY_TRANSFORM));
+ }
}
- if (pterrain->mining_result != T_NONE
- && pterrain->mining_result != pterrain) {
- my_snprintf(mintext, sizeof(mintext), minfmt,
- get_tile_change_menu_text(punit->tile, ACTIVITY_MINE));
- }
- if (pterrain->transform_result != T_NONE
- && pterrain->transform_result != pterrain) {
- my_snprintf(transtext, sizeof(transtext), transfmt,
- get_tile_change_menu_text(punit->tile,
- ACTIVITY_TRANSFORM));
- }
menus_rename("<main>/_Orders/Build _Irrigation", irrtext);
menus_rename("<main>/_Orders/Build _Mine", mintext);
menus_rename("<main>/_Orders/Transf_orm Terrain", transtext);
- if (can_unit_do_activity(punit, ACTIVITY_FORTIFYING)) {
+ if (can_units_do_activity(punits, ACTIVITY_FORTIFYING)) {
menus_rename("<main>/_Orders/Build _Fortress", _("_Fortify"));
} else {
menus_rename("<main>/_Orders/Build _Fortress", _("Build _Fortress"));
}
- if (unit_flag(punit, F_PARATROOPERS)) {
+ if (units_have_flag(punits, F_PARATROOPERS, TRUE)) {
menus_rename("<main>/_Orders/Clean _Pollution", _("_Paradrop"));
} else {
menus_rename("<main>/_Orders/Clean _Pollution", _("Clean _Pollution"));
Index: client/gui-gtk-2.0/dialogs.c
===================================================================
--- client/gui-gtk-2.0/dialogs.c (revision 11168)
+++ client/gui-gtk-2.0/dialogs.c (working copy)
@@ -628,7 +628,7 @@
-1);
g_object_unref(pix);
- if (punit == get_unit_in_focus()) {
+ if (unit_is_in_focus(punit)) {
unit_select_path =
gtk_tree_model_get_path(GTK_TREE_MODEL(unit_select_store), it);
}
Index: client/gui-gtk-2.0/mapctrl.c
===================================================================
--- client/gui-gtk-2.0/mapctrl.c (revision 11168)
+++ client/gui-gtk-2.0/mapctrl.c (working copy)
@@ -253,9 +253,9 @@
else if (ev->state & GDK_CONTROL_MASK) {
action_button_pressed(ev->x, ev->y, SELECT_SEA);
}
- /* <SHIFT> + LMB: Copy Production. */
+ /* <SHIFT> + LMB: Append focus unit Production. */
else if (ptile && (ev->state & GDK_SHIFT_MASK)) {
- clipboard_copy_production(ptile);
+ action_button_pressed(ev->x, ev->y, SELECT_APPEND);
}
/* <ALT> + LMB: popit (same as middle-click) */
/* FIXME: we need a general mechanism for letting freeciv work with
@@ -310,7 +310,8 @@
}
cancel_tile_hiliting();
if (hover_state == HOVER_NONE) {
- anchor_selection_rectangle(ev->x, ev->y);
+ anchor_selection_rectangle(ev->x, ev->y,
+ (ev->state & GDK_SHIFT_MASK) != 0);
rbutton_down = TRUE; /* causes rectangle updates */
}
}
@@ -403,7 +404,8 @@
if (can_client_change_view() && (ev->button == 3)) {
center_tile_mapcanvas(map_pos_to_tile(xtile, ytile));
} else if (can_client_issue_orders() && (ev->button == 1)) {
- do_map_click(map_pos_to_tile(xtile, ytile), SELECT_POPUP);
+ do_map_click(map_pos_to_tile(xtile, ytile),
+ (ev->state & GDK_SHIFT_MASK) ? SELECT_POPUP : SELECT_APPEND);
}
return TRUE;
Index: client/text.c
===================================================================
--- client/text.c (revision 11168)
+++ client/text.c (working copy)
@@ -73,7 +73,7 @@
const char *activity_text;
struct city *pcity = ptile->city;
struct unit *punit = find_visible_unit(ptile);
- struct unit *pfocus_unit = get_unit_in_focus();
+ struct unit *pfocus_unit = get_focus_unit_on_tile(ptile);
const char *diplo_nation_plural_adjectives[DS_LAST] =
{Q_("?nation:Neutral"), Q_("?nation:Hostile"),
"" /* unused, DS_CEASEFIRE*/,
@@ -623,16 +623,17 @@
FIXME: this should be renamed.
****************************************************************************/
-const char *get_unit_info_label_text1(struct unit *punit)
+const char *get_unit_info_label_text1(struct unit_list *punits)
{
static struct astring str = ASTRING_INIT;
+ int count = unit_list_size(punits);
astr_clear(&str);
- if (punit) {
- struct unit_type *ptype = unit_type(punit);
-
- astr_add(&str, "%s", ptype->name);
+ if (count == 1) {
+ astr_add(&str, "%s", unit_list_get(punits, 0)->type->name);
+ } else {
+ astr_add(&str, PL_("%d unit", "%d units", count), count);
}
return str.str;
}
@@ -642,27 +643,47 @@
FIXME: this should be renamed.
****************************************************************************/
-const char *get_unit_info_label_text2(struct unit *punit)
+const char *get_unit_info_label_text2(struct unit_list *punits)
{
static struct astring str = ASTRING_INIT;
+ int count = unit_list_size(punits);
astr_clear(&str);
/* This text should always have the same number of lines. Otherwise the
* GUI widgets may be confused and try to resize themselves. */
- if (punit) {
+
+ /* Line 1. Goto or activity text. */
+ if (count > 0 && unit_list_size(hover_units) > 0) {
+ int min, max;
+
+ goto_get_turns(&min, &max);
+ if (min == max) {
+ astr_add_line(&str, _("Turns to target: %d"), max);
+ } else {
+ astr_add_line(&str, _("Turns to target: %d to %d"), min, max);
+ }
+ } else if (count == 1) {
+ astr_add_line(&str, "%s",
+ unit_activity_text(unit_list_get(punits, 0)));
+ } else if (count > 1) {
+ astr_add_line(&str, PL_("%d unit selected",
+ "%d units selected",
+ count),
+ count);
+ } else {
+ astr_add_line(&str, _("No units selected."));
+ }
+
+ /* Lines 2, 3, and 4 vary. */
+ if (count == 1) {
+ struct unit *punit = unit_list_get(punits, 0);
struct city *pcity =
player_find_city_by_id(punit->owner, punit->homecity);
int infracount;
bv_special infrastructure =
get_tile_infrastructure_set(punit->tile, &infracount);
- if (hover_unit == punit->id) {
- astr_add_line(&str, _("Turns to target: %d"), get_goto_turns());
- } else {
- astr_add_line(&str, "%s", unit_activity_text(punit));
- }
-
astr_add_line(&str, "%s", tile_get_info_text(punit->tile));
if (infracount > 0) {
astr_add_line(&str, "%s", get_infrastructure_text(infrastructure));
@@ -674,12 +695,79 @@
} else {
astr_add_line(&str, " ");
}
+ } else if (count > 1) {
+ int mil = 0, nonmil = 0;
+ int types_count[U_LAST], i;
+ struct unit_type *top[3];
+
+ memset(types_count, 0, sizeof(types_count));
+ unit_list_iterate(punits, punit) {
+ if (unit_flag(punit, F_NONMIL)) {
+ nonmil++;
+ } else {
+ mil++;
+ }
+ types_count[punit->type->index]++;
+ } unit_list_iterate_end;
+
+ top[0] = top[1] = top[2] = NULL;
+ unit_type_iterate(utype) {
+ if (!top[2]
+ || types_count[top[2]->index] < types_count[utype->index]) {
+ top[2] = utype;
+
+ if (!top[1]
+ || types_count[top[1]->index] < types_count[top[2]->index]) {
+ top[2] = top[1];
+ top[1] = utype;
+
+ if (!top[0]
+ || types_count[top[0]->index] < types_count[utype->index]) {
+ top[1] = top[0];
+ top[0] = utype;
+ }
+ }
+ }
+ } unit_type_iterate_end;
+
+ for (i = 0; i < 3; i++) {
+ if (top[i] && types_count[top[i]->index] > 0) {
+ if (unit_type_flag(top[i], F_NONMIL)) {
+ nonmil -= types_count[top[i]->index];
+ } else {
+ mil -= types_count[top[i]->index];
+ }
+ astr_add_line(&str, "%d: %s",
+ types_count[top[i]->index], top[i]->name);
+ } else {
+ astr_add_line(&str, " ");
+ }
+ }
+
+ if (nonmil > 0 && mil > 0) {
+ astr_add_line(&str, _("Others: %d civil; %d military"), nonmil, mil);
+ } else if (nonmil > 0) {
+ astr_add_line(&str, _("Others: %d civilian"), nonmil);
+ } else if (mil > 0) {
+ astr_add_line(&str, _("Others: %d military"), mil);
+ } else {
+ astr_add_line(&str, " ");
+ }
+ } else {
+ astr_add_line(&str, " ");
+ astr_add_line(&str, " ");
+ astr_add_line(&str, " ");
+ }
+
+ /* Line 5. Debug text. */
#ifdef DEBUG
- astr_add_line(&str, "(Unit ID %d)", punit->id);
-#endif
+ if (count == 1) {
+ astr_add_line(&str, "(Unit ID %d)", unit_list_get(punits, 0)->id);
} else {
- astr_add(&str, "\n\n\n");
+ astr_add_line(&str, " ");
}
+#endif
+
return str.str;
}
Index: client/control.h
===================================================================
--- client/control.h (revision 11168)
+++ client/control.h (working copy)
@@ -26,13 +26,17 @@
/* Selecting unit from a stack without popup. */
enum quickselect_type {
- SELECT_POPUP = 0, SELECT_SEA, SELECT_LAND
+ SELECT_POPUP = 0, SELECT_SEA, SELECT_LAND, SELECT_APPEND
};
void control_init(void);
void control_done(void);
+void control_unit_killed(struct unit *punit);
-extern int hover_unit; /* unit hover_state applies to */
+void unit_change_battlegroup(struct unit *punit, int battlegroup);
+void unit_register_battlegroup(struct unit *punit);
+
+extern struct unit_list *hover_units; /* unit hover_state applies to */
extern enum cursor_hover_state hover_state;
extern enum unit_activity connect_activity;
extern enum unit_orders goto_last_order;
@@ -44,12 +48,12 @@
void do_unit_goto(struct tile *ptile);
void do_unit_nuke(struct unit *punit);
void do_unit_paradrop_to(struct unit *punit, struct tile *ptile);
-void do_unit_patrol_to(struct unit *punit, struct tile *ptile);
-void do_unit_connect(struct unit *punit, struct tile *ptile,
+void do_unit_patrol_to(struct tile *ptile);
+void do_unit_connect(struct tile *ptile,
enum unit_activity activity);
void do_map_click(struct tile *ptile, enum quickselect_type qtype);
-void set_hover_state(struct unit *punit, enum cursor_hover_state state,
+void set_hover_state(struct unit_list *punits, enum cursor_hover_state state,
enum unit_activity connect_activity,
enum unit_orders goto_last_order);
void request_center_focus_unit(void);
@@ -69,8 +73,8 @@
void request_unit_fortify(struct unit *punit);
void request_unit_goto(enum unit_orders last_order);
void request_unit_move_done(void);
-void request_unit_nuke(struct unit *punit);
-void request_unit_paradrop(struct unit *punit);
+void request_unit_nuke(struct unit_list *punits);
+void request_unit_paradrop(struct unit_list *punits);
void request_unit_patrol(void);
void request_unit_pillage(struct unit *punit);
void request_unit_sentry(struct unit *punit);
@@ -78,8 +82,9 @@
void request_unit_airlift(struct unit *punit, struct city *pcity);
void request_unit_return(struct unit *punit);
void request_unit_upgrade(struct unit *punit);
-void request_unit_wait(struct unit *punit);
+void request_units_wait(struct unit_list *punits);
void request_unit_wakeup(struct unit *punit);
+void request_unit_select_same_type(struct unit_list *punits);
void request_diplomat_action(enum diplomat_actions action, int dipl_id,
int target_id, int value);
void request_toggle_city_outlines(void);
@@ -103,17 +108,21 @@
void wakeup_sentried_units(struct tile *ptile);
+bool unit_is_in_focus(const struct unit *punit);
+struct unit *get_focus_unit_on_tile(const struct tile *ptile);
void auto_center_on_focus_unit(void);
void advance_unit_focus(void);
-struct unit *get_unit_in_focus(void);
+struct unit_list *get_units_in_focus(void);
+int get_num_units_in_focus(void);
void set_unit_focus(struct unit *punit);
+void add_unit_focus(struct unit *punit);
void set_unit_focus_and_select(struct unit *punit);
void update_unit_focus(void);
struct unit *find_visible_unit(struct tile *ptile);
void set_units_in_combat(struct unit *pattacker, struct unit *pdefender);
double blink_active_unit(void);
double blink_turn_done_button(void);
-void update_unit_pix_label(struct unit *punit);
+void update_unit_pix_label(struct unit_list *punitlist);
void process_caravan_arrival(struct unit *punit);
void process_diplomat_arrival(struct unit *pdiplomat, int victim_id);
@@ -170,6 +179,8 @@
void key_unit_unload_all(void);
void key_unit_wait(void);
void key_unit_wakeup_others(void);
+void key_unit_assign_battlegroup(int battlegroup, bool append);
+void key_unit_select_battlegroup(int battlegroup, bool append);
/* don't change this unless you also put more entries in data/Freeciv */
#define MAX_NUM_UNITS_BELOW 4
Index: client/text.h
===================================================================
--- client/text.h (revision 11168)
+++ client/text.h (working copy)
@@ -35,8 +35,8 @@
const char *get_global_warming_tooltip(void);
const char *get_nuclear_winter_tooltip(void);
const char *get_government_tooltip(void);
-const char *get_unit_info_label_text1(struct unit *punit);
-const char *get_unit_info_label_text2(struct unit *punit);
+const char *get_unit_info_label_text1(struct unit_list *punits);
+const char *get_unit_info_label_text2(struct unit_list *punits);
const char *get_spaceship_descr(struct player_spaceship *pship);
const char *get_timeout_label_text(void);
const char *format_duration(int duration);
Index: client/include/mapview_g.h
===================================================================
--- client/include/mapview_g.h (revision 11168)
+++ client/include/mapview_g.h (working copy)
@@ -22,7 +22,7 @@
#include "mapview_common.h"
void update_info_label(void);
-void update_unit_info_label(struct unit *punit);
+void update_unit_info_label(struct unit_list *punitlist);
void update_timeout_label(void);
void update_turn_done_button(bool do_restore);
void update_city_descriptions(void);
Index: client/goto.c
===================================================================
--- client/goto.c (revision 11168)
+++ client/goto.c (working copy)
@@ -49,31 +49,57 @@
struct pf_map *map;
};
+/* For each tile and each direction we store the number of lines going out
+ * of the tile in this direction. Since each line is undirected, we only
+ * store the 4 lower-numbered directions for each tile; the 4 upper-numbered
+ * directions are stored as reverses from the target tile.
+ * Notes: 1. This assumes that
+ * - there are 8 directions
+ * - out of every two opposite directions (like NORTH and SOUTH) one and
+ * only one has number less than 4
+ * 2. There _can_ be more than one line drawn between two tiles, because of
+ * the waypoints. */
static struct {
- /* For each tile and each direction we store the number of lines going out
- * of the tile in this direction. Since each line is undirected, we only
- * store the 4 lower-numbered directions for each tile; the 4 upper-numbered
- * directions are stored as reverses from the target tile.
- * Notes: 1. This assumes that
- * - there are 8 directions
- * - out of every two opposite directions (like NORTH and SOUTH) one and
- * only one has number less than 4
- * 2. There _can_ be more than one line drawn between two tiles, because of
- * the waypoints. */
- struct {
- unsigned char drawn[4];
- } *tiles;
+ unsigned char drawn[4];
+} *tiles;
+
+struct goto_map {
int unit_id; /* The unit of the goto map */
struct part *parts;
int num_parts;
struct pf_parameter template;
-} goto_map;
+};
-#define DRAWN(ptile, dir) (goto_map.tiles[(ptile)->index].drawn[dir])
+/* get 'struct goto_map_list' and related functions: */
+#define SPECLIST_TAG goto_map
+#define SPECLIST_TYPE struct goto_map
+#include "speclist.h"
+#define goto_map_list_iterate(gotolist, pgoto) \
+ TYPED_LIST_ITERATE(struct goto_map, gotolist, pgoto)
+#define goto_map_list_iterate_end \
+ LIST_ITERATE_END
+/* Iterate over goto maps, filtering out dead units. */
+#define goto_map_iterate(gotolist, pgoto, punit) \
+ goto_map_list_iterate(gotolist, pgoto) { \
+ struct unit *punit = find_unit_by_id(pgoto->unit_id); \
+ \
+ if (punit) { \
+ assert(unit_is_in_focus(punit)); \
+ {
+
+#define goto_map_iterate_end \
+ }
\
+ } \
+ } goto_map_list_iterate_end;
+
+static struct goto_map_list *goto_maps;
+
+#define DRAWN(ptile, dir) (tiles[(ptile)->index].drawn[dir])
+
static void increment_drawn(struct tile *src_tile, enum direction8 dir);
static void decrement_drawn(struct tile *src_tile, enum direction8 dir);
-static void reset_last_part(void);
+static void reset_last_part(struct goto_map *goto_map);
/**************************************************************************
@@ -83,6 +109,31 @@
static bool is_init = FALSE;
static int connect_initial;
+/****************************************************************************
+ Create a new goto map.
+****************************************************************************/
+static struct goto_map *goto_map_new(void)
+{
+ struct goto_map *goto_map = fc_malloc(sizeof(*goto_map));
+
+ goto_map->unit_id = -1;
+ goto_map->parts = NULL;
+ goto_map->num_parts = 0;
+
+ return goto_map;
+}
+
+/****************************************************************************
+ Free an existing goto map.
+****************************************************************************/
+static void goto_map_free(struct goto_map *goto_map)
+{
+ if (goto_map->parts) {
+ free(goto_map->parts);
+ }
+ free(goto_map);
+}
+
/**********************************************************************
Called once per game.
***********************************************************************/
@@ -92,12 +143,12 @@
free_client_goto();
}
- goto_map.tiles = fc_malloc(MAP_INDEX_SIZE * sizeof(*goto_map.tiles));
- goto_map.parts = NULL;
- goto_map.num_parts = 0;
- goto_map.unit_id = -1;
+ goto_maps = goto_map_list_new();
+
+ tiles = fc_malloc(MAP_INDEX_SIZE * sizeof(*tiles));
whole_map_iterate(ptile) {
int dir;
+
for (dir = 0; dir < 4; dir++) {
DRAWN(ptile, dir) = 0;
}
@@ -112,12 +163,13 @@
void free_client_goto(void)
{
if (is_init) {
- free(goto_map.tiles);
- if (goto_map.parts) {
- free(goto_map.parts);
- }
+ goto_map_list_iterate(goto_maps, goto_map) {
+ goto_map_free(goto_map);
+ } goto_map_list_iterate_end;
+ goto_map_list_unlink_all(goto_maps);
+ goto_map_list_free(goto_maps);
- memset(&goto_map, 0, sizeof(goto_map));
+ free(tiles);
is_init = FALSE;
}
@@ -127,20 +179,20 @@
Change the destination of the last part to the given position if a
path can be found. If not the destination is set to the start.
***********************************************************************/
-static void update_last_part(struct tile *ptile)
+static void update_last_part(struct goto_map *goto_map, struct tile *ptile)
{
- struct part *p = &goto_map.parts[goto_map.num_parts - 1];
+ struct part *p = &goto_map->parts[goto_map->num_parts - 1];
struct pf_path *new_path;
struct tile *old_tile = p->start_tile;
int i, start_index = 0;
freelog(LOG_DEBUG, "update_last_part(%d,%d) old (%d,%d)-(%d,%d)",
- TILE_XY(ptile), TILE_XY(p->start_tile), TILE_XY(p->end_tile));
+ TILE_XY(ptile), TILE_XY(p->start_tile), TILE_XY(p->end_tile));
new_path = pf_get_path(p->map, ptile);
if (!new_path) {
freelog(PATH_LOG_LEVEL, " no path found");
- reset_last_part();
+ reset_last_part(goto_map);
return;
}
@@ -155,8 +207,8 @@
struct pf_position *b = &new_path->positions[i];
if (a->dir_to_next_pos != b->dir_to_next_pos
- || !same_pos(a->tile, b->tile)) {
- break;
+ || !same_pos(a->tile, b->tile)) {
+ break;
}
}
start_index = i;
@@ -195,7 +247,7 @@
p->end_fuel_left = pf_last_position(p->path)->fuel_left;
if (hover_state == HOVER_CONNECT) {
- int move_rate = goto_map.template.move_rate;
+ int move_rate = goto_map->template.move_rate;
int moves = pf_last_position(p->path)->total_MC;
p->time = moves / move_rate;
@@ -203,8 +255,7 @@
p->time += connect_initial;
}
freelog(PATH_LOG_LEVEL, "To (%d,%d) MC: %d, connect_initial: %d",
- TILE_XY(ptile), moves, connect_initial);
-
+ TILE_XY(ptile), moves, connect_initial);
} else {
p->time = pf_last_position(p->path)->turn;
}
@@ -218,13 +269,13 @@
Change the drawn path to a size of 0 steps by setting it to the
start position.
***********************************************************************/
-static void reset_last_part(void)
+static void reset_last_part(struct goto_map *goto_map)
{
- struct part *p = &goto_map.parts[goto_map.num_parts - 1];
+ struct part *p = &goto_map->parts[goto_map->num_parts - 1];
if (!same_pos(p->start_tile, p->end_tile)) {
/* Otherwise no need to update */
- update_last_part(p->start_tile);
+ update_last_part(goto_map, p->start_tile);
}
}
@@ -233,26 +284,29 @@
of the new part is either the unit position (for the first part) or
the destination of the last part (not the first part).
***********************************************************************/
-static void add_part(void)
+static void add_part(struct goto_map *goto_map)
{
struct part *p;
- struct pf_parameter parameter = goto_map.template;
+ struct pf_parameter parameter = goto_map->template;
+ struct unit *punit = find_unit_by_id(goto_map->unit_id);
- goto_map.num_parts++;
- goto_map.parts =
- fc_realloc(goto_map.parts,
- goto_map.num_parts * sizeof(*goto_map.parts));
- p = &goto_map.parts[goto_map.num_parts - 1];
+ if (!punit) {
+ return;
+ }
- if (goto_map.num_parts == 1) {
+ goto_map->num_parts++;
+ goto_map->parts =
+ fc_realloc(goto_map->parts,
+ goto_map->num_parts * sizeof(*goto_map->parts));
+ p = &goto_map->parts[goto_map->num_parts - 1];
+
+ if (goto_map->num_parts == 1) {
/* first part */
- struct unit *punit = find_unit_by_id(goto_map.unit_id);
-
p->start_tile = punit->tile;
p->start_moves_left = parameter.moves_left_initially;
p->start_fuel_left = parameter.fuel_left_initially;
} else {
- struct part *prev = &goto_map.parts[goto_map.num_parts - 2];
+ struct part *prev = &goto_map->parts[goto_map->num_parts - 2];
p->start_tile = prev->end_tile;
p->start_moves_left = prev->end_moves_left;
@@ -270,19 +324,19 @@
/**********************************************************************
Remove the last part, erasing the corresponding path segment.
***********************************************************************/
-static void remove_last_part(void)
+static void remove_last_part(struct goto_map *goto_map)
{
- struct part *p = &goto_map.parts[goto_map.num_parts - 1];
+ struct part *p = &goto_map->parts[goto_map->num_parts - 1];
- assert(goto_map.num_parts >= 1);
+ assert(goto_map->num_parts >= 1);
- reset_last_part();
+ reset_last_part(goto_map);
if (p->path) {
/* We do not always have a path */
pf_destroy_path(p->path);
}
pf_destroy_map(p->map);
- goto_map.num_parts--;
+ goto_map->num_parts--;
}
/**********************************************************************
@@ -290,16 +344,15 @@
***********************************************************************/
void goto_add_waypoint(void)
{
- struct part *p = &goto_map.parts[goto_map.num_parts - 1];
-
assert(is_active);
- assert(find_unit_by_id(goto_map.unit_id)
- && find_unit_by_id(goto_map.unit_id) == get_unit_in_focus());
+ goto_map_iterate(goto_maps, goto_map, punit) {
+ struct part *p = &goto_map->parts[goto_map->num_parts - 1];
- if (!same_pos(p->start_tile, p->end_tile)) {
- /* Otherwise the last part has zero length. */
- add_part();
- }
+ if (!same_pos(p->start_tile, p->end_tile)) {
+ /* Otherwise the last part has zero length. */
+ add_part(goto_map);
+ }
+ } goto_map_iterate_end;
}
/**********************************************************************
@@ -308,27 +361,29 @@
***********************************************************************/
bool goto_pop_waypoint(void)
{
- struct part *p = &goto_map.parts[goto_map.num_parts - 1];
- struct tile *end_tile = p->end_tile;
+ bool popped = FALSE;
assert(is_active);
- assert(find_unit_by_id(goto_map.unit_id)
- && find_unit_by_id(goto_map.unit_id) == get_unit_in_focus());
+ goto_map_iterate(goto_maps, goto_map, punit) {
+ struct part *p = &goto_map->parts[goto_map->num_parts - 1];
+ struct tile *end_tile = p->end_tile;
- if (goto_map.num_parts == 1) {
- /* we don't have any waypoint but the start pos. */
- return FALSE;
- }
+ if (goto_map->num_parts == 1) {
+ /* we don't have any waypoint but the start pos. */
+ continue;
+ }
+ popped = TRUE;
- remove_last_part();
+ remove_last_part(goto_map);
- /*
- * Set the end position of the previous part (now the last) to the
- * end position of the last part (now gone). I.e. redraw a line to
- * the mouse position.
- */
- update_last_part(end_tile);
- return TRUE;
+ /*
+ * Set the end position of the previous part (now the last) to the
+ * end position of the last part (now gone). I.e. redraw a line to
+ * the mouse position.
+ */
+ update_last_part(goto_map, end_tile);
+ } goto_map_iterate_end;
+ return popped;
}
/**********************************************************************
@@ -710,16 +765,23 @@
Enter the goto state: activate, prepare PF-template and add the
initial part.
***********************************************************************/
-void enter_goto_state(struct unit *punit)
+void enter_goto_state(struct unit_list *punits)
{
assert(!is_active);
+ assert(goto_map_list_size(goto_maps) == 0);
- goto_map.unit_id = punit->id;
- assert(goto_map.num_parts == 0);
+ unit_list_iterate(punits, punit) {
+ struct goto_map *goto_map = goto_map_new();
- fill_client_goto_parameter(punit, &goto_map.template);
+ goto_map->unit_id = punit->id;
+ assert(goto_map->num_parts == 0);
- add_part();
+ fill_client_goto_parameter(punit, &goto_map->template);
+
+ add_part(goto_map);
+
+ goto_map_list_append(goto_maps, goto_map);
+ } unit_list_iterate_end;
is_active = TRUE;
}
@@ -732,11 +794,13 @@
return;
}
- while (goto_map.num_parts > 0) {
- remove_last_part();
- }
- free(goto_map.parts);
- goto_map.parts = NULL;
+ goto_map_list_iterate(goto_maps, goto_map) {
+ while (goto_map->num_parts > 0) {
+ remove_last_part(goto_map);
+ }
+ goto_map_free(goto_map);
+ } goto_map_list_iterate_end;
+ goto_map_list_unlink_all(goto_maps);
is_active = FALSE;
}
@@ -754,25 +818,43 @@
***********************************************************************/
struct tile *get_line_dest(void)
{
- struct part *p = &goto_map.parts[goto_map.num_parts - 1];
+ if (is_active) {
+ struct goto_map *goto_map = goto_map_list_get(goto_maps, 0);
+ struct part *p = &goto_map->parts[goto_map->num_parts - 1];
- assert(is_active);
-
- return p->end_tile;
+ return p->end_tile;
+ } else {
+ return NULL;
+ }
}
/**************************************************************************
Return the path length (in turns).
***************************************************************************/
-int get_goto_turns(void)
+void goto_get_turns(int *min, int *max)
{
- int time = 0, i;
+ if (min) {
+ *min = FC_INFINITY;
+ }
+ if (max) {
+ *max = -1;
+ }
+ if (is_active) {
+ goto_map_iterate(goto_maps, goto_map, punit) {
+ int time = 0, i;
- for(i = 0; i < goto_map.num_parts; i++) {
- time += goto_map.parts[i].time;
+ for (i = 0; i < goto_map->num_parts; i++) {
+ time += goto_map->parts[i].time;
+ }
+
+ if (min) {
+ *min = MIN(*min, time);
+ }
+ if (max) {
+ *max = MAX(*max, time);
+ }
+ } goto_map_iterate_end;
}
-
- return time;
}
/**********************************************************************
@@ -784,10 +866,12 @@
{
assert(is_active);
- update_last_part(dest_tile);
+ goto_map_iterate(goto_maps, goto_map, punit) {
+ update_last_part(goto_map, dest_tile);
+ } goto_map_iterate_end;
/* Update goto data in info label. */
- update_unit_info_label(get_unit_in_focus());
+ update_unit_info_label(get_units_in_focus());
}
/****************************************************************************
@@ -918,118 +1002,118 @@
Send the current patrol route (i.e., the one generated via HOVER_STATE)
to the server.
**************************************************************************/
-void send_patrol_route(struct unit *punit)
+void send_patrol_route(void)
{
- int i;
- struct pf_path *path = NULL, *return_path;
- struct pf_parameter parameter = goto_map.template;
- struct pf_map *map;
-
assert(is_active);
- assert(punit->id == goto_map.unit_id);
+ goto_map_iterate(goto_maps, goto_map, punit) {
+ int i;
+ struct pf_path *path = NULL, *return_path;
+ struct pf_parameter parameter = goto_map->template;
+ struct pf_map *map;
- parameter.start_tile = goto_map.parts[goto_map.num_parts - 1].end_tile;
- parameter.moves_left_initially
- = goto_map.parts[goto_map.num_parts - 1].end_moves_left;
- parameter.fuel_left_initially
- = goto_map.parts[goto_map.num_parts - 1].end_fuel_left;
- map = pf_create_map(¶meter);
- return_path = pf_get_path(map, goto_map.parts[0].start_tile);
- if (!return_path) {
- die("No return path found!");
- }
+ parameter.start_tile = goto_map->parts[goto_map->num_parts - 1].end_tile;
+ parameter.moves_left_initially
+ = goto_map->parts[goto_map->num_parts - 1].end_moves_left;
+ parameter.fuel_left_initially
+ = goto_map->parts[goto_map->num_parts - 1].end_fuel_left;
+ map = pf_create_map(¶meter);
+ return_path = pf_get_path(map, goto_map->parts[0].start_tile);
+ if (!return_path) {
+ die("No return path found!");
+ }
- for (i = 0; i < goto_map.num_parts; i++) {
- path = pft_concat(path, goto_map.parts[i].path);
- }
- path = pft_concat(path, return_path);
+ for (i = 0; i < goto_map->num_parts; i++) {
+ path = pft_concat(path, goto_map->parts[i].path);
+ }
+ path = pft_concat(path, return_path);
- pf_destroy_map(map);
- pf_destroy_path(return_path);
+ pf_destroy_map(map);
+ pf_destroy_path(return_path);
- send_path_orders(punit, path, TRUE, TRUE, NULL);
+ send_path_orders(punit, path, TRUE, TRUE, NULL);
- pf_destroy_path(path);
+ pf_destroy_path(path);
+ } goto_map_iterate_end;
}
/**************************************************************************
Send the current connect route (i.e., the one generated via HOVER_STATE)
to the server.
**************************************************************************/
-void send_connect_route(struct unit *punit, enum unit_activity activity)
+void send_connect_route(enum unit_activity activity)
{
- struct pf_path *path = NULL;
- int i;
- struct packet_unit_orders p;
- struct tile *old_tile;
-
assert(is_active);
- assert(punit->id == goto_map.unit_id);
+ goto_map_iterate(goto_maps, goto_map, punit) {
+ struct pf_path *path = NULL;
+ int i;
+ struct packet_unit_orders p;
+ struct tile *old_tile;
- memset(&p, 0, sizeof(p));
+ memset(&p, 0, sizeof(p));
- for (i = 0; i < goto_map.num_parts; i++) {
- path = pft_concat(path, goto_map.parts[i].path);
- }
+ for (i = 0; i < goto_map->num_parts; i++) {
+ path = pft_concat(path, goto_map->parts[i].path);
+ }
- p.unit_id = punit->id;
- p.src_x = punit->tile->x;
- p.src_y = punit->tile->y;
- p.repeat = FALSE;
- p.vigilant = FALSE; /* Should be TRUE? */
+ p.unit_id = punit->id;
+ p.src_x = punit->tile->x;
+ p.src_y = punit->tile->y;
+ p.repeat = FALSE;
+ p.vigilant = FALSE; /* Should be TRUE? */
- p.length = 0;
- old_tile = path->positions[0].tile;
+ p.length = 0;
+ old_tile = path->positions[0].tile;
- for (i = 0; i < path->length; i++) {
- switch (activity) {
- case ACTIVITY_IRRIGATE:
- if (!tile_has_special(old_tile, S_IRRIGATION)) {
- /* Assume the unit can irrigate or we wouldn't be here. */
- p.orders[p.length] = ORDER_ACTIVITY;
- p.activity[p.length] = ACTIVITY_IRRIGATE;
- p.length++;
- }
- break;
- case ACTIVITY_ROAD:
- case ACTIVITY_RAILROAD:
- if (!tile_has_special(old_tile, S_ROAD)) {
- /* Assume the unit can build the road or we wouldn't be here. */
- p.orders[p.length] = ORDER_ACTIVITY;
- p.activity[p.length] = ACTIVITY_ROAD;
- p.length++;
- }
- if (activity == ACTIVITY_RAILROAD) {
- if (!tile_has_special(old_tile, S_RAILROAD)) {
- /* Assume the unit can build the rail or we wouldn't be here. */
+ for (i = 0; i < path->length; i++) {
+ switch (activity) {
+ case ACTIVITY_IRRIGATE:
+ if (!tile_has_special(old_tile, S_IRRIGATION)) {
+ /* Assume the unit can irrigate or we wouldn't be here. */
p.orders[p.length] = ORDER_ACTIVITY;
- p.activity[p.length] = ACTIVITY_RAILROAD;
+ p.activity[p.length] = ACTIVITY_IRRIGATE;
p.length++;
}
+ break;
+ case ACTIVITY_ROAD:
+ case ACTIVITY_RAILROAD:
+ if (!tile_has_special(old_tile, S_ROAD)) {
+ /* Assume the unit can build the road or we wouldn't be here. */
+ p.orders[p.length] = ORDER_ACTIVITY;
+ p.activity[p.length] = ACTIVITY_ROAD;
+ p.length++;
+ }
+ if (activity == ACTIVITY_RAILROAD) {
+ if (!tile_has_special(old_tile, S_RAILROAD)) {
+ /* Assume the unit can build the rail or we wouldn't be here. */
+ p.orders[p.length] = ORDER_ACTIVITY;
+ p.activity[p.length] = ACTIVITY_RAILROAD;
+ p.length++;
+ }
+ }
+ break;
+ default:
+ die("Invalid connect activity.");
+ break;
}
- break;
- default:
- die("Invalid connect activity.");
- break;
- }
- if (i != path->length - 1) {
- struct tile *new_tile = path->positions[i + 1].tile;
+ if (i != path->length - 1) {
+ struct tile *new_tile = path->positions[i + 1].tile;
- assert(!same_pos(new_tile, old_tile));
+ assert(!same_pos(new_tile, old_tile));
- p.orders[p.length] = ORDER_MOVE;
- p.dir[p.length] = get_direction_for_step(old_tile, new_tile);
- p.length++;
+ p.orders[p.length] = ORDER_MOVE;
+ p.dir[p.length] = get_direction_for_step(old_tile, new_tile);
+ p.length++;
- old_tile = new_tile;
+ old_tile = new_tile;
+ }
}
- }
- p.dest_x = old_tile->x;
- p.dest_y = old_tile->y;
+ p.dest_x = old_tile->x;
+ p.dest_y = old_tile->y;
- send_packet_unit_orders(&aconnection, &p);
+ send_packet_unit_orders(&aconnection, &p);
+ } goto_map_iterate_end;
}
/**************************************************************************
@@ -1037,27 +1121,27 @@
HOVER_STATE) to the server. The route might involve more than one
part if waypoints were used. FIXME: danger paths are not supported.
**************************************************************************/
-void send_goto_route(struct unit *punit)
+void send_goto_route(void)
{
- struct pf_path *path = NULL;
- int i;
-
assert(is_active);
- assert(punit->id == goto_map.unit_id);
+ goto_map_iterate(goto_maps, goto_map, punit) {
+ struct pf_path *path = NULL;
+ int i;
- for (i = 0; i < goto_map.num_parts; i++) {
- path = pft_concat(path, goto_map.parts[i].path);
- }
+ for (i = 0; i < goto_map->num_parts; i++) {
+ path = pft_concat(path, goto_map->parts[i].path);
+ }
- if (goto_last_order == ORDER_LAST) {
- send_goto_path(punit, path, NULL);
- } else {
- struct unit_order order;
+ if (goto_last_order == ORDER_LAST) {
+ send_goto_path(punit, path, NULL);
+ } else {
+ struct unit_order order;
- order.order = goto_last_order;
- send_goto_path(punit, path, &order);
- }
- pf_destroy_path(path);
+ order.order = goto_last_order;
+ send_goto_path(punit, path, &order);
+ }
+ pf_destroy_path(path);
+ } goto_map_iterate_end;
}
/* ================= drawn functions ============================ */
Index: client/packhand.c
===================================================================
--- client/packhand.c (revision 11168)
+++ client/packhand.c (working copy)
@@ -115,6 +115,7 @@
} else {
punit->transported_by = -1;
}
+ punit->battlegroup = packet->battlegroup;
punit->has_orders = packet->has_orders;
punit->orders.length = packet->orders_length;
punit->orders.index = packet->orders_index;
@@ -227,7 +228,7 @@
client_remove_city(pcity);
/* update menus if the focus unit is on the tile. */
- if (get_unit_in_focus()) {
+ if (get_num_units_in_focus() > 0) {
update_menus();
}
}
@@ -360,7 +361,7 @@
update_info_label(); /* get initial population right */
update_unit_focus();
- update_unit_info_label(get_unit_in_focus());
+ update_unit_info_label(get_units_in_focus());
/* Find something sensible to display instead of the intro gfx. */
center_on_something();
@@ -392,7 +393,7 @@
bool need_units_dialog_update = FALSE;
struct city *pcity;
bool popup, update_descriptions = FALSE, name_changed = FALSE;
- struct unit *pfocus_unit = get_unit_in_focus();
+ struct unit_list *pfocus_units = get_units_in_focus();
pcity=find_city_by_id(packet->id);
@@ -554,8 +555,13 @@
}
/* Update focus unit info label if necessary. */
- if (name_changed && pfocus_unit && pfocus_unit->homecity == pcity->id) {
- update_unit_info_label(pfocus_unit);
+ if (name_changed) {
+ unit_list_iterate(pfocus_units, pfocus_unit) {
+ if (pfocus_unit->homecity == pcity->id) {
+ update_unit_info_label(pfocus_units);
+ break;
+ }
+ } unit_list_iterate_end;
}
/* Update the units dialog if necessary. */
@@ -622,11 +628,8 @@
}
/* update menus if the focus unit is on the tile. */
- {
- struct unit *punit = get_unit_in_focus();
- if (punit && same_pos(punit->tile, pcity->tile)) {
- update_menus();
- }
+ if (get_focus_unit_on_tile(pcity->tile)) {
+ update_menus();
}
if(is_new) {
@@ -775,7 +778,7 @@
update_unit_focus();
auto_center_on_focus_unit();
- update_unit_info_label(get_unit_in_focus());
+ update_unit_info_label(get_units_in_focus());
update_menus();
set_seconds_to_turndone(game.info.timeout);
@@ -961,7 +964,6 @@
bool check_focus = FALSE; /* conservative focus change */
bool moved = FALSE;
bool ret = FALSE;
- struct unit *focus_unit = get_unit_in_focus();
punit = player_find_unit_by_id(packet_unit->owner, packet_unit->id);
if (!punit && find_unit_by_id(packet_unit->id)) {
@@ -973,12 +975,13 @@
if (punit) {
ret = TRUE;
punit->activity_count = packet_unit->activity_count;
+ unit_change_battlegroup(punit, packet_unit->battlegroup);
if (punit->ai.control != packet_unit->ai.control) {
punit->ai.control = packet_unit->ai.control;
repaint_unit = TRUE;
/* AI is set: may change focus */
/* AI is cleared: keep focus */
- if (packet_unit->ai.control && punit == get_unit_in_focus()) {
+ if (packet_unit->ai.control && unit_is_in_focus(punit)) {
check_focus = TRUE;
}
}
@@ -997,7 +1000,7 @@
/* May change focus if focus unit gets a new activity.
* But if new activity is Idle, it means user specifically selected
* the unit */
- if (punit == get_unit_in_focus()
+ if (unit_is_in_focus(punit)
&& (packet_unit->activity != ACTIVITY_IDLE
|| packet_unit->has_orders)) {
check_focus = TRUE;
@@ -1013,9 +1016,8 @@
&& punit->activity == ACTIVITY_SENTRY
&& packet_unit->activity == ACTIVITY_IDLE
&& is_player_phase(game.player_ptr, game.info.phase)
- && (!get_unit_in_focus()
/* only 1 wakeup focus per tile is useful */
- || !same_pos(packet_unit->tile, get_unit_in_focus()->tile))) {
+ && !get_focus_unit_on_tile(packet_unit->tile)) {
set_unit_focus(punit);
check_focus = FALSE; /* and keep it */
@@ -1031,7 +1033,7 @@
punit->transported_by = packet_unit->transported_by;
if (punit->occupy != packet_unit->occupy
- && focus_unit && focus_unit->tile == packet_unit->tile) {
+ && get_focus_unit_on_tile(packet_unit->tile)) {
/* Special case: (un)loading a unit in a transporter on the
* same tile as the focus unit may (dis)allow the focus unit to be
* loaded. Thus the orders->(un)load menu item needs updating. */
@@ -1059,7 +1061,7 @@
/* These two lines force the menus to be updated as appropriate when
* the focus unit changes. */
- if (punit == get_unit_in_focus()) {
+ if (unit_is_in_focus(punit)) {
need_update_menus = TRUE;
}
@@ -1094,7 +1096,7 @@
if (pcity && (pcity->id != punit->homecity)) {
refresh_city_dialog(pcity);
}
- if(punit == get_unit_in_focus()) {
+ if (unit_is_in_focus(punit)) {
/* Update the orders menu -- the unit might have new abilities */
need_update_menus = TRUE;
}
@@ -1102,7 +1104,7 @@
/* May change focus if an attempted move or attack exhausted unit */
if (punit->moves_left != packet_unit->moves_left
- && punit == get_unit_in_focus()) {
+ && unit_is_in_focus(punit)) {
check_focus = TRUE;
}
@@ -1206,6 +1208,8 @@
unit_list_prepend(punit->owner->units, punit);
unit_list_prepend(punit->tile->units, punit);
+ unit_register_battlegroup(punit);
+
if((pcity=find_city_by_id(punit->homecity))) {
unit_list_prepend(pcity->units_supported, punit);
}
@@ -1226,20 +1230,17 @@
assert(punit != NULL);
- if (punit == get_unit_in_focus()) {
- update_unit_info_label(punit);
- } else if (get_unit_in_focus()
- && (same_pos(get_unit_in_focus()->tile, punit->tile)
- || (moved
- && same_pos(get_unit_in_focus()->tile, old_tile)))) {
- update_unit_info_label(get_unit_in_focus());
+ if (unit_is_in_focus(punit)
+ || get_focus_unit_on_tile(punit->tile)
+ || (moved && get_focus_unit_on_tile(old_tile))) {
+ update_unit_info_label(get_units_in_focus());
}
if (repaint_unit) {
refresh_unit_mapcanvas(punit, punit->tile, TRUE, FALSE);
}
- if ((check_focus || get_unit_in_focus() == NULL)
+ if ((check_focus || get_num_units_in_focus() == 0)
&& game.player_ptr
&& !game.player_ptr->ai.control
&& is_player_phase(game.player_ptr, game.info.phase)) {
@@ -1529,7 +1530,7 @@
/* If we just learned bridge building and focus is on a settler
on a river the road menu item will remain disabled unless we
do this. (applys in other cases as well.) */
- if (get_unit_in_focus()) {
+ if (get_num_units_in_focus() > 0) {
update_menus();
}
}
@@ -2014,8 +2015,7 @@
/* update menus if the focus unit is on the tile. */
if (tile_changed) {
- struct unit *punit = get_unit_in_focus();
- if (punit && same_pos(punit->tile, ptile)) {
+ if (get_focus_unit_on_tile(ptile)) {
update_menus();
}
}
Index: client/goto.h
===================================================================
--- client/goto.h (revision 11168)
+++ client/goto.h (working copy)
@@ -19,11 +19,11 @@
void init_client_goto(void);
void free_client_goto(void);
-void enter_goto_state(struct unit *punit);
+void enter_goto_state(struct unit_list *punits);
void exit_goto_state(void);
bool goto_is_active(void);
struct tile *get_line_dest(void);
-int get_goto_turns(void);
+void goto_get_turns(int *min, int *max);
void goto_add_waypoint(void);
bool goto_pop_waypoint(void);
@@ -36,9 +36,9 @@
void send_goto_path(struct unit *punit, struct pf_path *path,
struct unit_order *last_order);
bool send_goto_tile(struct unit *punit, struct tile *ptile);
-void send_patrol_route(struct unit *punit);
-void send_goto_route(struct unit *punit);
-void send_connect_route(struct unit *punit, enum unit_activity activity);
+void send_patrol_route(void);
+void send_goto_route(void);
+void send_connect_route(enum unit_activity activity);
struct pf_path *path_to_nearest_allied_city(struct unit *punit);
Index: client/tilespec.c
===================================================================
--- client/tilespec.c (revision 11168)
+++ client/tilespec.c (working copy)
@@ -190,6 +190,7 @@
*transform,
*connect,
*patrol,
+ *battlegroup[MAX_NUM_BATTLEGROUPS],
*lowfuel,
*tired;
} unit;
@@ -1861,7 +1862,7 @@
***********************************************************************/
static void tileset_lookup_sprite_tags(struct tileset *t)
{
- char buffer[512];
+ char buffer[512], buffer2[512];
const char dir_char[] = "nsew";
const int W = t->normal_tile_width, H = t->normal_tile_height;
int i, j;
@@ -2040,6 +2041,12 @@
SET_SPRITE(unit.transform, "unit.transform");
SET_SPRITE(unit.connect, "unit.connect");
SET_SPRITE(unit.patrol, "unit.patrol");
+ for (i = 0; i < MAX_NUM_BATTLEGROUPS; i++) {
+ my_snprintf(buffer, sizeof(buffer), "unit.battlegroup_%d", i);
+ my_snprintf(buffer2, sizeof(buffer2), "city.size_%d", i + 1);
+ assert(MAX_NUM_BATTLEGROUPS < NUM_TILES_DIGITS);
+ SET_SPRITE_ALT(unit.battlegroup[i], buffer, buffer2);
+ }
SET_SPRITE(unit.lowfuel, "unit.lowfuel");
SET_SPRITE(unit.tired, "unit.tired");
@@ -2086,8 +2093,6 @@
SET_SPRITE(city.disorder, "city.disorder");
for(i=0; i<NUM_TILES_DIGITS; i++) {
- char buffer2[512];
-
my_snprintf(buffer, sizeof(buffer), "city.size_%d", i);
SET_SPRITE(city.size[i], buffer);
my_snprintf(buffer2, sizeof(buffer2), "path.turns_%d", i);
@@ -2926,6 +2931,10 @@
}
}
+ if (punit->battlegroup != BATTLEGROUP_NONE) {
+ ADD_SPRITE_FULL(t->sprites.unit.battlegroup[punit->battlegroup]);
+ }
+
if (t->sprites.unit.lowfuel
&& unit_type(punit)->fuel > 0
&& punit->fuel == 1
@@ -3660,7 +3669,7 @@
bool known[NUM_EDGE_TILES], city[NUM_EDGE_TILES];
bool unit[NUM_EDGE_TILES], worked[NUM_EDGE_TILES];
int i;
- struct unit *pfocus = get_unit_in_focus();
+ struct unit_list *pfocus_units = get_units_in_focus();
for (i = 0; i < NUM_EDGE_TILES; i++) {
const struct tile *tile = pedge->tile[i];
@@ -3668,9 +3677,18 @@
int dummy_x, dummy_y;
known[i] = tile && client_tile_get_known(tile) != TILE_UNKNOWN;
- unit[i] = tile && pfocus && unit_flag(pfocus, F_CITIES)
- && city_can_be_built_here(pfocus->tile, pfocus)
- && base_map_to_city_map(&dummy_x, &dummy_y, pfocus->tile, tile);
+ unit[i] = FALSE;
+ if (tile) {
+ unit_list_iterate(pfocus_units, pfocus_unit) {
+ if (unit_flag(pfocus_unit, F_CITIES)
+ && city_can_be_built_here(pfocus_unit->tile, pfocus_unit)
+ && base_map_to_city_map(&dummy_x, &dummy_y,
+ pfocus_unit->tile, tile)) {
+ unit[i] = TRUE;
+ break;
+ }
+ } unit_list_iterate_end;
+ }
worked[i] = FALSE;
city[i] = (tile
@@ -3778,10 +3796,11 @@
return 0;
}
if (ptile && ptile == get_line_dest()) {
- int length = get_goto_turns();
+ int length;
int units = length % NUM_TILES_DIGITS;
int tens = (length / 10) % NUM_TILES_DIGITS;
+ goto_get_turns(NULL, &length);
if (length >= 100) {
static bool reported = FALSE;
@@ -3830,14 +3849,14 @@
struct terrain *pterrain = NULL, *tterrain_near[8];
bv_special tspecial, tspecial_near[8];
int tileno, dir;
- struct unit *pfocus = get_unit_in_focus();
struct drawn_sprite *save_sprs = sprs;
struct player *owner = NULL;
/* Unit drawing is disabled if the view options is turned off, but only
* if we're drawing on the mapview. */
bool do_draw_unit = (punit && (draw_units || !ptile
- || (draw_focus_unit && pfocus == punit)));
+ || (draw_focus_unit
+ && unit_is_in_focus(punit))));
bool solid_bg = (solid_color_behind_units
&& (do_draw_unit
|| (pcity && draw_cities)
@@ -4030,12 +4049,11 @@
case LAYER_UNIT:
case LAYER_FOCUS_UNIT:
- if (do_draw_unit && XOR(layer == LAYER_UNIT,
- punit == get_unit_in_focus())) {
+ if (do_draw_unit && XOR(layer == LAYER_UNIT, unit_is_in_focus(punit))) {
bool stacked = ptile && (unit_list_size(ptile->units) > 1);
bool backdrop = !pcity;
- if (ptile && punit == get_unit_in_focus()
+ if (ptile && unit_is_in_focus(punit)
&& t->sprites.unit.select[0]) {
/* Special case for drawing the selection rectangle. The blinking
* unit is handled separately, inside get_drawable_unit(). */
@@ -4279,7 +4297,6 @@
const struct city *citymode)
{
struct unit *punit = find_visible_unit(ptile);
- struct unit *pfocus = get_unit_in_focus();
if (!punit)
return NULL;
@@ -4287,9 +4304,8 @@
if (citymode && punit->owner == citymode->owner)
return NULL;
- if (punit != pfocus
- || t->sprites.unit.select[0] || focus_unit_state == 0
- || !same_pos(punit->tile, pfocus->tile))
+ if (!unit_is_in_focus(punit)
+ || t->sprites.unit.select[0] || focus_unit_state == 0)
return punit;
else
return NULL;
Index: client/mapctrl_common.c
===================================================================
--- client/mapctrl_common.c (revision 11168)
+++ client/mapctrl_common.c (working copy)
@@ -49,6 +49,7 @@
bool rbutton_down = FALSE;
bool rectangle_active = FALSE;
+static bool rectangle_append;
/* This changes the behaviour of left mouse
button in Area Selection mode. */
@@ -79,7 +80,8 @@
anchor is not the drawing start point, but is used to calculate
width, height. Also record the current mapview centering.
**************************************************************************/
-void anchor_selection_rectangle(int canvas_x, int canvas_y)
+void anchor_selection_rectangle(int canvas_x, int canvas_y,
+ bool append)
{
struct tile *ptile = canvas_pos_to_nearest_tile(canvas_x, canvas_y);
@@ -89,6 +91,7 @@
/* FIXME: This may be off-by-one. */
rec_canvas_center_tile = get_center_tile_mapcanvas();
rec_w = rec_h = 0;
+ rectangle_append = append;
}
/**************************************************************************
@@ -112,8 +115,8 @@
/* Iteration direction */
const int inc_x = (rec_w > 0 ? half_W : -half_W);
const int inc_y = (rec_h > 0 ? half_H : -half_H);
-
int x, y, x2, y2, xx, yy;
+ int units = 0;
y = rec_corner_y;
for (yy = 0; yy <= segments_y; yy++, y += inc_y) {
@@ -144,9 +147,20 @@
/* Tile passed all tests; process it.
*/
if (ptile->city && ptile->city->owner == game.player_ptr) {
+ /* FIXME: handle rectangle_append */
map_deco[ptile->index].hilite = HILITE_CITY;
tiles_hilited_cities = TRUE;
}
+ unit_list_iterate(ptile->units, punit) {
+ if (punit->owner == game.player_ptr) {
+ if (units == 0 && !rectangle_append) {
+ set_unit_focus(punit);
+ } else {
+ add_unit_focus(punit);
+ }
+ units++;
+ }
+ } unit_list_iterate_end;
}
}
@@ -227,6 +241,7 @@
}
/* It is currently drawn only to the screen, not backing store */
+ rectangle_active = TRUE;
draw_selection_rectangle(canvas_x, canvas_y, rec_w, rec_h);
rec_corner_x = canvas_x;
rec_corner_y = canvas_y;
@@ -427,12 +442,9 @@
struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
if (keyboardless_goto_active && hover_state == HOVER_GOTO && ptile) {
- struct unit *punit =
- player_find_unit_by_id(game.player_ptr, hover_unit);
-
do_unit_goto(ptile);
set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
- update_unit_info_label(punit);
+ update_unit_info_label(hover_units);
}
keyboardless_goto_active = FALSE;
keyboardless_goto_button_down = FALSE;
@@ -447,7 +459,7 @@
{
struct tile *ptile = canvas_pos_to_tile(canvas_x, canvas_y);
- if (ptile && get_unit_in_focus()
+ if (ptile && get_num_units_in_focus() > 0
&& !same_pos(keyboardless_goto_start_tile, ptile)
&& can_client_issue_orders()) {
keyboardless_goto_active = TRUE;
@@ -658,7 +670,7 @@
{
struct unit *my_unit, *defender, *attacker;
- if (!(my_unit = get_unit_in_focus())
+ if (!unit_is_in_focus(my_unit)
|| !(defender = get_defender(my_unit, ptile))
|| !(attacker = get_attacker(my_unit, ptile))) {
return FALSE;
Index: client/mapctrl_common.h
===================================================================
--- client/mapctrl_common.h (revision 11168)
+++ client/mapctrl_common.h (working copy)
@@ -27,7 +27,7 @@
extern bool keyboardless_goto_active;
extern struct tile *keyboardless_goto_start_tile;
-void anchor_selection_rectangle(int canvas_x, int canvas_y);
+void anchor_selection_rectangle(int canvas_x, int canvas_y, bool append);
void update_selection_rectangle(int canvas_x, int canvas_y);
bool is_city_hilited(struct city *pcity);
Index: client/mapview_common.c
===================================================================
--- client/mapview_common.c (revision 11168)
+++ client/mapview_common.c (working copy)
@@ -1733,9 +1733,9 @@
return;
}
- if (punit == get_unit_in_focus() && hover_state != HOVER_NONE) {
+ if (unit_is_in_focus(punit) && hover_state != HOVER_NONE) {
set_hover_state(NULL, HOVER_NONE, ACTIVITY_LAST, ORDER_LAST);
- update_unit_info_label(punit);
+ update_unit_info_label(get_units_in_focus());
}
dest_x = src_tile->x + dx;
Index: client/civclient.c
===================================================================
--- client/civclient.c (revision 11168)
+++ client/civclient.c (working copy)
@@ -680,7 +680,7 @@
time_until_next_call = MIN(time_until_next_call, blink_time);
}
- if (get_unit_in_focus()) {
+ if (get_num_units_in_focus() > 0) {
double blink_time = blink_active_unit();
time_until_next_call = MIN(time_until_next_call, blink_time);
Index: client/climisc.c
===================================================================
--- client/climisc.c (revision 11168)
+++ client/climisc.c (working copy)
@@ -74,28 +74,23 @@
struct city *pcity;
struct tile *ptile = punit->tile;
int hc = punit->homecity;
- struct unit *ufocus = get_unit_in_focus();
struct unit old_unit = *punit;
+ int old = get_num_units_in_focus();
+ bool update;
freelog(LOG_DEBUG, "removing unit %d, %s %s (%d %d) hcity %d",
punit->id, get_nation_name(unit_owner(punit)->nation),
unit_name(punit->type), TILE_XY(punit->tile), hc);
- if (punit == ufocus) {
- set_unit_focus(NULL);
- game_remove_unit(punit);
- punit = ufocus = NULL;
+ update = (get_focus_unit_on_tile(punit->tile) != NULL);
+ control_unit_killed(punit);
+ game_remove_unit(punit);
+ punit = NULL;
+ if (old > 0 && get_num_units_in_focus() == 0) {
advance_unit_focus();
- } else {
- /* calculate before punit disappears, use after punit removed: */
- bool update = (ufocus
- && same_pos(ufocus->tile, punit->tile));
-
- game_remove_unit(punit);
- punit = NULL;
- if (update) {
- update_unit_pix_label(ufocus);
- }
+ } else if (update) {
+ update_unit_pix_label(get_units_in_focus());
+ update_unit_info_label(get_units_in_focus());
}
pcity = tile_get_city(ptile);
@@ -409,8 +404,8 @@
}
can_slide = FALSE;
- if ((punit = get_unit_in_focus())) {
- center_tile_mapcanvas(punit->tile);
+ if (get_num_units_in_focus() > 0) {
+ center_tile_mapcanvas(unit_list_get(get_units_in_focus(), 0)->tile);
} else if (game.player_ptr && (pcity = find_palace(game.player_ptr))) {
/* Else focus on the capital. */
center_tile_mapcanvas(pcity->tile);
@@ -1081,3 +1076,18 @@
}
dsend_packet_player_rates(&aconnection, tax, lux, sci);
}
+
+/****************************************************************************
+ Returns TRUE if any of the units can do the connect activity.
+****************************************************************************/
+bool can_units_do_connect(struct unit_list *punits,
+ enum unit_activity activity)
+{
+ unit_list_iterate(punits, punit) {
+ if (can_unit_do_connect(punit, activity)) {
+ return TRUE;
+ }
+ } unit_list_iterate_end;
+
+ return FALSE;
+}
Index: client/climisc.h
===================================================================
--- client/climisc.h (revision 11168)
+++ client/climisc.h (working copy)
@@ -113,4 +113,7 @@
void cityrep_buy(struct city *pcity);
void common_taxrates_callback(int i);
+bool can_units_do_connect(struct unit_list *punits,
+ enum unit_activity activity);
+
#endif /* FC__CLIMISC_H */
- [Freeciv-Dev] (PR#14365) battlegroups..., (continued)
- [Freeciv-Dev] (PR#14365) battlegroups..., Mateusz Stefek, 2005/10/19
- [Freeciv-Dev] Re: (PR#14365) battlegroups..., Jason Short, 2005/10/19
- [Freeciv-Dev] Re: (PR#14365) battlegroups..., Andreas Røsdal, 2005/10/20
- [Freeciv-Dev] Re: (PR#14365) battlegroups..., Jason Short, 2005/10/20
- [Freeciv-Dev] (PR#14365) battlegroups..., Mateusz Stefek, 2005/10/20
- [Freeciv-Dev] Re: (PR#14365) battlegroups..., Andreas Røsdal, 2005/10/20
- [Freeciv-Dev] Re: (PR#14365) battlegroups..., Jason Short, 2005/10/20
- [Freeciv-Dev] Re: (PR#14365) battlegroups..., Jason Short, 2005/10/21
- [Freeciv-Dev] (PR#14365) battlegroups..., Mateusz Stefek, 2005/10/21
- [Freeciv-Dev] Re: (PR#14365) battlegroups..., Jason Short, 2005/10/21
- [Freeciv-Dev] Re: (PR#14365) battlegroups...,
Jason Short <=
- [Freeciv-Dev] Re: (PR#14365) battlegroups..., Jason Short, 2005/10/22
|
|