Project

Profile

Help

HostedRedmine.com has moved to the Planio platform. All logins and passwords remained the same. All users will be able to login and use Redmine just as before. Read more...

Feature #864356 » 0038-Add-Action-Wipe-Units.patch

Marko Lindqvist, 2021-03-15 07:50 AM

View differences:

ai/default/daicity.c
1282 1282
  case ACTRES_NUKE_UNITS:
1283 1283
  case ACTRES_PARADROP:
1284 1284
  case ACTRES_ATTACK:
1285
  case ACTRES_WIPE_UNITS:
1285 1286
  case ACTRES_HEAL_UNIT:
1286 1287
  case ACTRES_TRANSFORM_TERRAIN:
1287 1288
  case ACTRES_CULTIVATE:
ai/default/daidiplomacy.c
1945 1945
    case ACTRES_CAPTURE_UNITS:
1946 1946
    case ACTRES_BOMBARD:
1947 1947
    case ACTRES_ATTACK:
1948
    case ACTRES_WIPE_UNITS:
1948 1949
    case ACTRES_SPY_ATTACK:
1949 1950
      /* Unit loss */
1950 1951
      dai_incident_simple(receiver, violator, victim, scope, 5);
client/gui-qt/dialogs.cpp
111 111
static void unit_recycle(QVariant data1, QVariant data2);
112 112
static void capture_units(QVariant data1, QVariant data2);
113 113
static void nuke_units(QVariant data1, QVariant data2);
114
static void wipe_units(QVariant data1, QVariant data2);
114 115
static void expel_unit(QVariant data1, QVariant data2);
115 116
static void bombard(QVariant data1, QVariant data2);
116 117
static void bombard2(QVariant data1, QVariant data2);
......
262 263
      = paradrop_frighten_conquer;
263 264
  action_function[ACTION_ATTACK] = attack;
264 265
  action_function[ACTION_SUICIDE_ATTACK] = suicide_attack;
266
  action_function[ACTION_WIPE_UNITS] = wipe_units;
265 267
  action_function[ACTION_TRANSFORM_TERRAIN] = transform_terrain;
266 268
  action_function[ACTION_CULTIVATE] = cultivate;
267 269
  action_function[ACTION_PLANT] = plant;
......
2576 2578
                    target_id, 0, "");
2577 2579
}
2578 2580

  
2581
/**********************************************************************//**
2582
  Action "Wipe Units" for choice dialog
2583
***************************************************************************/
2584
static void wipe_units(QVariant data1, QVariant data2)
2585
{
2586
  int actor_id = data1.toInt();
2587
  int target_id = data2.toInt();
2588

  
2589
  request_do_action(ACTION_WIPE_UNITS, actor_id,
2590
                    target_id, 0, "");
2591
}
2592

  
2579 2593
/**********************************************************************//**
2580 2594
  Action capture units for choice dialog
2581 2595
***************************************************************************/
client/helpdata.c
2669 2669
                         "is reduced accordingly.\n"));
2670 2670
        }
2671 2671
        break;
2672
      case ACTRES_WIPE_UNITS:
2673
        cat_snprintf(buf, bufsz,
2674
                     _("  * can wipe stack of units with zero defense.\n"));
2675
        break;
2672 2676
      case ACTRES_CONVERT:
2673 2677
        cat_snprintf(buf, bufsz,
2674 2678
                     /* TRANS: %s is a unit type. "MP" = movement points. */
client/packhand.c
4785 4785
      case ACTION_NUKE_UNITS:
4786 4786
      case ACTION_ATTACK:
4787 4787
      case ACTION_SUICIDE_ATTACK:
4788
      case ACTION_WIPE_UNITS:
4788 4789
      case ACTION_CONQUER_CITY:
4789 4790
      case ACTION_CONQUER_CITY2:
4790 4791
      case ACTION_CONQUER_CITY3:
common/actions.c
388 388
                             " a target the actor is at war with."),
389 389
                          ACTRES_BOMBARD,
390 390
                          ACTRES_ATTACK,
391
                          ACTRES_WIPE_UNITS,
391 392
                          ACTRES_NONE);
