From dbc2676eec021b4d6d39f4e0cacdb0d331e5e190 Mon Sep 17 00:00:00 2001
From: Pepeto <pepeto@gna.org>
Date: Sun, 14 Nov 2010 23:29:31 +0100
Subject: [PATCH 6/7] Execute orders cleanup.

---
 server/unittools.c |  111 ++++++++++++++++++++++++++++++----------------------
 1 files changed, 64 insertions(+), 47 deletions(-)

diff --git a/server/unittools.c b/server/unittools.c
index 937d735..f273a47 100644
--- a/server/unittools.c
+++ b/server/unittools.c
@@ -3175,12 +3175,6 @@ bool execute_orders(struct unit *punit)
   while (TRUE) {
     struct unit_order order;
 
-    if (punit->moves_left == 0) {
-      /* FIXME: this check won't work when actions take 0 MP. */
-      log_debug("  stopping because of no more move points");
-      return TRUE;
-    }
-
     if (punit->done_moving) {
       log_debug("  stopping because we're done this turn");
       return TRUE;
@@ -3204,10 +3198,28 @@ bool execute_orders(struct unit *punit)
     }
     moves_made++;
 
+    order = punit->orders.list[punit->orders.index];
+    if (0 == punit->moves_left) {
+      switch (order.order) {
+      case ORDER_MOVE:
+      case ORDER_FULL_MP:
+      case ORDER_BUILD_CITY:
+        log_debug("  stopping because of no more move points");
+        return TRUE;
+      case ORDER_ACTIVITY:
+      case ORDER_DISBAND:
+      case ORDER_BUILD_WONDER:
+      case ORDER_TRADE_ROUTE:
+      case ORDER_HOMECITY:
+      case ORDER_LAST:
+        /* Those actions don't require moves left. */
+        break;
+      }
+    }
+
     last_order = (!punit->orders.repeat
-		  && punit->orders.index + 1 == punit->orders.length);
+                  && punit->orders.index + 1 == punit->orders.length);
 
-    order = punit->orders.list[punit->orders.index];
     if (last_order) {
       /* Clear the orders before we engage in the move.  That way any
        * has_orders checks will yield FALSE and this will be treated as
@@ -3251,25 +3263,53 @@ bool execute_orders(struct unit *punit)
       }
     case ORDER_ACTIVITY:
       activity = order.activity;
-      base = order.base;
-      if ((activity == ACTIVITY_BASE && !can_unit_do_activity_base(punit, base))
-          || (activity != ACTIVITY_BASE
-              && !can_unit_do_activity(punit, activity))) {
-        cancel_orders(punit, "  orders canceled because of failed activity");
-        notify_player(pplayer, unit_tile(punit), E_UNIT_ORDERS, ftc_server,
+      if (ACTIVITY_BASE == activity) {
+        base = order.base;
+        if (can_unit_do_activity_base(punit, base)) {
+          punit->done_moving = TRUE;
+          set_unit_activity_base(punit, base);
+          send_unit_info(NULL, punit);
+          break;
+        } else if (tile_has_base(unit_tile(punit), base_by_number(base))) {
+          break; /* Already built, continue. */
+        } else {
+          cancel_orders(punit,
+                        "  orders canceled because of failed activity");
+          notify_player(pplayer, unit_tile(punit), E_UNIT_ORDERS, ftc_server,
                       _("Orders for %s aborted since they "
                         "give an invalid activity."),
                       unit_link(punit));
-        return TRUE;
-      }
-      punit->done_moving = TRUE;
-
-      if (activity != ACTIVITY_BASE) {
-        set_unit_activity(punit, activity);
+          return TRUE;
+        }
       } else {
-        set_unit_activity_base(punit, base);
+        if (can_unit_do_activity(punit, activity)) {
+          punit->done_moving = TRUE;
+          set_unit_activity(punit, activity);
+          send_unit_info(NULL, punit);
+          break;
+        } else if ((ACTIVITY_ROAD == activity
+                    && tile_has_special(unit_tile(punit), S_ROAD))
+                   || (ACTIVITY_MINE == activity
+                       && tile_has_special(unit_tile(punit), S_MINE))
+                   || (ACTIVITY_IRRIGATE == activity
+                       && (tile_has_special(unit_tile(punit), S_FARMLAND)
+                           || tile_has_special(unit_tile(punit),
+                                               S_IRRIGATION)))
+                   || (ACTIVITY_RAILROAD == activity
+                       && tile_has_special(unit_tile(punit), S_RAILROAD))
+                   || (ACTIVITY_MINE == activity
+                       && tile_has_special(unit_tile(punit), S_MINE))) {
+          break; /* Already built, continue. */
+        } else {
+          cancel_orders(punit,
+                        "  orders canceled because of failed activity");
+          notify_player(pplayer, unit_tile(punit), E_UNIT_ORDERS, ftc_server,
+                      _("Orders for %s aborted since they "
+                        "give an invalid activity."),
+                      unit_link(punit));
+          return TRUE;
+        }
       }
-      send_unit_info(NULL, punit);
       break;
     case ORDER_MOVE:
       /* Move unit */
@@ -3307,7 +3347,8 @@ bool execute_orders(struct unit *punit)
         return TRUE;
       }
 
-      if (!res && punit->moves_left > 0) {
+      if (!res) {
+        fc_assert(0 <= punit->moves_left);
         /* Movement failed (ZOC, etc.) */
         cancel_orders(punit, "  attempt to move failed.");
         notify_player(pplayer, unit_tile(punit), E_UNIT_ORDERS, ftc_server,
@@ -3315,30 +3356,6 @@ bool execute_orders(struct unit *punit)
                       unit_link(punit));
         return TRUE;
       }
-
-      if (!res && punit->moves_left == 0) {
-        /* Movement failed (not enough MP). Keep this move around for
-         * next turn. */
-        log_debug("  orders move failed (out of MP).");
-	if (unit_has_orders(punit)) {
-	  punit->orders.index--;
-	} else {
-	  /* FIXME: If this was the last move, the orders will already have
-	   * been freed, so we have to add them back on.  This is quite a
-	   * hack; one problem is that the no-orders unit has probably
-	   * already had its unit info sent out to the client. */
-	  punit->has_orders = TRUE;
-	  punit->orders.length = 1;
-	  punit->orders.index = 0;
-	  punit->orders.repeat = FALSE;
-	  punit->orders.vigilant = FALSE;
-	  punit->orders.list = fc_malloc(sizeof(order));
-	  punit->orders.list[0] = order;
-	}
-	send_unit_info(NULL, punit);
-	return TRUE;
-      }
-
       break;
     case ORDER_DISBAND:
       log_debug("  orders: disbanding");
-- 
1.7.0.4

