Author: sveinung
Date: Tue Aug 18 04:18:46 2015
New Revision: 29577

URL: http://svn.gna.org/viewcvs/freeciv?rev=29577&view=rev
Log:
Add the new unit action "Destroy City"

"Destroy City" can be used to implement scorched earth tactics. If
destroying a foreign city using a standard unit with a reasonable cost is
permitted the ruleset should probably demand that the foreign city doesn't
have any defending units in it. It isn't hard coded. A ruleset author may
wish to have "Destroy City" as a special ability of the Leader or another
high value unit.

No way to defend against this action as long as it is enabled currently
exists. A follow up patch could make it probabilistic by allowing the
Action_Odds_Pct effect to reduce its initial odds of 100%.

The population of the target city has no chance of escaping as
refugee units or by using the migration system. A ruleset setting to give
them some means of escape and some chance of making use of it could also be
added in a follow up patch.

See patch #6248

Modified:
    trunk/ai/default/aicity.c
    trunk/client/gui-gtk-2.0/action_dialog.c
    trunk/client/gui-gtk-3.0/action_dialog.c
    trunk/client/gui-qt/dialogs.cpp
    trunk/client/gui-sdl/action_dialog.c
    trunk/client/gui-sdl2/action_dialog.c
    trunk/common/actions.c
    trunk/common/actions.h
    trunk/doc/README.actions
    trunk/fc_version
    trunk/server/ruleset.c
    trunk/server/unithand.c
    trunk/tools/ruledit/rulesave.c

Modified: trunk/ai/default/aicity.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/ai/default/aicity.c?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/ai/default/aicity.c   (original)
+++ trunk/ai/default/aicity.c   Tue Aug 18 04:18:46 2015
@@ -1055,6 +1055,9 @@
 
   /* Really bad for the city owner. */
   case ACTION_SPY_NUKE:
+  /* The ai will never destroy his own city to keep it out of enemy
+   * hands. If it starts supporting it this value should change. */
+  case ACTION_DESTROY_CITY:
     return 20;
 
   /* Bad for the city owner. */