392 393

  
393 394
  /* Why this is a hard requirement: Keep the old rules. Need to work
......
523 524
                             " the NonMil utype flag."),
524 525
                          ACTRES_ATTACK,
525 526
                          ACTRES_CONQUER_CITY,
527
                          ACTRES_WIPE_UNITS,
526 528
                          ACTRES_NONE);
527 529
  oblig_hard_req_reg(req_contradiction_or(
528 530
                       2,
......
1100 1102
      unit_action_new(ACTION_SUICIDE_ATTACK, ACTRES_ATTACK,
1101 1103
                      FALSE, TRUE,
1102 1104
                      MAK_FORCED, 1, 1, TRUE);
1105
  actions[ACTION_WIPE_UNITS] =
1106
      unit_action_new(ACTION_WIPE_UNITS, ACTRES_WIPE_UNITS,
1107
                      FALSE, TRUE,
1108
                      /* Tries a forced move if the target unit's tile has
1109
                       * no non allied units and the occupychance dice roll
1110
                       * tells it to move. */
1111
                      MAK_FORCED,
1112
                      1, 1, FALSE);
1103 1113
  actions[ACTION_STRIKE_BUILDING] =
1104 1114
      unit_action_new(ACTION_STRIKE_BUILDING, ACTRES_STRIKE_BUILDING,
1105 1115
                      FALSE, FALSE,
......
1596 1606
{
1597 1607
  switch (pact->result) {
1598 1608
  case ACTRES_ATTACK:
1609
  case ACTRES_WIPE_UNITS:
1599 1610
    return ABK_STANDARD;
1600 1611
  case ACTRES_SPY_ATTACK:
1601 1612
  case ACTRES_SPY_POISON:
......
2123 2134
  case ACTRES_STRIKE_BUILDING:
2124 2135
  case ACTRES_STRIKE_PRODUCTION:
2125 2136
  case ACTRES_ATTACK:
2137
  case ACTRES_WIPE_UNITS:
2126 2138
  case ACTRES_CONQUER_CITY:
2127 2139
  case ACTRES_CONQUER_EXTRAS:
2128 2140
  case ACTRES_HEAL_UNIT:
......
2207 2219
  case ACTRES_STRIKE_BUILDING:
2208 2220
  case ACTRES_STRIKE_PRODUCTION:
2209 2221
  case ACTRES_ATTACK:
2222
  case ACTRES_WIPE_UNITS:
2210 2223
  case ACTRES_CONQUER_CITY:
2211 2224
  case ACTRES_CONQUER_EXTRAS:
2212 2225
  case ACTRES_HEAL_UNIT:
......
3142 3155
    break;
3143 3156

  
3144 3157
  case ACTRES_ATTACK:
3158
  case ACTRES_WIPE_UNITS:
3145 3159
    if (actor_unittype->attack_strength <= 0) {
3146 3160
      /* Reason: Can't attack without strength. */
3147 3161
      return FALSE;
......
3430 3444
  case ACTRES_HOME_CITY:
3431 3445
  case ACTRES_UPGRADE_UNIT:
3432 3446
  case ACTRES_ATTACK:
3447
  case ACTRES_WIPE_UNITS:
3433 3448
  case ACTRES_STRIKE_BUILDING:
3434 3449
  case ACTRES_STRIKE_PRODUCTION:
3435 3450
  case ACTRES_CONQUER_CITY:
......
3906 3921
    }
3907 3922
    break;
3908 3923

  
3924
  case ACTRES_WIPE_UNITS:
3925
    if (!can_unit_attack_tile(actor_unit, target_tile)) {
3926
      return TRI_NO;
3927
    }
3928

  
3929
    unit_list_iterate(target_tile->units, punit) {
3930
      if (unit_type_get(punit)->defense_strength > 0) {
3931
        return TRI_NO;
3932
      }
3933
    } unit_list_iterate_end;
3934
    break;
3935

  
3909 3936
  case ACTRES_CONQUER_CITY:
3910 3937
    /* Reason: "Conquer City" involves moving into the city. */
3911 3938
    if (!unit_can_move_to_tile(&(wld.map), actor_unit, target_tile,
......
5411 5438
      }
5412 5439
    }
5413 5440
    break;
5441
  case ACTRES_WIPE_UNITS:
5442
    chance = ACTPROB_CERTAIN;
5443
    break;
5414 5444
  case ACTRES_STRIKE_BUILDING:
5415 5445
    /* TODO: not implemented yet because:
5416 5446
     * - dice roll 100% * Action_Odds_Pct could be handled with
......
5766 5796
  }
5767 5797

  
5768 5798
  if ((action_id_has_result_safe(act_id, ACTRES_ATTACK)
5769
       || action_id_has_result_safe(act_id, ACTRES_BOMBARD))
5799
       || action_id_has_result_safe(act_id, ACTRES_BOMBARD)
5800
       || action_id_has_result_safe(act_id, ACTRES_WIPE_UNITS))
5770 5801
      && tile_city(target_tile) != NULL
5771 5802
      && !pplayers_at_war(city_owner(tile_city(target_tile)),
5772 5803
                          unit_owner(actor_unit))) {
......
6615 6646
  case ACTRES_PARADROP_CONQUER:
6616 6647
  case ACTRES_AIRLIFT:
6617 6648
  case ACTRES_ATTACK:
6649
  case ACTRES_WIPE_UNITS:
6618 6650
  case ACTRES_CONQUER_CITY:
6619 6651
  case ACTRES_CONQUER_EXTRAS:
6620 6652
  case ACTRES_HEAL_UNIT:
......
7138 7170
    return "ui_name_attack";
7139 7171
  case ACTION_SUICIDE_ATTACK:
7140 7172
    return "ui_name_suicide_attack";
7173
  case ACTION_WIPE_UNITS:
7174
    return "ui_name_wipe_units";
7141 7175
  case ACTION_STRIKE_BUILDING:
7142 7176
    return "ui_name_surgical_strike_building";
7143 7177
  case ACTION_STRIKE_PRODUCTION:
......
7416 7450
  case ACTION_SUICIDE_ATTACK:
7417 7451
    /* TRANS: _Suicide Attack (100% chance of success). */
7418 7452
    return N_("%sSuicide Attack%s");
7453
  case ACTION_WIPE_UNITS:
7454
    /* TRANS: _Wipe Units (100% chance of success). */
7455
    return N_("%sWipe Units%s");
7419 7456
  case ACTION_STRIKE_BUILDING:
7420 7457
    /* TRANS: Surgical Str_ike Building (100% chance of success). */
7421 7458
    return N_("Surgical Str%sike Building%s");
......
7592 7629
  case ACTION_AIRLIFT:
7593 7630
  case ACTION_ATTACK:
7594 7631
  case ACTION_SUICIDE_ATTACK:
7632
  case ACTION_WIPE_UNITS:
7595 7633
  case ACTION_STRIKE_BUILDING:
7596 7634
  case ACTION_STRIKE_PRODUCTION:
7597 7635
  case ACTION_CONQUER_CITY:
......
7702 7740
  case ACTRES_STRIKE_BUILDING:
7703 7741
  case ACTRES_STRIKE_PRODUCTION:
7704 7742
  case ACTRES_ATTACK:
7743
  case ACTRES_WIPE_UNITS:
7705 7744
  case ACTRES_CONQUER_CITY:
7706 7745
  case ACTRES_HEAL_UNIT:
7707 7746
  case ACTRES_TRANSFORM_TERRAIN:
......
7796 7835
  case ACTION_PARADROP_ENTER_CONQUER:
7797 7836
  case ACTION_ATTACK:
7798 7837
  case ACTION_SUICIDE_ATTACK:
7838
  case ACTION_WIPE_UNITS:
7799 7839
  case ACTION_STRIKE_BUILDING:
7800 7840
  case ACTION_STRIKE_PRODUCTION:
7801 7841
  case ACTION_CONQUER_CITY:
......
7911 7951
  case ACTRES_STRIKE_BUILDING:
7912 7952
  case ACTRES_STRIKE_PRODUCTION:
7913 7953
  case ACTRES_ATTACK:
7954
  case ACTRES_WIPE_UNITS:
7914 7955
  case ACTRES_CONQUER_CITY:
7915 7956
  case ACTRES_HEAL_UNIT:
7916 7957
  case ACTRES_TRANSFORM_TERRAIN:
......
8017 8058
  case ACTION_AIRLIFT:
8018 8059
  case ACTION_ATTACK:
8019 8060
  case ACTION_SUICIDE_ATTACK:
8061
  case ACTION_WIPE_UNITS:
8020 8062
  case ACTION_STRIKE_BUILDING:
8021 8063
  case ACTION_STRIKE_PRODUCTION:
8022 8064
  case ACTION_CONQUER_CITY:
......
8139 8181
     * target tile (Units) and, depending on the
8140 8182
     * unreachable_protects setting, each or any
8141 8183
     * *non transported* unit at the target tile. */
8184
  case ACTRES_WIPE_UNITS:
8142 8185
  case ACTRES_CAPTURE_UNITS:
8143 8186
  case ACTRES_NUKE_UNITS:
8144 8187
  case ACTRES_SPY_ATTACK:
......
8228 8271
  case ACTRES_BOMBARD:
8229 8272
  case ACTRES_NUKE_UNITS:
8230 8273
  case ACTRES_ATTACK:
8274
  case ACTRES_WIPE_UNITS:
8231 8275
  case ACTRES_SPY_ATTACK:
8232 8276
    return tgt_kind == ATK_UNITS;
8233 8277
  case ACTRES_FOUND_CITY:
......
8329 8373
  case ACTRES_BOMBARD:
8330 8374
  case ACTRES_NUKE_UNITS:
8331 8375
  case ACTRES_ATTACK:
8376
  case ACTRES_WIPE_UNITS:
8332 8377
  case ACTRES_SPY_ATTACK:
8333 8378
    return ASTK_NONE;
8334 8379
  case ACTRES_FOUND_CITY:
......
8419 8464
  case ACTRES_BOMBARD:
8420 8465
  case ACTRES_NUKE_UNITS:
8421 8466
  case ACTRES_ATTACK:
8467
  case ACTRES_WIPE_UNITS:
8422 8468
  case ACTRES_SPY_ATTACK:
8423 8469
    return ACT_TGT_COMPL_SIMPLE;
8424 8470
  case ACTRES_FOUND_CITY:
......
8514 8560
  case ACTION_AIRLIFT:
8515 8561
  case ACTION_ATTACK:
8516 8562
  case ACTION_SUICIDE_ATTACK:
8563
  case ACTION_WIPE_UNITS:
8517 8564
  case ACTION_STRIKE_BUILDING:
8518 8565
  case ACTION_STRIKE_PRODUCTION:
8519 8566
  case ACTION_CONQUER_CITY:
......
8617 8664
    return "attack_blocked_by";
8618 8665
  case ACTION_SUICIDE_ATTACK:
8619 8666
    return "suicide_attack_blocked_by";
8667
  case ACTION_WIPE_UNITS:
8668
    return "wipe_units_blocked_by";
8620 8669
  case ACTION_CONQUER_CITY:
8621 8670
    return "conquer_city_blocked_by";
8622 8671
  case ACTION_CONQUER_CITY2:
......
8739 8788
  fc_assert_ret_val(act != NULL, NULL);
8740 8789

  
8741 8790
  if (!(action_has_result(act, ACTRES_SPY_BRIBE_UNIT)
8742
        ||action_has_result(act, ACTRES_ATTACK))) {
8791
        || action_has_result(act, ACTRES_ATTACK)
8792
        || action_has_result(act, ACTRES_WIPE_UNITS))) {
8743 8793
    /* No support in the action performer function */
8744 8794
    return NULL;
8745 8795
  }
......
8749 8799
    return "bribe_unit_post_success_forced_actions";
8750 8800
  case ACTION_ATTACK:
8751 8801
    return "attack_post_success_forced_actions";
8802
  case ACTION_WIPE_UNITS:
8803
    return "wipe_units_post_success_forced_actions";
8752 8804
  case ACTION_MARKETPLACE:
8753 8805
  case ACTION_BOMBARD:
8754 8806
  case ACTION_BOMBARD2:
common/actions.h
272 272
#define SPECENUM_VALUE102NAME "Paradrop Unit Enter"
273 273
#define SPECENUM_VALUE103 ACTION_PARADROP_ENTER_CONQUER
274 274
#define SPECENUM_VALUE103NAME "Paradrop Unit Enter Conquer"
275
#define SPECENUM_VALUE104 ACTION_WIPE_UNITS
276
#define SPECENUM_VALUE104NAME "Wipe Units"
275 277
#define SPECENUM_BITVECTOR bv_actions
276 278
#define SPECENUM_COUNT ACTION_COUNT
277 279
#include "specenum_gen.h"
......
600 602

  
601 603
/* Hard coded location of action auto performers. Used for conversion while
602 604
 * action auto performers aren't directly exposed to the ruleset. */
603
#define ACTION_AUTO_UPKEEP_FOOD   0
604
#define ACTION_AUTO_UPKEEP_GOLD   1
605
#define ACTION_AUTO_UPKEEP_SHIELD 2
606
#define ACTION_AUTO_MOVED_ADJ     3
607
#define ACTION_AUTO_POST_BRIBE    4
608
#define ACTION_AUTO_POST_ATTACK   5
609
#define ACTION_AUTO_ESCAPE_CITY   6
610
#define ACTION_AUTO_ESCAPE_STACK  7
605
#define ACTION_AUTO_UPKEEP_FOOD     0
606
#define ACTION_AUTO_UPKEEP_GOLD     1
607
#define ACTION_AUTO_UPKEEP_SHIELD   2
608
#define ACTION_AUTO_MOVED_ADJ       3
609
#define ACTION_AUTO_POST_BRIBE      4
610
#define ACTION_AUTO_POST_ATTACK     5
611
#define ACTION_AUTO_ESCAPE_CITY     6
612
#define ACTION_AUTO_ESCAPE_STACK    7
613
#define ACTION_AUTO_POST_WIPE_UNITS 8
611 614

  
612 615
/* Initialization */
613 616
void actions_init(void);
common/combat.c
236 236
  }
237 237
}
238 238

  
239
/*******************************************************************//**
240
  Check if unit can wipe unit stack from tile.
241
***********************************************************************/
242
enum unit_attack_result unit_wipe_units_at_tile_result(const struct unit *punit,
243
                                                       const struct tile *ptile)
