Complete.Org: Mailing Lists: Archives: freeciv-dev: January 2004:
[Freeciv-Dev] Re: (PR#7131) client orders to replace client goto
Home

[Freeciv-Dev] Re: (PR#7131) client orders to replace client goto

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: undisclosed-recipients: ;
Subject: [Freeciv-Dev] Re: (PR#7131) client orders to replace client goto
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Mon, 19 Jan 2004 00:38:54 -0800
Reply-to: rt@xxxxxxxxxxx

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

Raimar Falke wrote:

> I have no more problems with the patch. But I haven't tested it.

Four bugs fixed:

- Don't clear orders when changing the activity.  In the future it will 
be possible to have an activity while under orders.

- Don't sentry a unit when boarding a ship unless the orders are over. 
This check is now duplicated in move_unit and execute_orders (since 
orders code shouldn't go into move_unit).  In the future it can be 
removed from move_unit.

- Don't focus a unit when it's under orders.  One additional place in 
find_best_focus_candidate had to check for unit_has_orders().  Otherwise 
if _every_ unit had orders one of them would be focused.

- Reinstate the enum activity_type enumeration.  Since these values are 
put in the savegame we can't just remove ACTIVITY_PATROL without 
completely breaking things.  Instead it's renamed as 
ACTIVITY_PATROL_UNUSED and checked for when loading savegames.

Savegame behavior is now like this:

- When loading an old game, patrol routes are lost.
- When loading an old game, goto routes are changed into server goto.
- When loading a new game in an old server, patrol and goto routes are lost.

jason

Index: client/control.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/control.c,v
retrieving revision 1.123
diff -u -r1.123 control.c
--- client/control.c    2004/01/09 16:59:50     1.123
+++ client/control.c    2004/01/19 08:36:17
@@ -129,6 +129,10 @@
     punit->focus_status=FOCUS_AVAIL;
     refresh_tile_mapcanvas(punit->x, punit->y, 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->activity = ACTIVITY_IDLE;
       punit->ai.control = FALSE;
@@ -180,6 +184,7 @@
 {
   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 
@@ -269,6 +274,7 @@
     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) {
@@ -392,18 +398,23 @@
 void update_unit_pix_label(struct unit *punit)
 {
   static enum unit_activity prev_activity = ACTIVITY_UNKNOWN;
+  static bool prev_has_orders = FALSE;
   static Unit_Type_id prev_unit_type = U_LAST;
   static int prev_hp = -1;              /* or could store ihp cf tilespec.c */
   
   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 (punit->type != prev_unit_type
        || punit->activity != prev_activity
+       || punit->has_orders != prev_has_orders
        || punit->hp != prev_hp) {
       set_unit_icon(-1, punit);
       prev_unit_type = punit->type;
       prev_activity = punit->activity;
+      prev_has_orders = punit->has_orders;
       prev_hp = punit->hp;
     }
 
@@ -430,6 +441,7 @@
   else {
     prev_unit_type = U_LAST;
     prev_activity = ACTIVITY_UNKNOWN;
+    prev_has_orders = FALSE;
     prev_hp = -1;
     for(i=-1; i<num_units_below; i++) {
       set_unit_icon(i, NULL);
@@ -1194,6 +1206,7 @@
   
   if (game.player_idx == punit->owner
       && auto_center_on_unit
+      && !unit_has_orders(punit)
       && punit->activity != ACTIVITY_GOTO
       && punit->activity != ACTIVITY_SENTRY
       && !tile_visible_and_not_on_border_mapcanvas(target_unit->x,
Index: client/goto.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/goto.c,v
retrieving revision 1.65
diff -u -r1.65 goto.c
--- client/goto.c       2004/01/10 13:05:59     1.65
+++ client/goto.c       2004/01/19 08:36:18
@@ -464,30 +464,72 @@
   update_last_part(dest_x, dest_y);
 }
 
+/****************************************************************************
+  Send a packet to the server to request that the current orders be
+  cleared.
+****************************************************************************/
+void request_orders_cleared(struct unit *punit)
+{
+  struct packet_unit_orders p;
+
+  /* Clear the orders by sending an empty orders path. */
+  freelog(PACKET_LOG_LEVEL, "Clearing orders for unit %d.", punit->id);
+  p.unit_id = punit->id;
+  p.repeat = p.vigilant = FALSE;
+  p.length = 0;
+  p.dest_x = punit->x;
+  p.dest_y = punit->y;
+  send_packet_unit_orders(&aconnection, &p);
+}
+
 /**************************************************************************
   Send a path as a goto or patrol route to the server.
 **************************************************************************/
-static void send_path_route(struct unit *punit, struct pf_path *path,
-                           enum unit_activity activity)
+static void send_path_orders(struct unit *punit, struct pf_path *path,
+                            bool repeat, bool vigilant)
 {
-  struct packet_unit_route p;
-  int i;
+  struct packet_unit_orders p;
+  int i, old_x, old_y;
 
   p.unit_id = punit->id;
-  p.activity = activity;
+  p.repeat = repeat;
+  p.vigilant = vigilant;
+
+  freelog(PACKET_LOG_LEVEL, "Orders for unit %d:", punit->id);
 
-  /* we skip the start position */
+  /* We skip the start position. */
   p.length = path->length - 1;
   assert(p.length < MAX_LEN_ROUTE);
+  old_x = path->positions[0].x;
+  old_y = path->positions[0].y;
+
+  freelog(PACKET_LOG_LEVEL, "  Repeat: %d.  Vigilant: %d.  Length: %d",
+         p.repeat, p.vigilant, p.length);
 
+  /* If the path has n positions it takes n-1 steps. */
   for (i = 0; i < path->length - 1; i++) {
-    p.x[i] = path->positions[i + 1].x;
-    p.y[i] = path->positions[i + 1].y;
-    freelog(PACKET_LOG_LEVEL, "  packet[%d] = (%d,%d)",
-           i, p.x[i], p.y[i]);
+    int new_x = path->positions[i + 1].x;
+    int new_y = path->positions[i + 1].y;
+
+    if (same_pos(new_x, new_y, old_x, old_y)) {
+      p.orders[i] = ORDER_FINISH_TURN;
+      p.dir[i] = -1;
+      freelog(PACKET_LOG_LEVEL, "  packet[%d] = wait: %d,%d",
+             i, old_x, old_y);
+    } else {
+      p.orders[i] = ORDER_MOVE;
+      p.dir[i] = get_direction_for_step(old_x, old_y, new_x, new_y);
+      freelog(PACKET_LOG_LEVEL, "  packet[%d] = move %s: %d,%d => %d,%d",
+             i, dir_get_name(p.dir[i]), old_x, old_y, new_x, new_y);
+    }
+    old_x = new_x;
+    old_y = new_y;
   }
+
+  p.dest_x = old_x;
+  p.dest_y = old_y;
 
-  send_packet_unit_route(&aconnection, &p);
+  send_packet_unit_orders(&aconnection, &p);
 }
 
 /**************************************************************************
@@ -495,7 +537,7 @@
 **************************************************************************/
 void send_goto_path(struct unit *punit, struct pf_path *path)
 {
-  send_path_route(punit, path, ACTIVITY_GOTO);
+  send_path_orders(punit, path, FALSE, FALSE);
 }
 
 /**************************************************************************
@@ -531,7 +573,7 @@
   pf_destroy_map(map);
   pf_destroy_path(return_path);
 
-  send_path_route(punit, path, ACTIVITY_PATROL);
+  send_path_orders(punit, path, TRUE, TRUE);
 
   pf_destroy_path(path);
 }
Index: client/goto.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/goto.h,v
retrieving revision 1.10
diff -u -r1.10 goto.h
--- client/goto.h       2003/08/11 02:31:38     1.10
+++ client/goto.h       2004/01/19 08:36:18
@@ -29,6 +29,7 @@
 void draw_line(int dest_x, int dest_y);
 int get_drawn(int x, int y, int dir);
 
+void request_orders_cleared(struct unit *punit);
 void send_goto_path(struct unit *punit, struct pf_path *path);
 void send_patrol_route(struct unit *punit);
 void send_goto_route(struct unit *punit);
Index: client/packhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v
retrieving revision 1.344
diff -u -r1.344 packhand.c
--- client/packhand.c   2004/01/18 17:55:26     1.344
+++ client/packhand.c   2004/01/19 08:36:18
@@ -116,6 +116,9 @@
   } else {
     punit->transported_by = 0;
   }
+  punit->has_orders = packet->has_orders;
+  punit->orders.repeat = packet->repeat;
+  punit->orders.vigilant = packet->vigilant;
   return punit;
 }
 
@@ -936,14 +939,18 @@
     }
 
     if (punit->activity != packet_unit->activity
-        || punit->activity_target != packet_unit->activity_target) {
+        || punit->activity_target != packet_unit->activity_target
+       || punit->has_orders != packet_unit->has_orders
+       || punit->orders.repeat != packet_unit->orders.repeat
+       || punit->orders.vigilant != packet_unit->orders.vigilant) {
       /*** Change in activity or activity's target. ***/
 
       /* 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()
-         && packet_unit->activity != ACTIVITY_IDLE) {
+         && (packet_unit->activity != ACTIVITY_IDLE
+             || packet_unit->has_orders)) {
         check_focus = TRUE;
       }
 
@@ -972,6 +979,9 @@
 
       punit->activity = packet_unit->activity;
       punit->activity_target = packet_unit->activity_target;
+      punit->has_orders = packet_unit->has_orders;
+      punit->orders.repeat = packet_unit->orders.repeat;
+      punit->orders.vigilant = packet_unit->orders.vigilant;
 
       if (punit->owner == game.player_idx) {
         refresh_unit_city_dialogs(punit);
@@ -1072,9 +1082,7 @@
         if((unit_flag(punit, F_TRADE_ROUTE) || unit_flag(punit, F_HELP_WONDER))
           && (!game.player_ptr->ai.control || ai_popup_windows)
           && punit->owner==game.player_idx
-          && (punit->activity!=ACTIVITY_GOTO
-              || same_pos(goto_dest_x(punit), goto_dest_y(punit),
-                          pcity->x, pcity->y))
+          && !unit_has_orders(punit)
           && (unit_can_help_build_wonder_here(punit)
               || unit_can_est_traderoute_here(punit))) {
          process_caravan_arrival(punit);
Index: client/tilespec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v
retrieving revision 1.136
diff -u -r1.136 tilespec.c
--- client/tilespec.c   2004/01/16 16:01:25     1.136
+++ client/tilespec.c   2004/01/19 08:36:19
@@ -1509,8 +1509,12 @@
     ADD_SPRITE_SIMPLE(sprites.unit.connect);
   }
 
-  if (punit->activity == ACTIVITY_PATROL) {
-    ADD_SPRITE_SIMPLE(sprites.unit.patrol);
+  if (unit_has_orders(punit)) {
+    if (punit->orders.repeat) {
+      ADD_SPRITE_SIMPLE(sprites.unit.patrol);
+    } else {
+      ADD_SPRITE_SIMPLE(sprites.unit.go_to);
+    }
   }
 
   if (stack) {
Index: client/gui-gtk/menu.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/menu.c,v
retrieving revision 1.80
diff -u -r1.80 menu.c
--- client/gui-gtk/menu.c       2003/11/06 11:54:34     1.80
+++ client/gui-gtk/menu.c       2004/01/19 08:36:19
@@ -1146,8 +1146,6 @@
                           can_unit_do_activity(punit, ACTIVITY_EXPLORE));
       menus_set_sensitive("<main>/_Orders/_Connect",
                           can_unit_do_connect(punit, ACTIVITY_IDLE));
-      menus_set_sensitive("<main>/_Orders/Patrol (_Q)",
-                          can_unit_do_activity(punit, ACTIVITY_PATROL));
       menus_set_sensitive("<main>/_Orders/Return to nearest city",
                          !(is_air_unit(punit) || is_heli_unit(punit)));
       menus_set_sensitive("<main>/_Orders/_Disband Unit",
Index: client/gui-gtk-2.0/menu.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/menu.c,v
retrieving revision 1.25
diff -u -r1.25 menu.c
--- client/gui-gtk-2.0/menu.c   2003/11/09 04:06:50     1.25
+++ client/gui-gtk-2.0/menu.c   2004/01/19 08:36:19
@@ -1175,8 +1175,6 @@
                           can_unit_do_activity(punit, ACTIVITY_EXPLORE));
       menus_set_sensitive("<main>/_Orders/_Connect",
                           can_unit_do_connect(punit, ACTIVITY_IDLE));
-      menus_set_sensitive("<main>/_Orders/Patrol (_Q)",
-                          can_unit_do_activity(punit, ACTIVITY_PATROL));
       menus_set_sensitive("<main>/_Orders/Return to nearest city",
                          !(is_air_unit(punit) || is_heli_unit(punit)));
       menus_set_sensitive("<main>/_Orders/Diplomat\\/Spy Actions",
Index: client/gui-mui/gui_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-mui/gui_main.c,v
retrieving revision 1.79
diff -u -r1.79 gui_main.c
--- client/gui-mui/gui_main.c   2003/11/19 17:30:51     1.79
+++ client/gui-mui/gui_main.c   2004/01/19 08:36:19
@@ -1259,7 +1259,6 @@
       menu_entry_sensitive(MENU_ORDER_AUTO_ATTACK, (can_unit_do_auto(punit) && 
!unit_flag(punit, F_SETTLERS)));
       menu_entry_sensitive(MENU_ORDER_AUTO_EXPLORE, 
can_unit_do_activity(punit, ACTIVITY_EXPLORE));
       menu_entry_sensitive(MENU_ORDER_CONNECT, can_unit_do_connect(punit, 
ACTIVITY_IDLE));
-      menu_entry_sensitive(MENU_ORDER_PATROL, can_unit_do_activity(punit, 
ACTIVITY_PATROL));
       menu_entry_sensitive(MENU_ORDER_GOTO_CITY, any_cities);
       menu_entry_sensitive(MENU_ORDER_BUILD_WONDER, 
unit_can_help_build_wonder_here(punit));
       menu_entry_sensitive(MENU_ORDER_TRADEROUTE, 
unit_can_est_traderoute_here(punit));
Index: client/gui-sdl/menu.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-sdl/menu.c,v
retrieving revision 1.23
diff -u -r1.23 menu.c
--- client/gui-sdl/menu.c       2003/10/02 21:37:44     1.23
+++ client/gui-sdl/menu.c       2004/01/19 08:36:20
@@ -1198,12 +1198,6 @@
        local_hide(ID_UNIT_ORDER_CONNECT);
       }
 
-      if (can_unit_do_activity(pUnit, ACTIVITY_PATROL)) {
-       local_show(ID_UNIT_ORDER_PATROL);
-      } else {
-       local_hide(ID_UNIT_ORDER_PATROL);
-      }
-
       if (is_diplomat_unit(pUnit) &&
          diplomat_can_do_action(pUnit, DIPLOMAT_ANY_ACTION, pUnit->x,
                                 pUnit->y)) {
Index: client/gui-xaw/menu.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-xaw/menu.c,v
retrieving revision 1.58
diff -u -r1.58 menu.c
--- client/gui-xaw/menu.c       2003/12/01 19:32:04     1.58
+++ client/gui-xaw/menu.c       2004/01/19 08:36:20
@@ -369,8 +369,6 @@
                           can_unit_do_activity(punit, ACTIVITY_EXPLORE));
       menu_entry_sensitive(MENU_ORDER, MENU_ORDER_CONNECT, 
                           can_unit_do_connect(punit, ACTIVITY_IDLE));
-      menu_entry_sensitive(MENU_ORDER, MENU_ORDER_PATROL, 
-                          can_unit_do_activity(punit, ACTIVITY_PATROL));
       menu_entry_sensitive(MENU_ORDER, MENU_ORDER_GOTO_CITY,
                           any_cities);
       menu_entry_sensitive(MENU_ORDER, MENU_ORDER_BUILD_WONDER,
Index: common/capstr.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/capstr.c,v
retrieving revision 1.151
diff -u -r1.151 capstr.c
--- common/capstr.c     2004/01/11 17:45:03     1.151
+++ common/capstr.c     2004/01/19 08:36:20
@@ -74,13 +74,15 @@
  * are not directly related to the capability strings discussed here.)
  */
 
-#define CAPABILITY "+1.14.delta +last_turns_shield_surplus veteran"
+#define CAPABILITY "+1.14.delta +last_turns_shield_surplus veteran +orders"
 
 /* "+1.14.delta" is the new delta protocol for 1.14.0-dev.
  *
  * "last_turns_shield_surplus" means the surplus from the previous turn is
  * tracked by the server and sent to the client.  This information is used
  * in determining penalties when switching production.
+ *
+ * "orders" means client orders is used for client-side goto and patrol.
  */
 
 void init_our_capability(void)
Index: common/map.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/map.h,v
retrieving revision 1.168
diff -u -r1.168 map.h
--- common/map.h        2004/01/18 16:49:17     1.168
+++ common/map.h        2004/01/19 08:36:20
@@ -36,17 +36,6 @@
   int x,y;
 };
 
-struct goto_route {
-  int first_index; /* first valid tile pos */
-  int last_index; /* point to the first non_legal pos. Note that the pos
-                  is always alloced in the pos array (for coding reasons) */
-  int length; /* length of pos array (use this as modulus when iterating)
-                Note that this is always at least 1 greater than the number
-                of valid positions, to make comparing first_index with
-                last_index during wrapped iteration easier. */
-  struct map_position *pos;
-};
-
 /* For client Area Selection */
 enum tile_hilite {
   HILITE_NONE = 0, HILITE_CITY
@@ -558,6 +547,11 @@
 };
 
 BV_DEFINE(dir_vector, 8);
+
+struct unit_order {
+  enum unit_orders order;
+  enum direction8 dir;         /* Only valid for ORDER_MOVE. */
+};
 
 /* return the reverse of the direction */
 #define DIR_REVERSE(dir) (7 - (dir))
Index: common/packets.def
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets.def,v
retrieving revision 1.6
diff -u -r1.6 packets.def
--- common/packets.def  2004/01/14 11:58:12     1.6
+++ common/packets.def  2004/01/19 08:36:20
@@ -174,6 +174,8 @@
 type REPORT_TYPE       = uint8(enum report_type)
 type AUTH_TYPE         = uint8(enum authentication_type)
 type IMPR_RANGE                = uint8(enum impr_range)
+type DIRECTION         = uint8(enum direction8)
+type ORDERS            = uint8(enum unit_orders)
 
 # typedefs for IDs
 type PLAYER            = UINT8
@@ -596,6 +598,7 @@
   UINT8 veteran; add-cap(veteran)
   BOOL veteran_old; remove-cap(veteran)
   BOOL ai, paradropped, connecting, carried, done_moving;
+  BOOL has_orders, repeat, vigilant;
 
   UNIT_TYPE type;
   UINT8 movesleft, hp, fuel, activity_count;
@@ -661,18 +664,20 @@
   UNIT unit_id;
 end
 
+# used for server-side goto (air units only)
 PACKET_UNIT_GOTO=58;cs
   UNIT unit_id;
   COORD x, y;
 end
 
-# used for ACTIVITY_GOTO and ACTIVITY_PATROL
-PACKET_UNIT_ROUTE=59;cs
+# used for client orders: currently client-side goto and patrol
+PACKET_UNIT_ORDERS=59;cs
   UNIT unit_id;
-  ACTIVITY activity;
   UINT16 length;
-  COORD x[MAX_LEN_ROUTE:length];
-  COORD y[MAX_LEN_ROUTE:length];
+  BOOL repeat, vigilant;
+  ORDERS orders[MAX_LEN_ROUTE:length];
+  DIRECTION dir[MAX_LEN_ROUTE:length];
+  COORD dest_x, dest_y;
 end
 
 PACKET_UNIT_AUTO=60;cs
Index: common/unit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/unit.c,v
retrieving revision 1.195
diff -u -r1.195 unit.c
--- common/unit.c       2004/01/19 06:01:21     1.195
+++ common/unit.c       2004/01/19 08:36:21
@@ -214,6 +214,14 @@
   return TRUE;
 }
 
+/****************************************************************************
+  Return TRUE iff the unit is following client-side orders.
+****************************************************************************/
+bool unit_has_orders(struct unit *punit)
+{
+  return punit->has_orders;
+}
+
 /**************************************************************************
 ...
 **************************************************************************/
@@ -579,7 +587,6 @@
   case ACTIVITY_TRANSFORM:     text = _("Transform"); break;
   case ACTIVITY_AIRBASE:       text = _("Airbase"); break;
   case ACTIVITY_FALLOUT:       text = _("Fallout"); break;
-  case ACTIVITY_PATROL:        text = _("Patrol"); break;
   default:                     text = _("Unknown"); break;
   }
 
@@ -671,7 +678,6 @@
   switch(activity) {
   case ACTIVITY_IDLE:
   case ACTIVITY_GOTO:
-  case ACTIVITY_PATROL:
     return TRUE;
 
   case ACTIVITY_POLLUTION:
@@ -931,7 +937,6 @@
    case ACTIVITY_SENTRY:
    case ACTIVITY_GOTO:
    case ACTIVITY_EXPLORE:
-   case ACTIVITY_PATROL:
      return get_activity_text (punit->activity);
    case ACTIVITY_PILLAGE:
      if(punit->activity_target == S_NO_SPECIAL) {
@@ -1303,7 +1308,7 @@
   /* 1) */
   if (activity != ACTIVITY_IDLE
       && activity != ACTIVITY_GOTO
-      && activity != ACTIVITY_PATROL && !connecting) {
+      && !connecting) {
     return MR_BAD_ACTIVITY;
   }
 
@@ -1497,12 +1502,12 @@
   punit->ai.charge = 0;
   punit->bribe_cost = -1; /* flag value */
   punit->transported_by = -1;
-  punit->pgr = NULL;
   punit->focus_status = FOCUS_AVAIL;
   punit->ord_map = 0;
   punit->ord_city = 0;
   set_unit_activity(punit, ACTIVITY_IDLE);
   punit->occupy = 0;
+  punit->has_orders = FALSE;
 
   return punit;
 }
@@ -1513,7 +1518,7 @@
 **************************************************************************/
 void destroy_unit_virtual(struct unit *punit)
 {
-  free_unit_goto_route(punit);
+  free_unit_orders(punit);
   free(punit);
 }
 
@@ -1521,13 +1526,13 @@
   Free and reset the unit's goto route (punit->pgr).  Only used by the
   server.
 **************************************************************************/
-void free_unit_goto_route(struct unit *punit)
+void free_unit_orders(struct unit *punit)
 {
-  if (punit->pgr) {
-    free(punit->pgr->pos);
-    free(punit->pgr);
-    punit->pgr = NULL;
+  if (punit->has_orders) {
+    free(punit->orders.list);
+    punit->orders.list = NULL;
   }
+  punit->has_orders = FALSE;
 }
 
 /****************************************************************************
Index: common/unit.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/unit.h,v
retrieving revision 1.108
diff -u -r1.108 unit.h
--- common/unit.h       2004/01/11 17:45:04     1.108
+++ common/unit.h       2004/01/19 08:36:21
@@ -19,20 +19,28 @@
 
 struct player;
 struct city;
-struct goto_route;
 struct tile;
+struct unit_order;
 
 #define BARBARIAN_LIFE    5
 
+/* Changing this enum will break savegame and network compatability. */
 enum unit_activity {
   ACTIVITY_IDLE, ACTIVITY_POLLUTION, ACTIVITY_ROAD, ACTIVITY_MINE,
   ACTIVITY_IRRIGATE, ACTIVITY_FORTIFIED, ACTIVITY_FORTRESS, ACTIVITY_SENTRY,
   ACTIVITY_RAILROAD, ACTIVITY_PILLAGE, ACTIVITY_GOTO, ACTIVITY_EXPLORE,
   ACTIVITY_TRANSFORM, ACTIVITY_UNKNOWN, ACTIVITY_AIRBASE, ACTIVITY_FORTIFYING,
-  ACTIVITY_FALLOUT, ACTIVITY_PATROL,
+  ACTIVITY_FALLOUT,
+  ACTIVITY_PATROL_UNUSED, /* Needed for savegame compatability. */
   ACTIVITY_LAST   /* leave this one last */
 };
 
+/* Changing this enum will break savegame and network compatability. */
+enum unit_orders {
+  ORDER_MOVE, ORDER_FINISH_TURN,
+  ORDER_LAST
+};
+
 enum unit_focus_status {
   FOCUS_AVAIL, FOCUS_WAIT, FOCUS_DONE  
 };
@@ -153,7 +161,14 @@
 
   int transported_by;
   int occupy; /* number of units that occupy transporter */
-  struct goto_route *pgr;
+
+  bool has_orders;
+  struct {
+    int length, index; /* server only */
+    bool repeat;       /* The path is to be repeated on completion. */
+    bool vigilant;     /* Orders should be cleared if an enemy is met. */
+    struct unit_order *list; /* server only */
+  } orders;
 };
 
 /* Wrappers for accessing the goto destination of a unit.  This goto_dest
@@ -222,6 +237,7 @@
 bool unit_can_est_traderoute_here(struct unit *punit);
 bool unit_can_defend_here(struct unit *punit);
 bool unit_can_airlift_to(struct unit *punit, struct city *pcity);
+bool unit_has_orders(struct unit *punit);
 
 bool can_unit_paradrop(struct unit *punit);
 bool can_unit_change_homecity(struct unit *punit);
@@ -303,7 +319,7 @@
 struct unit *create_unit_virtual(struct player *pplayer, struct city *pcity,
                                  Unit_Type_id type, int veteran_level);
 void destroy_unit_virtual(struct unit *punit);
-void free_unit_goto_route(struct unit *punit);
+void free_unit_orders(struct unit *punit);
 
 int get_transporter_occupancy(struct unit *ptrans);
 
Index: server/gotohand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/gotohand.c,v
retrieving revision 1.177
diff -u -r1.177 gotohand.c
--- server/gotohand.c   2004/01/11 17:45:05     1.177
+++ server/gotohand.c   2004/01/19 08:36:21
@@ -1279,10 +1279,7 @@
   enum goto_result status;
   int x, y;
 
-  if (punit->pgr) {
-    /* we have a precalculated goto route */
-    return goto_route_execute(punit);
-  }
+  assert(!unit_has_orders(punit));
 
   unit_id = punit->id;
   dest_x = waypoint_x = goto_dest_x(punit);
@@ -1412,9 +1409,7 @@
   /* normally we would just do this unconditionally, but if we had an
      airplane goto we might not be finished even if the loop exited */
   if (same_pos(punit->x, punit->y, dest_x, dest_y)) {
-    if (punit->activity != ACTIVITY_PATROL) {
-      punit->activity = ACTIVITY_IDLE;
-    }
+    punit->activity = ACTIVITY_IDLE;
     status = GR_ARRIVED;
   } else {
     /* we have a plane refueling at a waypoint */
Index: server/hand_gen.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/hand_gen.c,v
retrieving revision 1.2
diff -u -r1.2 hand_gen.c
--- server/hand_gen.c   2004/01/06 20:19:17     1.2
+++ server/hand_gen.c   2004/01/19 08:36:21
@@ -184,13 +184,8 @@
       ((struct packet_unit_goto *)packet)->y);
     return TRUE;
 
-  case PACKET_UNIT_ROUTE:
-    handle_unit_route(pplayer,
-      ((struct packet_unit_route *)packet)->unit_id,
-      ((struct packet_unit_route *)packet)->activity,
-      ((struct packet_unit_route *)packet)->length,
-      ((struct packet_unit_route *)packet)->x,
-      ((struct packet_unit_route *)packet)->y);
+  case PACKET_UNIT_ORDERS:
+    handle_unit_orders(pplayer, packet);
     return TRUE;
 
   case PACKET_UNIT_AUTO:
Index: server/hand_gen.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/hand_gen.h,v
retrieving revision 1.2
diff -u -r1.2 hand_gen.h
--- server/hand_gen.h   2003/12/06 19:23:51     1.2
+++ server/hand_gen.h   2004/01/19 08:36:21
@@ -49,7 +49,8 @@
 void handle_unit_establish_trade(struct player *pplayer, int unit_id);
 void handle_unit_help_build_wonder(struct player *pplayer, int unit_id);
 void handle_unit_goto(struct player *pplayer, int unit_id, int x, int y);
-void handle_unit_route(struct player *pplayer, int unit_id, enum unit_activity 
activity, int length, int *x, int *y);
+struct packet_unit_orders;
+void handle_unit_orders(struct player *pplayer, struct packet_unit_orders 
*packet);
 void handle_unit_auto(struct player *pplayer, int unit_id);
 void handle_unit_unload(struct player *pplayer, int unit_id);
 void handle_unit_upgrade(struct player *pplayer, int unit_id);
Index: server/savegame.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/savegame.c,v
retrieving revision 1.144
diff -u -r1.144 savegame.c
--- server/savegame.c   2004/01/12 16:59:17     1.144
+++ server/savegame.c   2004/01/19 08:36:22
@@ -175,7 +175,7 @@
    and rulesets */
 #define SAVEFILE_OPTIONS "startoptions spacerace2 rulesets" \
 " diplchance_percent worklists2 map_editor known32fix turn " \
-"attributes watchtower rulesetdir client_worklists"
+"attributes watchtower rulesetdir client_worklists orders"
 
 static const char hex_chars[] = "0123456789abcdef";
 static const char terrain_chars[] = "adfghjm prstu";
@@ -597,6 +597,7 @@
   int i, j, x, y, nunits, ncities, c_s;
   char *p;
   char *savefile_options = secfile_lookup_str(file, "savefile.options");
+  enum unit_activity activity;
 
   server_player_init(plr, TRUE);
 
@@ -1043,7 +1044,18 @@
     
     punit->moves_left=secfile_lookup_int(file, "player%d.u%d.moves", plrno, i);
     punit->fuel= secfile_lookup_int(file, "player%d.u%d.fuel", plrno, i);
-    set_unit_activity(punit, secfile_lookup_int(file, 
"player%d.u%d.activity",plrno, i));
+    activity = secfile_lookup_int(file, "player%d.u%d.activity",plrno, i);
+    if (activity == ACTIVITY_PATROL_UNUSED) {
+      /* Previously ACTIVITY_PATROL and ACTIVITY_GOTO were used for
+       * client-side goto.  Now client-side goto is handled by setting
+       * a special flag, and units with orders generally have ACTIVITY_IDLE.
+       * Old orders are lost.  Old client-side goto units will still have
+       * ACTIVITY_GOTO and will goto the correct position via server goto.
+       * Old client-side patrol units lose their patrol routes and are put
+       * into idle mode. */
+      activity = ACTIVITY_IDLE;
+    }
+    set_unit_activity(punit, activity);
 /* need to do this to assign/deassign settlers correctly -- Syela */
 /* was punit->activity=secfile_lookup_int(file, "player%d.u%d.activity",plrno, 
i); */
     punit->activity_count=secfile_lookup_int(file, 
@@ -1090,58 +1102,42 @@
        etc may use junk values).
     */
 
-    /* load the goto route */
-    {
-      int len = secfile_lookup_int_default(file, 0, 
"player%d.u%d.goto_length", plrno, i);
+    /* load the unit orders */
+    if (has_capability("orders", savefile_options)) {
+      int len = secfile_lookup_int_default(file, 0,
+                       "player%d.u%d.orders_length", plrno, i);
       if (len > 0) {
-       char *goto_buf, *goto_buf_ptr;
-       struct goto_route *pgr = fc_malloc(sizeof(struct goto_route));
-       pgr->pos = fc_malloc((len+1) * sizeof(struct map_position));
-       pgr->first_index = 0;
-       pgr->length = len+1;
-       pgr->last_index = len;
-       punit->pgr = pgr;
-
-       /* get x coords */
-       goto_buf = secfile_lookup_str(file, "player%d.u%d.goto_route_x", plrno, 
i);
-       goto_buf_ptr = goto_buf;
-       for (j = 0; j < len; j++) {
-         if (sscanf(goto_buf_ptr, "%d", &pgr->pos[j].x) == 0) {
-           die("not an int");
-         }
-         while (*goto_buf_ptr != ',') {
-           goto_buf_ptr++;
-           if (*goto_buf_ptr == '\0') {
-             die("byebye");
-           }
-         }
-         goto_buf_ptr++;
-       }
-       /* get y coords */
-       goto_buf = secfile_lookup_str(file, "player%d.u%d.goto_route_y", plrno, 
i);
-       goto_buf_ptr = goto_buf;
+       char *orders_buf, *dir_buf;
+
+       punit->orders.list = fc_malloc(len * sizeof(*(punit->orders.list)));
+       punit->orders.length = len;
+       punit->orders.index = secfile_lookup_int_default(file, 0,
+                       "player%d.u%d.orders_index", plrno, i);
+       punit->orders.repeat = secfile_lookup_bool_default(file, FALSE,
+                       "player%d.u%d.orders_repeat", plrno, i);
+       punit->orders.vigilant = secfile_lookup_bool_default(file, FALSE,
+                       "player%d.u%d.orders_vigilant", plrno, i);
+
+       orders_buf = secfile_lookup_str_default(file, "",
+                       "player%d.u%d.orders_list", plrno, i);
+       dir_buf = secfile_lookup_str_default(file, "",
+                       "player%d.u%d.dir_list", plrno, i);
        for (j = 0; j < len; j++) {
-         if (sscanf(goto_buf_ptr, "%d", &pgr->pos[j].y) == 0) {
-           die("not an int");
-         }
-         while (*goto_buf_ptr != ',') {
-           goto_buf_ptr++;
-           if (*goto_buf_ptr == '\0') {
-             die("byebye");
-           }
+         if (orders_buf[j] == '\0' || dir_buf == '\0') {
+           freelog(LOG_ERROR, _("Savegame error: invalid unit orders."));
+           free_unit_orders(punit);
+           break;
          }
-         goto_buf_ptr++;
+         punit->orders.list[j].order = orders_buf[j] - 'a';
+         punit->orders.list[j].dir = dir_buf[j] - 'a';
        }
+       punit->has_orders = TRUE;
       } else {
-       /* mark unused strings as read to avoid warnings */
-       (void) secfile_lookup_str_default(file, "",
-                                         "player%d.u%d.goto_route_x", plrno,
-                                         i);
-       (void) secfile_lookup_str_default(file, "",
-                                         "player%d.u%d.goto_route_y", plrno,
-                                         i);
-       punit->pgr = NULL;
+       punit->has_orders = FALSE;
+       punit->orders.list = NULL;
       }
+    } else {
+      /* Old-style goto routes get discarded. */
     }
 
     {
@@ -1543,42 +1539,30 @@
     secfile_insert_bool(file, punit->paradropped, "player%d.u%d.paradropped", 
plrno, i);
     secfile_insert_int(file, punit->transported_by,
                       "player%d.u%d.transported_by", plrno, i);
-    if (punit->pgr && punit->pgr->first_index != punit->pgr->last_index) {
-      struct goto_route *pgr = punit->pgr;
-      int index = pgr->first_index;
-      int len = 0;
-      while (pgr && index != pgr->last_index) {
-       len++;
-       index = (index + 1) % pgr->length;
+    if (punit->has_orders) {
+      int len = punit->orders.length, j;
+      char orders_buf[len + 1], dir_buf[len + 1];
+
+      secfile_insert_int(file, len, "player%d.u%d.orders_length", plrno, i);
+      secfile_insert_int(file, punit->orders.index,
+                        "player%d.u%d.orders_index", plrno, i);
+      secfile_insert_bool(file, punit->orders.repeat,
+                         "player%d.u%d.orders_repeat", plrno, i);
+      secfile_insert_bool(file, punit->orders.vigilant,
+                         "player%d.u%d.orders_vigilant", plrno, i);
+
+      for (j = 0; j < len; j++) {
+       orders_buf[j] = 'a' + punit->orders.list[j].order;
+       dir_buf[j] = 'a' + punit->orders.list[j].dir;
       }
-      assert(len > 0);
-      secfile_insert_int(file, len, "player%d.u%d.goto_length", plrno, i);
-      /* assumption about the chars per map position */
-      assert(MAP_MAX_HEIGHT < 1000 && MAP_MAX_WIDTH < 1000);
-      {
-       char *goto_buf = fc_malloc(4 * len + 1);
-       char *goto_buf_ptr = goto_buf;
-       index = pgr->first_index;
-       while (index != pgr->last_index) {
-         goto_buf_ptr += sprintf(goto_buf_ptr, "%d,", pgr->pos[index].x);
-         index = (index + 1) % pgr->length;
-       }
-       *goto_buf_ptr = '\0';
-       secfile_insert_str(file, goto_buf, "player%d.u%d.goto_route_x", plrno, 
i);
+      orders_buf[len] = dir_buf[len] = '\0';
 
-       goto_buf_ptr = goto_buf;
-       index = pgr->first_index;
-       while (index != pgr->last_index) {
-         goto_buf_ptr += sprintf(goto_buf_ptr, "%d,", pgr->pos[index].y);
-         index = (index + 1) % pgr->length;
-       }
-       *goto_buf_ptr = '\0';
-       secfile_insert_str(file, goto_buf, "player%d.u%d.goto_route_y", plrno, 
i);
-      }
+      secfile_insert_str(file, orders_buf,
+                        "player%d.u%d.orders_list", plrno, i);
+      secfile_insert_str(file, dir_buf,
+                        "player%d.u%d.dir_list", plrno, i);
     } else {
-      secfile_insert_int(file, 0, "player%d.u%d.goto_length", plrno, i);
-      secfile_insert_str(file, "", "player%d.u%d.goto_route_x", plrno, i);
-      secfile_insert_str(file, "", "player%d.u%d.goto_route_y", plrno, i);
+      secfile_insert_int(file, 0, "player%d.u%d.orders_length", plrno, i);
     }
   }
   unit_list_iterate_end;
Index: server/unithand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/unithand.c,v
retrieving revision 1.283
diff -u -r1.283 unithand.c
--- server/unithand.c   2004/01/12 16:34:44     1.283
+++ server/unithand.c   2004/01/19 08:36:22
@@ -847,10 +847,6 @@
     }
   }
 
-  if (pwinner == punit && punit->activity != ACTIVITY_IDLE) {
-    /* Ensure we remove ACTIVITY_GOTO here */
-    set_unit_activity(punit, ACTIVITY_IDLE);
-  }
   send_unit_info(NULL, pwinner);
 }
 
@@ -1402,7 +1398,6 @@
     enum tile_special_type old_target = punit->activity_target;
 
     set_unit_activity(punit, new_activity);
-    free_unit_goto_route(punit);
     send_unit_info(NULL, punit);
     handle_unit_activity_dependencies(punit, old_activity, old_target);
   }
@@ -1422,7 +1417,7 @@
     enum tile_special_type old_target = punit->activity_target;
 
     set_unit_activity_targeted(punit, new_activity, new_target);
-    free_unit_goto_route(punit);
+    free_unit_orders(punit);
     send_unit_info(NULL, punit);    
     handle_unit_activity_dependencies(punit, old_activity, old_target);
   }
@@ -1495,66 +1490,69 @@
 }
 
 /**************************************************************************
-Receives goto route packages.
+Receives route packages.
 **************************************************************************/
-static void handle_route(struct player *pplayer, struct unit *punit,
-                        int length, int *x, int *y)
+void handle_unit_orders(struct player *pplayer,
+                       struct packet_unit_orders *packet)
 {
-  struct goto_route *pgr = NULL;
+  struct unit *punit = player_find_unit_by_id(pplayer, packet->unit_id);
   int i;
 
-  free_unit_goto_route(punit);
+  if (!punit || packet->length < 0 || punit->activity != ACTIVITY_IDLE) {
+    return;
+  }
 
-  /* 
-   * pgr->pos is implemented as a circular buffer of positions, and
-   * this circular buffer requires an extra "empty" spot.  Hence we
-   * increase the length by one. 
-   */
-  pgr = fc_malloc(sizeof(struct goto_route));
-  pgr->length = length + 1;
-  pgr->first_index = 0;
-  pgr->last_index = length;
-  pgr->pos = fc_malloc(pgr->length * sizeof(*pgr->pos));
-
-  for (i = 0; i < length; i++) {
-    pgr->pos[i].x = x[i];
-    pgr->pos[i].y = y[i];
+  for (i = 0; i < packet->length; i++) {
+    switch (packet->orders[i]) {
+    case ORDER_MOVE:
+      if (!is_valid_dir(packet->dir[i])) {
+       return;
+      }
+      break;
+    case ORDER_FINISH_TURN:
+      break;
+    default:
+      /* An invalid order.  This is handled in execute_orders. */
+      packet->orders[i] = ORDER_LAST;
+      break;
+    }
   }
 
-  punit->pgr = pgr;
+  free_unit_orders(punit);
 
-#ifdef DEBUG
-  freelog(LOG_DEBUG, "first:%d, last:%d, length:%d",
-         pgr->first_index, pgr->last_index, pgr->length);
-  for (i = pgr->first_index; i < pgr->last_index; i++) {
-    freelog(LOG_NORMAL, "  %d,%d", pgr->pos[i].x, pgr->pos[i].y);
+  if (packet->length == 0) {
+    assert(!unit_has_orders(punit));
+    send_unit_info(NULL, punit);
+    return;
   }
-#endif
 
-  if (punit->activity == ACTIVITY_GOTO) {
-    set_goto_dest(punit, pgr->pos[pgr->last_index-1].x,
-                 pgr->pos[pgr->last_index-1].y);
-    send_unit_info(pplayer, punit);
+  punit->has_orders = TRUE;
+  punit->orders.length = packet->length;
+  punit->orders.index = 0;
+  punit->orders.repeat = packet->repeat;
+  punit->orders.vigilant = packet->vigilant;
+  punit->orders.list
+    = fc_malloc(packet->length * sizeof(*(punit->orders.list)));
+  for (i = 0; i < packet->length; i++) {
+    punit->orders.list[i].order = packet->orders[i];
+    punit->orders.list[i].dir = packet->dir[i];
   }
-
-  assign_units_to_transporter(punit, TRUE);
-  (void) goto_route_execute(punit);
-}
 
-/**************************************************************************
-Receives route packages.
-**************************************************************************/
-void handle_unit_route(struct player *pplayer, int unit_id,
-                      enum unit_activity activity, int length, int *x,
-                      int *y)
-{
-  struct unit *punit = player_find_unit_by_id(pplayer, unit_id);
+  if (!packet->repeat) {
+    set_goto_dest(punit, packet->dest_x, packet->dest_y);
+  }
 
-  if (!punit || length < 1
-      || !(activity == ACTIVITY_GOTO || activity == ACTIVITY_PATROL)) {
-    return;
+#ifdef DEBUG
+  freelog(LOG_DEBUG, "Orders for unit %d: length:%d",
+         packet->unit_id, packet->length);
+  for (i = 0; i < packet->length; i++) {
+    freelog(LOG_NORMAL, "  %d,%s", packet->orders[i],
+           dir_get_name(packet->dir[i]));
   }
+#endif
 
-  handle_unit_activity_request(punit, activity);
-  handle_route(pplayer, punit, length, x, y);
+  assign_units_to_transporter(punit, TRUE);
+  if (execute_orders(punit) != GR_DIED) {
+    send_unit_info(NULL, punit);
+  }
 }
Index: server/unittools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/unittools.c,v
retrieving revision 1.270
diff -u -r1.270 unittools.c
--- server/unittools.c  2004/01/11 17:45:06     1.270
+++ server/unittools.c  2004/01/19 08:36:23
@@ -653,8 +653,7 @@
   struct tile *ptile = map_get_tile(punit->x, punit->y);
   
   if (activity != ACTIVITY_IDLE && activity != ACTIVITY_FORTIFIED
-      && activity != ACTIVITY_GOTO && activity != ACTIVITY_EXPLORE
-      && activity != ACTIVITY_PATROL) {
+      && activity != ACTIVITY_GOTO && activity != ACTIVITY_EXPLORE) {
     /*  We don't need the activity_count for the above */
     if (punit->moves_left > 0) {
       /* 
@@ -900,8 +899,8 @@
     return;
   }
 
-  if (punit->activity == ACTIVITY_PATROL) {
-    if (goto_route_execute(punit) == GR_DIED) {
+  if (unit_has_orders(punit)) {
+    if (execute_orders(punit) == GR_DIED) {
       return;
     }
   }
@@ -1894,6 +1893,13 @@
   packet->done_moving = punit->done_moving;
   packet->carried = carried;
   packet->occupy = get_transporter_occupancy(punit);
+  packet->has_orders = punit->has_orders;
+  if (punit->has_orders) {
+    packet->repeat = punit->orders.repeat;
+    packet->vigilant = punit->orders.vigilant;
+  } else {
+    packet->repeat = packet->vigilant = FALSE;
+  }
 }
 
 /**************************************************************************
@@ -1926,9 +1932,8 @@
   packet->type = punit->type;
   packet->hp = punit->hp;
   packet->occupied = (get_transporter_occupancy(punit) > 0);
-  if (punit->activity == ACTIVITY_GOTO
-      || punit->activity == ACTIVITY_EXPLORE
-      || punit->activity == ACTIVITY_PATROL) {
+  if (punit->activity == ACTIVITY_EXPLORE
+      || punit->activity == ACTIVITY_GOTO) {
     packet->activity = ACTIVITY_IDLE;
   } else {
     packet->activity = punit->activity;
@@ -2744,7 +2749,8 @@
   square_iterate(punit->x, punit->y, 3, x, y) {
     unit_list_iterate(map_get_tile(x, y)->units, ppatrol) {
       if (punit != ppatrol
-         && ppatrol->activity == ACTIVITY_PATROL) {
+         && unit_has_orders(ppatrol)
+         && ppatrol->orders.vigilant) {
        (void) maybe_cancel_patrol_due_to_enemy(ppatrol);
       }
     } unit_list_iterate_end;
@@ -2859,10 +2865,9 @@
 static void check_unit_activity(struct unit *punit)
 {
   if (punit->activity != ACTIVITY_IDLE
-      && punit->activity != ACTIVITY_GOTO
       && punit->activity != ACTIVITY_SENTRY
       && punit->activity != ACTIVITY_EXPLORE
-      && punit->activity != ACTIVITY_PATROL
+      && punit->activity != ACTIVITY_GOTO
       && !punit->connecting) {
     set_unit_activity(punit, ACTIVITY_IDLE);
   }
@@ -2905,7 +2910,8 @@
   }
 
   /* A transporter should not take units with it on an attack goto. */
-  if (punit->activity == ACTIVITY_GOTO
+  if ((unit_has_orders(punit)
+       || punit->activity == ACTIVITY_GOTO)
       && (is_non_allied_unit_tile(pdesttile, pplayer)
           || is_non_allied_city_tile(pdesttile, pplayer))
       && !is_ocean(psrctile->terrain)) {
@@ -2948,13 +2954,6 @@
       pcargo->x = dest_x;
       pcargo->y = dest_y;
 
-      if ((pcargo->activity == ACTIVITY_GOTO
-          || pcargo->activity == ACTIVITY_PATROL)
-         && !pcargo->ai.control) {
-       /* Cancel any _client_ gotos for these units. */
-       handle_unit_activity_request(pcargo, ACTIVITY_IDLE);
-      }
-
       unit_list_insert(&pdesttile->units, pcargo);
       check_unit_activity(pcargo);
       send_unit_info_to_onlookers(NULL, pcargo, src_x, src_y, FALSE);
@@ -3031,14 +3030,8 @@
     } unit_list_iterate_end;
     assert(punit->transported_by != -1);
 
-    /* set activity to sentry if boarding a ship unless the unit is just 
-     * passing through the ship on its way somewhere else.  If the unit is
-     * GOTOing and the ship isn't the final destination, then don't go
-     * to sleep. */
-    if (!(pplayer->ai.control)
-       && !(punit->activity == ACTIVITY_GOTO
-            && !same_pos(goto_dest_x(punit), goto_dest_y(punit),
-                         dest_x, dest_y))) {
+    /* Set activity to sentry if boarding a ship. */
+    if (!pplayer->ai.control && !unit_has_orders(punit)) {
       set_unit_activity(punit, ACTIVITY_SENTRY);
     }
 
@@ -3165,115 +3158,126 @@
   return cancel;
 }
 
-/**************************************************************************
-Moves a unit according to its pgr (goto or patrol order). If two consequetive
-positions in the route is not adjacent it is assumed to be a goto. The unit
-is put on idle if a move fails.
-If the activity is ACTIVITY_PATROL the map positions are put back in the
-route (at the end).  To avoid infinite loops on railroad we stop for this
-turn when the unit is back where it started, eben if it have moves left.
-
-If a patrolling unit came across an enemy we could make the patrolling unit
-autoattack or just stop and wait for the owner to attack. It is also
-debateable if units on goto should stop when they encountered an enemy
-unit. Currently the unit continues if it can, or if it is blocked it stops.
-**************************************************************************/
-enum goto_result goto_route_execute(struct unit *punit)
-{
-  struct goto_route *pgr = punit->pgr;
-  int index, x, y;
-  bool res, last_tile, moved;
-  int patrol_stop_index = pgr->last_index;
+/****************************************************************************
+  Executes a unit's orders stored in punit->orders.  The unit is put on idle
+  if an action fails or if "patrol" is set and an enemy unit is encountered.
+
+  If the orders are repeating the loop starts over at the beginning once it
+  completes.  To avoid infinite loops on railroad we stop for this
+  turn when the unit is back where it started, even if it have moves left.
+
+  A unit will attack under orders only on its final action.
+****************************************************************************/
+enum goto_result execute_orders(struct unit *punit)
+{
+  int dest_x, dest_y;
+  bool res, last_order;
   int unitid = punit->id;
   struct player *pplayer = unit_owner(punit);
+  int moves_made = 0;
 
-  assert(pgr != NULL);
-  while (TRUE) {
-    freelog(LOG_DEBUG, "running a round\n");
+  assert(unit_has_orders(punit));
 
-    index = pgr->first_index;
-    if (index == pgr->last_index) {
-      free_unit_goto_route(punit);
-      if (punit->activity == ACTIVITY_GOTO) 
-       /* the activity could already be SENTRY (if boarded a ship) 
-          -- leave it as it is then */
-       handle_unit_activity_request(punit, ACTIVITY_IDLE);
-      return GR_ARRIVED;
-    }
-    x = pgr->pos[index].x; y = pgr->pos[index].y;
-    freelog(LOG_DEBUG, "%i,%i -> %i,%i\n", punit->x, punit->y, x, y);
+  freelog(LOG_DEBUG, "Executing orders for %s %d",
+         unit_name(punit->type), punit->id);
 
+  while (TRUE) {
     if (punit->moves_left == 0) {
+      /* FIXME: this check won't work when actions take 0 MP. */
+      freelog(LOG_DEBUG, "  stopping because of no more move points");
       return GR_OUT_OF_MOVEPOINTS;
     }
 
-    if (punit->activity == ACTIVITY_PATROL
-       && maybe_cancel_patrol_due_to_enemy(punit)) {
-      return GR_FAILED;
+    if (punit->done_moving) {
+      freelog(LOG_DEBUG, "  stopping because we're done this turn");
+      return GR_WAITING;
     }
-
-    last_tile = (((index + 1) % pgr->length) == (pgr->last_index));
 
-    if (punit->activity == ACTIVITY_GOTO && !last_tile 
-        && maybe_cancel_goto_due_to_enemy(punit, x, y)) {
+    if (punit->orders.vigilant && maybe_cancel_patrol_due_to_enemy(punit)) {
+      /* "Patrol" orders are stopped if an enemy is near. */
+      freelog(LOG_DEBUG, "  stopping because of nearby enemy");
       return GR_FAILED;
     }
-
-    /* Move unit */
-    moved = !same_pos(punit->x, punit->y, x, y);
-    if (moved) {
-      res = handle_unit_move_request(punit, x, y, FALSE, !last_tile);
-    } else {
-      res = TRUE;
-    }
 
-    if (!player_find_unit_by_id(pplayer, unitid)) {
-      return GR_DIED;
+    if (moves_made == punit->orders.length) {
+      /* For repeating orders, don't repeat more than once per turn. */
+      freelog(LOG_DEBUG, "  stopping because we ran a round");
+      return GR_ARRIVED;
     }
 
-    if (same_pos(punit->x, punit->y, x, y)) {
-      /* We succeeded in moving one step forward */
-      pgr->first_index = (pgr->first_index + 1) % pgr->length;
-      if (punit->activity == ACTIVITY_PATROL) {
-       /* When patroling we go in little circles; 
-        * done by reinserting points */
-       pgr->pos[pgr->last_index].x = x;
-       pgr->pos[pgr->last_index].y = y;
-       pgr->last_index = (pgr->last_index + 1) % pgr->length;
-
-       if (patrol_stop_index == pgr->first_index) {
-         freelog(LOG_DEBUG, "stopping because we ran a round\n");
-         return GR_ARRIVED;    /* don't patrol more than one round */
-       }
-       if (maybe_cancel_patrol_due_to_enemy(punit)) {
-         return GR_FAILED;
-       }
-      }
+    last_order = (!punit->orders.repeat
+                 && punit->orders.index + 1 == punit->orders.length);
 
-      if (!moved) {
-       /* Sometimes the goto route will have us sit still for a moment -
-        * for instance a trireme will do this to have enough MP to
-        * cross the ocean in one turn. */
-       punit->done_moving = TRUE;
-       send_unit_info(unit_owner(punit), punit);
-       return GR_WAITING;
-      }
+    switch (punit->orders.list[punit->orders.index].order) {
+    case ORDER_FINISH_TURN:
+      punit->done_moving = TRUE;
+      freelog(LOG_DEBUG, "  waiting this turn");
+      send_unit_info(unit_owner(punit), punit);
+      break;
+    case ORDER_MOVE:
+      /* Move unit */
+      if (!MAPSTEP(dest_x, dest_y, punit->x, punit->y,
+                  punit->orders.list[punit->orders.index].dir)) {
+       freelog(LOG_DEBUG, "  move order sent us to invalid location");
+       return GR_FAILED;
+      }
+
+      if (!last_order
+         && maybe_cancel_goto_due_to_enemy(punit, dest_x, dest_y)) {
+       freelog(LOG_DEBUG, "  orders canceled because of enemy");
+       return GR_FAILED;
+      }
+
+      freelog(LOG_DEBUG, "  moving to %d,%d", dest_x, dest_y);
+      res = handle_unit_move_request(punit, dest_x, dest_y,
+                                    FALSE, !last_order);
+      if (!player_find_unit_by_id(pplayer, unitid)) {
+       freelog(LOG_DEBUG, "  unit died while moving.");
+       return GR_DIED;
+      }
+
+      if (res && !same_pos(dest_x, dest_y, punit->x, punit->y)) {
+       /* Movement succeeded but unit didn't move. */
+       freelog(LOG_DEBUG, "  orders resulted in combat.");
+       return GR_FOUGHT;
+      }
+
+      if (!res && punit->moves_left > 0) {
+       /* Movement failed (ZOC, etc.) */
+       freelog(LOG_DEBUG, "  attempt to move failed.");
+       return GR_FAILED;
+      }
+
+      if (last_order && punit->transported_by != -1) {
+       /* Set activity to sentry if boarding a ship.  This is done in
+        * move_unit, but that function doesn't handle the orders case. */
+       set_unit_activity(punit, ACTIVITY_SENTRY);
+      }
+      break;
+    case ORDER_LAST:
+      freelog(LOG_DEBUG, "  client sent invalid order!");
+      notify_player_ex(pplayer, punit->x, punit->y, E_NOEVENT,
+                      _("Game: Your %s has invalid orders."),
+                      unit_name(punit->type));
+      return GR_FAILED;
     }
 
-    if (res && !same_pos(x, y, punit->x, punit->y)) {
-      /*
-       * unit is alive, moved alright, didn't arrive, has moves_left
-       * --- what else can it be 
-       */
-      return GR_FOUGHT;
-    }
+    /* We succeeded in moving one step forward */
+    punit->orders.index++;
 
-    if (!res && punit->moves_left > 0) {
-      freelog(LOG_DEBUG, "move idling\n");
-      handle_unit_activity_request(punit, ACTIVITY_IDLE);
-      return GR_FAILED;
+    if (punit->orders.index == punit->orders.length) {
+      if (!punit->orders.repeat) {
+       free_unit_orders(punit);
+       assert(punit->has_orders == FALSE);
+       freelog(LOG_DEBUG, "  stopping because orders are complete");
+       return GR_ARRIVED;
+      } else {
+       /* Start over. */
+       freelog(LOG_DEBUG, "  repeating orders.");
+       punit->orders.index = 0;
+      }
     }
-  } /* end while*/
+  } /* end while */
 }
 
 /**************************************************************************
Index: server/unittools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/unittools.h,v
retrieving revision 1.60
diff -u -r1.60 unittools.h
--- server/unittools.h  2004/01/11 17:45:06     1.60
+++ server/unittools.h  2004/01/19 08:36:23
@@ -82,6 +82,6 @@
 void assign_units_to_transporter(struct unit *ptrans, bool take_from_land);
 bool move_unit(struct unit *punit, int dest_x, int dest_y,
              bool transport_units, bool take_from_land, int move_cost);
-enum goto_result goto_route_execute(struct unit *punit);
+enum goto_result execute_orders(struct unit *punit);
 
 #endif  /* FC__UNITTOOLS_H */

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