Modified: trunk/client/gui-gtk-2.0/action_dialog.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/client/gui-gtk-2.0/action_dialog.c?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/client/gui-gtk-2.0/action_dialog.c    (original)
+++ trunk/client/gui-gtk-2.0/action_dialog.c    Tue Aug 18 04:18:46 2015
@@ -469,6 +469,23 @@
   if (NULL != game_unit_by_number(args->actor_unit_id)
       && NULL != game_city_by_number(args->target_city_id)) {
     request_do_action(ACTION_SPY_NUKE, args->actor_unit_id,
+                      args->target_city_id, 0, "");
+  }
+
+  gtk_widget_destroy(act_sel_dialog);
+  free(args);
+}
+
+/****************************************************************
+  User selected destroy city from choice dialog
+*****************************************************************/
+static void destroy_city_callback(GtkWidget *w, gpointer data)
+{
+  struct action_data *args = (struct action_data *)data;
+
+  if (NULL != game_unit_by_number(args->actor_unit_id)
+      && NULL != game_city_by_number(args->target_city_id)) {
+    request_do_action(ACTION_DESTROY_CITY, args->actor_unit_id,
                       args->target_city_id, 0, "");
   }
 
@@ -1062,6 +1079,7 @@
   [ACTION_HELP_WONDER] = (GCallback)caravan_help_build_wonder_callback,
   [ACTION_JOIN_CITY] = (GCallback)join_city_callback,
   [ACTION_SPY_NUKE] = (GCallback)spy_nuke_city_callback,
+  [ACTION_DESTROY_CITY] = (GCallback)destroy_city_callback,
 
   /* Unit acting against a unit target. */
   [ACTION_SPY_BRIBE_UNIT] = (GCallback)diplomat_bribe_callback,

Modified: trunk/client/gui-gtk-3.0/action_dialog.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/client/gui-gtk-3.0/action_dialog.c?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/client/gui-gtk-3.0/action_dialog.c    (original)
+++ trunk/client/gui-gtk-3.0/action_dialog.c    Tue Aug 18 04:18:46 2015
@@ -469,6 +469,23 @@
   if (NULL != game_unit_by_number(args->actor_unit_id)
       && NULL != game_city_by_number(args->target_city_id)) {
     request_do_action(ACTION_SPY_NUKE, args->actor_unit_id,
+                      args->target_city_id, 0, "");
+  }
+
+  gtk_widget_destroy(act_sel_dialog);
+  free(args);
+}
+
+/****************************************************************
+  User selected destroy city from choice dialog
+*****************************************************************/
+static void destroy_city_callback(GtkWidget *w, gpointer data)
+{
+  struct action_data *args = (struct action_data *)data;
+
+  if (NULL != game_unit_by_number(args->actor_unit_id)
+      && NULL != game_city_by_number(args->target_city_id)) {
+    request_do_action(ACTION_DESTROY_CITY, args->actor_unit_id,
                       args->target_city_id, 0, "");
   }
 
@@ -1072,6 +1089,7 @@
   [ACTION_HELP_WONDER] = (GCallback)caravan_help_build_wonder_callback,
   [ACTION_JOIN_CITY] = (GCallback)join_city_callback,
   [ACTION_SPY_NUKE] = (GCallback)spy_nuke_city_callback,
+  [ACTION_DESTROY_CITY] = (GCallback)destroy_city_callback,
 
   /* Unit acting against a unit target. */
   [ACTION_SPY_BRIBE_UNIT] = (GCallback)diplomat_bribe_callback,

Modified: trunk/client/gui-qt/dialogs.cpp
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/client/gui-qt/dialogs.cpp?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/client/gui-qt/dialogs.cpp     (original)
+++ trunk/client/gui-qt/dialogs.cpp     Tue Aug 18 04:18:46 2015
@@ -72,6 +72,7 @@
 static void spy_steal_gold(QVariant data1, QVariant data2);
 static void spy_steal_maps(QVariant data1, QVariant data2);
 static void spy_nuke_city(QVariant data1, QVariant data2);
+static void destroy_city(QVariant data1, QVariant data2);
 static void diplomat_embassy(QVariant data1, QVariant data2);
 static void spy_sabotage_unit(QVariant data1, QVariant data2);
 static void diplomat_investigate(QVariant data1, QVariant data2);
@@ -129,6 +130,7 @@
   action_function[ACTION_JOIN_CITY] = join_city;
   action_function[ACTION_STEAL_MAPS] = spy_steal_maps;
   action_function[ACTION_SPY_NUKE] = spy_nuke_city;
+  action_function[ACTION_DESTROY_CITY] = destroy_city;
 
   /* Unit acting against a unit target. */
   action_function[ACTION_SPY_BRIBE_UNIT] = diplomat_bribe;
@@ -1725,6 +1727,21 @@
 }
 
 /***************************************************************************
+  Action destroy city for choice dialog
+***************************************************************************/
+static void destroy_city(QVariant data1, QVariant data2)
+{
+  int diplomat_id = data1.toInt();
+  int diplomat_target_id = data2.toInt();
+
+  if (NULL != game_unit_by_number(diplomat_id)
+      && NULL != game_city_by_number(diplomat_target_id)) {
+    request_do_action(ACTION_DESTROY_CITY,
+                      diplomat_id, diplomat_target_id, 0, "");
+  }
+}
+
+/***************************************************************************
   Action steal gold for choice dialog
 ***************************************************************************/
 static void spy_steal_gold(QVariant data1, QVariant data2)