244
{
245
  /* Can unit wipe in general? */
246
  if (!utype_can_do_action(unit_type_get(punit), ACTION_WIPE_UNITS)) {
247
    return ATT_NON_ATTACK;
248
  }
249

  
250
  unit_list_iterate(ptile->units, target) {
251
    if (unit_type_get(target)->defense_strength > 0) {
252
      return ATT_NOT_WIPABLE;
253
    }
254

  
255
    /* Can't wipe with ground unit from ocean, except for marines */
256
    if (!is_native_tile(unit_type_get(punit), unit_tile(punit))
257
        && !utype_can_do_act_when_ustate(unit_type_get(punit), ACTION_WIPE_UNITS,
258
                                         USP_NATIVE_TILE, FALSE)) {
259
      return ATT_NONNATIVE_SRC;
260
    }
261

  
262
    /* Most units can not wipe on non-native terrain.
263
     * Most ships can wipe on land tiles (shore bombardment) */
264
    if (!is_native_tile(unit_type_get(punit), ptile)
265
        && !can_attack_non_native(unit_type_get(punit))) {
266
      return ATT_NONNATIVE_DST;
267
    }
268

  
269
    /* Only fighters can wipe planes, except in city or airbase attacks */
270
    if (!is_unit_reachable_at(target, punit, ptile)) {
271
      return ATT_UNREACHABLE;
272
    }
273
  } unit_list_iterate_end;
274

  
275
  return ATT_OK;
276
}
277

  
239 278
/*******************************************************************//**
240 279
  Is unit (1) diplomatically allowed to attack and (2) physically able
241 280
  to do so?
common/combat.h
34 34
  ATT_NON_ATTACK,
35 35
  ATT_UNREACHABLE,
36 36
  ATT_NONNATIVE_SRC,
37
  ATT_NONNATIVE_DST
37
  ATT_NONNATIVE_DST,
38
  ATT_NOT_WIPABLE
38 39
};
39 40

  
40 41
bool is_unit_reachable_at(const struct unit *defender,
......
45 46
                                                        const struct tile *dest_tile);
46 47
enum unit_attack_result unit_attack_units_at_tile_result(const struct unit *punit,
47 48
                                                         const struct tile *ptile);
49
enum unit_attack_result unit_wipe_units_at_tile_result(const struct unit *punit,
50
                                                       const struct tile *ptile);
48 51
bool can_unit_attack_tile(const struct unit *punit,
49 52
                          const struct tile *ptile);
50 53

  
common/explanation.h
1
/**********************************************************************
1
/***********************************************************************
2 2
 Freeciv - Copyright (C) 2014-2020 - The Freeciv Project contributors.
3 3
   This program is free software; you can redistribute it and/or modify
4 4
   it under the terms of the GNU General Public License as published by
......
94 94
  ANEK_ACT_NOT_ENOUGH_MONEY,
95 95
  /* Explanation: the action is blocked by another action. */
96 96
  ANEK_ACTION_BLOCKS,
97
  /* Explanation: the target unit is not wipable. */
98
  ANEK_NOT_WIPABLE,
97 99
  /* Explanation not detected. */
98 100
  ANEK_UNKNOWN,
99 101
};
common/fc_types.h
53 53
#define MAX_GOODS_TYPES 25
54 54
#define MAX_DISASTER_TYPES 10
55 55
#define MAX_ACHIEVEMENT_TYPES 40
56
#define MAX_NUM_ACTION_AUTO_PERFORMERS 8
56
#define MAX_NUM_ACTION_AUTO_PERFORMERS 9
57 57
#define MAX_NUM_MULTIPLIERS 15
58 58
#define MAX_NUM_LEADERS MAX_NUM_ITEMS /* Used in the network protocol. */
59 59
#define MAX_NUM_NATION_SETS 32 /* Used in the network protocol.
......
293 293
#define SPECENUM_VALUE58NAME "Unit Move"
294 294
#define SPECENUM_VALUE59 ACTRES_PARADROP_CONQUER
295 295
#define SPECENUM_VALUE59NAME "Unit Paradrop Conquer"
296
#define SPECENUM_VALUE60 ACTRES_WIPE_UNITS
297
#define SPECENUM_VALUE60NAME "Wipe_Units"
296 298
/* All consequences are handled as (ruleset) action data. */
297 299
#define SPECENUM_COUNT ACTRES_NONE
298 300
#include "specenum_gen.h"
common/movement.c
163 163
}
164 164

  
165 165
/************************************************************************//**
166
 This unit can attack non-native tiles (eg. Ships ability to
167
 shore bombardment)
166
  This unit can attack non-native tiles (eg. Ships ability to
167
  shore bombardment)
168 168
****************************************************************************/
169 169
bool can_attack_non_native(const struct unit_type *utype)
170 170
{
171 171
  return uclass_has_flag(utype_class(utype), UCF_ATTACK_NON_NATIVE)
172 172
         && (utype_can_do_action_result(utype, ACTRES_ATTACK)
173
             || utype_can_do_action_result(utype, ACTRES_BOMBARD))
173
             || utype_can_do_action_result(utype, ACTRES_BOMBARD)
174
             || utype_can_do_action_result(utype, ACTRES_WIPE_UNITS))