Modified: trunk/client/gui-sdl/action_dialog.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/client/gui-sdl/action_dialog.c?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/client/gui-sdl/action_dialog.c        (original)
+++ trunk/client/gui-sdl/action_dialog.c        Tue Aug 18 04:18:46 2015
@@ -231,6 +231,27 @@
         && NULL != game_city_by_number(
           pDiplomat_Dlg->target_ids[ATK_CITY])) {
       request_do_action(ACTION_SPY_NUKE, pDiplomat_Dlg->actor_unit_id,
+                        pDiplomat_Dlg->target_ids[ATK_CITY],
+                        0, "");
+    }
+
+    popdown_diplomat_dialog();
+    choose_action_queue_next();
+  }
+
+  return -1;
+}
+
+/****************************************************************
+  User clicked "Destroy City"
+*****************************************************************/
+static int destroy_city_callback(struct widget *pWidget)
+{
+  if (Main.event.button.button == SDL_BUTTON_LEFT) {
+    if (NULL != game_unit_by_number(pDiplomat_Dlg->actor_unit_id)
+        && NULL != game_city_by_number(
+          pDiplomat_Dlg->target_ids[ATK_CITY])) {
+      request_do_action(ACTION_DESTROY_CITY, pDiplomat_Dlg->actor_unit_id,
                         pDiplomat_Dlg->target_ids[ATK_CITY],
                         0, "");
     }
@@ -876,6 +897,7 @@
   [ACTION_HELP_WONDER] = caravan_help_build_wonder_callback,
   [ACTION_JOIN_CITY] = join_city_callback,
   [ACTION_SPY_NUKE] = spy_nuke_city_callback,
+  [ACTION_DESTROY_CITY] = destroy_city_callback,
 
   /* Unit acting against a unit target. */
   [ACTION_SPY_BRIBE_UNIT] = diplomat_bribe_callback,

Modified: trunk/client/gui-sdl2/action_dialog.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/client/gui-sdl2/action_dialog.c?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/client/gui-sdl2/action_dialog.c       (original)
+++ trunk/client/gui-sdl2/action_dialog.c       Tue Aug 18 04:18:46 2015
@@ -233,6 +233,27 @@
         && NULL != game_city_by_number(
           pDiplomat_Dlg->target_ids[ATK_CITY])) {
       request_do_action(ACTION_SPY_NUKE, pDiplomat_Dlg->actor_unit_id,
+                        pDiplomat_Dlg->target_ids[ATK_CITY],
+                        0, "");
+    }
+
+    popdown_diplomat_dialog();
+    choose_action_queue_next();
+  }
+
+  return -1;
+}
+
+/****************************************************************
+  User clicked "Destroy City"
+*****************************************************************/
+static int destroy_city_callback(struct widget *pWidget)
+{
+  if (Main.event.button.button == SDL_BUTTON_LEFT) {
+    if (NULL != game_unit_by_number(pDiplomat_Dlg->actor_unit_id)
+        && NULL != game_city_by_number(
+          pDiplomat_Dlg->target_ids[ATK_CITY])) {
+      request_do_action(ACTION_DESTROY_CITY, pDiplomat_Dlg->actor_unit_id,
                         pDiplomat_Dlg->target_ids[ATK_CITY],
                         0, "");
     }
@@ -879,6 +900,7 @@
   [ACTION_HELP_WONDER] = caravan_help_build_wonder_callback,
   [ACTION_JOIN_CITY] = join_city_callback,
   [ACTION_SPY_NUKE] = spy_nuke_city_callback,
+  [ACTION_DESTROY_CITY] = destroy_city_callback,
 
   /* Unit acting against a unit target. */
   [ACTION_SPY_BRIBE_UNIT] = diplomat_bribe_callback,

Modified: trunk/common/actions.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/common/actions.c?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/common/actions.c      (original)
+++ trunk/common/actions.c      Tue Aug 18 04:18:46 2015
@@ -127,6 +127,9 @@
                  /* FIXME: Target is actually Tile + Units + City */
                  ATK_TILE,
                  TRUE);
+  actions[ACTION_DESTROY_CITY] =
+      action_new(ACTION_DESTROY_CITY, ATK_CITY,
+                 TRUE);
 
   /* Initialize the action enabler list */
   action_iterate(act) {
@@ -1556,6 +1559,10 @@
   case ACTION_NUKE:
     /* TODO */
     break;
+  case ACTION_DESTROY_CITY:
+    /* No battle is fought first. */
+    chance = 200;
+    break;
   case ACTION_COUNT:
     fc_assert(FALSE);
     break;

Modified: trunk/common/actions.h
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/common/actions.h?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/common/actions.h      (original)
+++ trunk/common/actions.h      Tue Aug 18 04:18:46 2015
@@ -85,6 +85,8 @@
 #define SPECENUM_VALUE19NAME N_("Suitcase Nuke")
 #define SPECENUM_VALUE20 ACTION_NUKE
 #define SPECENUM_VALUE20NAME N_("Explode Nuclear")
+#define SPECENUM_VALUE21 ACTION_DESTROY_CITY
+#define SPECENUM_VALUE21NAME N_("Destroy City")
 #define SPECENUM_COUNT ACTION_COUNT
 #include "specenum_gen.h"
 

Modified: trunk/doc/README.actions
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/doc/README.actions?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/doc/README.actions    (original)
+++ trunk/doc/README.actions    Tue Aug 18 04:18:46 2015
@@ -183,6 +183,10 @@
  * UI name can be set using ui_name_suitcase_nuke
  * actor must be on the same tile as the target or on the tile next to it.
 
+"Destroy City" - Destroys the target city.
+ * UI name can be set using ui_name_destroy_city
+ * actor must be on the same tile as the target or on the tile next to it.
+
 "Establish Trade Route" - Establish a trade route to the target city.
  * UI name can be set using ui_name_establish_trade_route
  * actor must be on the same tile as the target or on the tile next to it.

Modified: trunk/fc_version
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/fc_version?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/fc_version    (original)
+++ trunk/fc_version    Tue Aug 18 04:18:46 2015
@@ -54,7 +54,7 @@
 #   - Avoid adding a new mandatory capability to the development branch for
 #     as long as possible.  We want to maintain network compatibility with
 #     the stable branch for as long as possible.
-NETWORK_CAPSTRING_MANDATORY="+Freeciv.Devel-3.0-2015.Aug.16"
+NETWORK_CAPSTRING_MANDATORY="+Freeciv.Devel-3.0-2015.Aug.18"
 NETWORK_CAPSTRING_OPTIONAL=""
 
 FREECIV_DISTRIBUTOR=""

Modified: trunk/server/ruleset.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/server/ruleset.c?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/server/ruleset.c      (original)
+++ trunk/server/ruleset.c      Tue Aug 18 04:18:46 2015
@@ -5399,6 +5399,13 @@
           N_("Explode %sNuclear%s"),
           "actions.ui_name_explode_nuclear");
       sz_strlcpy(action_by_number(ACTION_NUKE)->ui_name,
+                 text);
+
+      text = secfile_lookup_str_default(file,
+          /* TRANS: Destroy _City (100% chance of success). */
+          N_("Destroy %sCity%s"),
+          "actions.ui_name_destroy_city");
+      sz_strlcpy(action_by_number(ACTION_DESTROY_CITY)->ui_name,
                  text);
     }
 