174 175
         && utype->attack_strength > 0
175 176
         && !utype_has_flag(utype, UTYF_ONLY_NATIVE_ATTACK);
176 177
}
177 178

  
178 179
/************************************************************************//**
179
 This unit can attack from non-native tiles (Marines can attack from
180
 transport, ships from harbour cities)
180
  This unit can attack from non-native tiles (Marines can attack from
181
  transport, ships from harbour cities)
181 182
****************************************************************************/
182 183
bool can_attack_from_non_native(const struct unit_type *utype)
183 184
{
......
185 186
                                       USP_NATIVE_TILE, FALSE)
186 187
          || utype_can_do_act_when_ustate(utype, ACTION_SUICIDE_ATTACK,
187 188
                                          USP_NATIVE_TILE, FALSE)
189
          || utype_can_do_act_when_ustate(utype, ACTION_WIPE_UNITS,
190
                                          USP_LIVABLE_TILE, FALSE)
188 191
          || utype_can_do_act_when_ustate(utype, ACTION_CONQUER_CITY4,
189 192
                                          USP_LIVABLE_TILE, FALSE)
190 193
          || utype_can_do_act_when_ustate(utype, ACTION_CONQUER_CITY3,
common/unit.c
303 303
bool is_attack_unit(const struct unit *punit)
304 304
{
305 305
  return ((unit_can_do_action_result(punit, ACTRES_ATTACK)
306
           || unit_can_do_action_result(punit, ACTRES_BOMBARD))
306
           || unit_can_do_action_result(punit, ACTRES_BOMBARD)
307
           || unit_can_do_action_result(punit, ACTRES_WIPE_UNITS))
307 308
          && unit_type_get(punit)->attack_strength > 0);
308 309
}
309 310

  
common/unittype.c
328 328
  case ACTRES_STRIKE_BUILDING:
329 329
  case ACTRES_STRIKE_PRODUCTION:
330 330
  case ACTRES_ATTACK:
331
  case ACTRES_WIPE_UNITS:
331 332
  case ACTRES_CONQUER_CITY:
332 333
  case ACTRES_PILLAGE:
333 334
  case ACTRES_SPY_ATTACK:
doc/README.actions
769 769
   - it is in a city
770 770
   - it is on a tile with a native Extra
771 771

  
772
"Wipe Units"
773
 * UI name can be set using ui_name_wipe_units
774
 * any action listed in wipe_units_blocked_by must be impossible
775
 * forced actions after success can be set with
776
   wipe_units_post_success_forced_actions
777
 * the actor must be on the tile next to the target.
778
 * the actor's attack must be above 0
779
 * the actor can't have the "NonMil" unit type flag (!)
780
 * the actor must be native to the target tile unless it has the
781
   "AttackNonNative" unit class flag and not the "Only_Native_Attack" unit
782
   type flag.
783
 * the target tile has no non enemy units. (!)
784
 * the target tile has no non enemy city.
785
 * the target tile has no units with positive defense strength
786
 * all units at the target tile must be reachable.
787
   A unit is reachable if any of the following is true:
788
   - it doesn't have the "Unreachable" unit class flag
789
   - it is listed in the actor unit's targets
790
   - it is in a city
791
   - it is on a tile with a native Extra
792

  
772 793
"Nuke Units" - Detonate at the target unit stack. Cause a nuclear explosion.
773 794
 * UI name can be set using ui_name_nuke_units
774 795
 * set if the actor unit is spent with nuke_units_consuming_always