Modified: trunk/server/unithand.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/server/unithand.c?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/server/unithand.c     (original)
+++ trunk/server/unithand.c     Tue Aug 18 04:18:46 2015
@@ -129,6 +129,9 @@
 static bool unit_bombard(struct unit *punit, struct tile *ptile);
 static bool unit_nuke(struct player *pplayer, struct unit *punit,
                       struct tile *def_tile);
+static bool unit_do_destroy_city(struct player *act_player,
+                                 struct unit *act_unit,
+                                 struct city *tgt_city);
 
 /**************************************************************************
   Handle airlift request.
@@ -1606,6 +1609,19 @@
       }
     }
     break;
+  case ACTION_DESTROY_CITY:
+    if (pcity) {
+      if (is_action_enabled_unit_on_city(action_type,
+                                         actor_unit, pcity)) {
+        ACTION_STARTED_UNIT_CITY(action_type, actor_unit, pcity);
+
+        return unit_do_destroy_city(pplayer, actor_unit, pcity);
+      } else {
+        illegal_action(pplayer, actor_unit, action_type,
+                       city_owner(pcity), NULL, pcity, NULL);
+      }
+    }
+    break;
   case ACTION_CAPTURE_UNITS:
     if (target_tile) {
       if (is_action_enabled_unit_on_units(action_type,
@@ -2347,6 +2363,106 @@
                              def_tile,
                              tile_link(def_tile));
 
+  return TRUE;
+}
+
+/**************************************************************************
+  Destroy the target city.
+
+  This function assumes the destruction is legal. The calling function
+  should have already made all necessary checks.
+
+  Returns TRUE iff action could be done, FALSE if it couldn't. Even if
+  this returns TRUE, unit may have died during the action.
+**************************************************************************/
+static bool unit_do_destroy_city(struct player *act_player,
+                                 struct unit *act_unit,
+                                 struct city *tgt_city)
+{
+  int tgt_city_id;
+  struct player *tgt_player;
+  bool try_civil_war = FALSE;
+
+  if (!act_player) {
+    /* Someone should be performing the action. */
+    fc_assert(act_player);
+
+    return FALSE;
+  }
+
+  if (!tgt_city) {
+    /* City was destroyed during pre action Lua. */
+    return FALSE;
+  }
+
+  tgt_player = city_owner(tgt_city);
+  if (!tgt_player) {
+    /* How can a city be ownerless? */
+    fc_assert(tgt_player);
+
+    return FALSE;
+  }
+
+  if (!act_unit || !unit_alive(act_unit->id)) {
+    /* Actor unit was destroyed during pre action Lua. */
+    return FALSE;
+  }
+
+  /* Save city ID. */
+  tgt_city_id = tgt_city->id;
+
+  if (is_capital(tgt_city)
+      && (tgt_player->spaceship.state == SSHIP_STARTED
+          || tgt_player->spaceship.state == SSHIP_LAUNCHED)) {
+    /* Destroying this city destroys the victim's space ship. */
+    spaceship_lost(tgt_player);
+  }
+
+  if (is_capital(tgt_city)
+      && civil_war_possible(tgt_player, TRUE, TRUE)
+      && normal_player_count() < MAX_NUM_PLAYERS
+      && civil_war_triggered(tgt_player)) {
+    /* Destroying this city can trigger a civil war. */
+    try_civil_war = TRUE;
+  }
+
+  /* Let the actor know. */
+  notify_player(act_player, city_tile(tgt_city),
+                E_UNIT_WIN, ftc_server,
+                _("You destroy %s completely."),
+                city_tile_link(tgt_city));
+
+  if (tgt_player != act_player) {
+    /* This was done to a foreign city. Inform the victim player. */
+    notify_player(tgt_player, city_tile(tgt_city),
+                  E_CITY_LOST, ftc_server,
+                  _("%s has been destroyed by %s."),
+                  city_tile_link(tgt_city),
+                  player_name(act_player));
+  }
+
+  /* May cause an incident */
+  action_consequence_success(ACTION_DESTROY_CITY, act_player,
+                             tgt_player, city_tile(tgt_city),
+                             city_link(tgt_city));
+
+  /* Run post city destruction Lua script. */
+  script_server_signal_emit("city_destroyed", 3,
+                            API_TYPE_CITY, tgt_city,
+                            API_TYPE_PLAYER, tgt_player,
+                            API_TYPE_PLAYER, act_player);
+
+  /* Can't be sure of city existence after running script. */
+  if (city_exist(tgt_city_id)) {
+    remove_city(tgt_city);
+  }
+
+  if (try_civil_war) {
+    /* Try to start the civil war. */
+    (void) civil_war(tgt_player);
+  }
+
+  /* The city is no more. */
   return TRUE;
 }
 

Modified: trunk/tools/ruledit/rulesave.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/tools/ruledit/rulesave.c?rev=29577&r1=29576&r2=29577&view=diff
==============================================================================
--- trunk/tools/ruledit/rulesave.c      (original)
+++ trunk/tools/ruledit/rulesave.c      Tue Aug 18 04:18:46 2015
@@ -874,6 +874,9 @@
   secfile_insert_str(sfile,
                      action_by_number(ACTION_NUKE)->ui_name,
                      "actions.ui_name_explode_nuclear");
+  secfile_insert_str(sfile,
+                     action_by_number(ACTION_DESTROY_CITY)->ui_name,
+                     "actions.ui_name_destroy_city");
 
   sect_idx = 0;
   action_enablers_iterate(pae) {


_______________________________________________
Freeciv-commits mailing list
Freeciv-commits@gna.org
https://mail.gna.org/listinfo/freeciv-commits

Reply via email to