fc_version
56 56
#   - No new mandatory capabilities can be added to the release branch; doing
57 57
#     so would break network capability of supposedly "compatible" releases.
58 58
#
59
NETWORK_CAPSTRING="+Freeciv.Devel-3.2-2021.Mar.15"
59
NETWORK_CAPSTRING="+Freeciv.Devel-3.2-2021.Mar.15b"
60 60

  
61 61
FREECIV_DISTRIBUTOR=""
62 62

  
server/actiontools.c
51 51
    } else if (action_has_result(paction, ACTRES_NUKE)
52 52
               || action_has_result(paction, ACTRES_NUKE_UNITS)) {
53 53
      wipe_unit(actor, ULR_DETONATED, NULL);
54
    } else if (action_has_result(paction, ACTRES_ATTACK)) {
54
    } else if (action_has_result(paction, ACTRES_ATTACK)
55
               || action_has_result(paction, ACTRES_WIPE_UNITS)) {
55 56
      wipe_unit(actor, ULR_MISSILE, NULL);
56 57
    } else {
57 58
      wipe_unit(actor, ULR_USED, NULL);
server/advisors/advdata.c
899 899
            break;
900 900
          case ACTRES_CONQUER_EXTRAS:
901 901
          case ACTRES_PILLAGE:
902
          case ACTRES_WIPE_UNITS:
902 903
            bonus += 0.2;
903 904
            break;
904 905
          case ACTRES_HUT_ENTER:
server/ruleset.c
6942 6942
        ok = FALSE;
6943 6943
      }
6944 6944

  
6945
      /* "Wipe Units" */
6946
      if (!load_action_post_success_force(file, filename,
6947
                                          ACTION_AUTO_POST_WIPE_UNITS,
6948
                                          action_by_number(
6949
                                            ACTION_WIPE_UNITS))) {
6950
        ok = FALSE;
6951
      }
6952

  
6945 6953
      /* No "Suicide Attack". Can't act when dead. */
6946 6954
    }
6947 6955

  
server/unithand.c
382 382
  return TRUE;
383 383
}
384 384

  
385
/**********************************************************************//**
386
  Wipe all units at target tile.
387

  
388
  Returns TRUE iff action could be done, FALSE if it couldn't. Even if
389
  this returns TRUE, unit may have died during the action.
390
**************************************************************************/
391
static bool do_wipe_units(struct unit *punit,
392
                          struct tile *pdesttile,
393
                          const struct action *paction)
394
{
395
  struct player *wiper = unit_owner(punit);
396
  char wiper_link[MAX_LEN_LINK];
397
  const char *wiper_nation = nation_plural_for_player(wiper);
398
  const struct unit_type *act_utype = unit_type_get(punit);
399

  
400
  /* N.B: unit_link() always returns the same pointer. */
401
  sz_strlcpy(wiper_link, unit_link(punit));
402

  
403
  unit_list_iterate_safe(pdesttile->units, to_wipe) {
404
    struct player *owner = unit_owner(to_wipe);
405
    const char *victim_link = unit_link(to_wipe);
406

  
407
    wipe_unit(to_wipe, ULR_KILLED, wiper);
408

  
409
    /* Notify players */
410
    notify_player(wiper, pdesttile, E_UNIT_WIN_ATT, ftc_server,
411
                  /* TRANS: <unit> ... <unit> */
412
                  _("Your %s wiped the %s %s."),
413
                  wiper_link, nation_adjective_for_player(owner),
414
                  victim_link);
415
    notify_player(owner, pdesttile,
416
                  E_UNIT_LOST_DEF, ftc_server,
417
                  /* TRANS: <unit> ... <Poles> */
418
                  _("Your %s was wiped by the %s."),
419
                  victim_link, wiper_nation);
420

  
421
    /* May cause an incident */
422
    action_consequence_success(paction, wiper, act_utype, owner,
423
                               pdesttile, victim_link);
424

  
425
  } unit_list_iterate_safe_end;
426

  
427
  unit_did_action(punit);
428
  unit_forget_last_activity(punit);
429

  
430
  send_unit_info(NULL, punit);
431

  
432
  return TRUE;
433
}
434

  
385 435
/**********************************************************************//**
386 436
  Expel the target unit to his owner's capital.
387 437

  
......
807 857
  switch (paction->result) {
808 858
  case ACTRES_BOMBARD:
809 859
  case ACTRES_ATTACK:
860
  case ACTRES_WIPE_UNITS:
810 861
    /* Target is a unit stack but a city can block it. */
811 862
    fc_assert_action(action_get_target_kind(paction) == ATK_UNITS, break);
863

  
812 864
    if (target_tile) {
813 865
      struct city *tcity;
814 866

  
......
1231 1283
    case ACTRES_ATTACK:
1232 1284
      action_custom = unit_attack_units_at_tile_result(punit, target_tile);
1233 1285
      break;
1286
    case ACTRES_WIPE_UNITS:
1287
      action_custom = unit_wipe_units_at_tile_result(punit, target_tile);
1288
      break;
1234 1289
    case ACTRES_CONQUER_CITY:
1235 1290
      if (target_city) {
1236 1291
        action_custom = unit_move_to_tile_test(&(wld.map), punit,
......
1474 1529
                                       + unit_pop_value(punit))))) {
1475 1530
    explnat->kind = ANEK_CITY_POP_LIMIT;
1476 1531
  } else if ((action_has_result_safe(paction, ACTRES_NUKE_UNITS)
1477
              || action_has_result_safe(paction, ACTRES_ATTACK))
1532
              || action_has_result_safe(paction, ACTRES_ATTACK)
1533
              || action_has_result_safe(paction, ACTRES_WIPE_UNITS))
1478 1534
             && action_custom != ATT_OK) {
1479 1535
    switch (action_custom) {
1480 1536
    case ATT_NON_ATTACK:
......
1491 1547
      explnat->kind = ANEK_BAD_TERRAIN_TGT;
1492 1548
      explnat->no_act_terrain = tile_terrain(target_tile);
1493 1549
      break;
1550
    case ATT_NOT_WIPABLE:
1551
      explnat->kind = ANEK_NOT_WIPABLE;
1552
      break;
1494 1553
    default:
1495 1554
      fc_assert(action_custom != ATT_OK);
1496 1555
      explnat->kind = ANEK_UNKNOWN;
......
1847 1906
                    "unit."),
1848 1907
                  unit_name_translation(punit));
1849 1908
    break;
1909
  case ANEK_NOT_WIPABLE:
1910
    notify_player(pplayer, target_tile, E_BAD_COMMAND, ftc_server,
1911
                  _("%s can't do anything since there is unit with a positive "
1912
                    "defense value."),
1913
                  unit_name_translation(punit));
1914
    break;
1850 1915
  case ANEK_TGT_IS_UNIQUE_ACT_HAS:
1851 1916
    notify_player(pplayer, target_tile, E_BAD_COMMAND, ftc_server,
1852 1917
                  _("%s can't do anything since you already have a %s."),
......
2549 2614
                  unit_name_translation(actor),
2550 2615
                  action_id_name_translation(stopped_action));
2551 2616
    break;
2617
  case ANEK_NOT_WIPABLE:
2618
    notify_player(pplayer, target_tile,
2619
                  event, ftc_server,
2620
                  /* TRANS: "Your Legion can't do Wipe Units there ..." */
2621
                  _("Your %s can't do %s there since there's an "
2622
                    "unit with positive defense value."),
2623
                  unit_name_translation(actor),
2624
                  action_id_name_translation(stopped_action));
2625
    break;
2552 2626
  case ANEK_TGT_IS_UNIQUE_ACT_HAS:
2553 2627
    notify_player(pplayer, target_tile, event, ftc_server,
2554 2628
                  /* TRANS: "You already have a Leader." */
......
3450 3524
    ACTION_STARTED_UNIT_UNITS(action_type, actor_unit, target_tile,
3451 3525
                              do_attack(actor_unit, target_tile, paction));
3452 3526
    break;
3527
  case ACTRES_WIPE_UNITS:
3528
    ACTION_STARTED_UNIT_UNITS(action_type, actor_unit, target_tile,
3529
                              do_wipe_units(actor_unit, target_tile, paction));
3530
    break;
3453 3531
  case ACTRES_NUKE_UNITS:
3454 3532
    ACTION_STARTED_UNIT_UNITS(action_type, actor_unit, target_tile,
3455 3533
                              unit_nuke(pplayer, actor_unit, target_tile,
tools/ruleutil/rulesave.c
1286 1286
    return FALSE;
1287 1287
  }
1288 1288

  
1289
  if (!save_action_post_success_force(sfile, ACTION_AUTO_POST_WIPE_UNITS,
1290
                                      action_by_number(ACTION_WIPE_UNITS))) {
1291
    log_error("Didn't save all post success forced actions.");
1292
    return FALSE;
1293
  }
1294

  
1289 1295
  if (!save_action_auto_actions(sfile, ACTION_AUTO_ESCAPE_CITY,
1290 1296
                                "actions.escape_city")) {
1291 1297
    log_error("Didn't save all escape city forced actions.");
1292
- 
(1-1/3)