Hello all,

This is the updated layers patch for current SVN, 2.2 only.It helps fix a bug 
that is common in the later game, when submarines, cruise missiles and 
airplanes become available.
A submarine can be invulnerable to attack just by unloading/reloading a cruise 
missile it is carrying. Same issue crops up with aircraft stacked with mech 
infantry, it is impossible to defeat such a stack with fighters. And ground 
units can't even attack that stack.

This patch ends that issue. A fighter stacked with mech infantry only protects 
that stack against attacks by air units such as helicopters and bombers. Which 
is how it *should* work!
This patch has only been tested to see if it compiles with current SVN. It 
needs more testing to see if all the various combat scenarios play out 
correctly:

Correct behaviour:

Fighter stacked with ground units prevents attack by any air units except other 
fighters. Then it is fighter vs fighter. Same goes for naval units and 
fighters. In general, whatever type of unit the attacker is(ground, sea, air, 
or submarine) the defender is normally selected to be the same type if it 
exists on that tile. Comments on coding style, whether all units carried by a 
transport should go down with the ship and bugs or bug fixes welcome.

Aloha,
RK.



      Enjoy a better web experience. Upgrade to the new Internet Explorer 8 
optimised for Yahoo!7. Get it now.
diff -Nur -Xdiff_ignore svn_freeciv/ai/aitools.c freeciv/ai/aitools.c
--- svn_freeciv/ai/aitools.c	2009-04-09 23:09:26.000000000 +1000
+++ freeciv/ai/aitools.c	2009-05-02 12:59:45.000000000 +1000
@@ -1111,6 +1111,12 @@
   if (is_stack_vulnerable(pdef->tile)) {
     /* lotsa people die */
     unit_list_iterate(pdef->tile->units, aunit) {
+     /* 3rd row: on sea, cargo would be lost anyway */
+      if (game.info.layered_combat
+          && !is_same_layer(pdef, aunit)
+          && (!is_sailing_unit(pdef) || is_sailing_unit(aunit))) {
+        continue;
+      }
       victim_cost += unit_build_shield_cost(aunit);
     } unit_list_iterate_end;
   } else {
diff -Nur -Xdiff_ignore svn_freeciv/common/combat.c freeciv/common/combat.c
--- svn_freeciv/common/combat.c	2009-04-09 23:09:18.000000000 +1000
+++ freeciv/common/combat.c	2009-05-03 18:03:00.000000000 +1000
@@ -128,12 +128,23 @@
 
 /***********************************************************************
   To attack a stack, unit must be able to attack every unit there (not
-  including transported units).
+  including transported units). If layered combat is enabled, it only needs to
+  be able to attack one unit in the stack.
 ************************************************************************/
 bool can_unit_attack_all_at_tile(const struct unit *punit,
 				 const struct tile *ptile)
 {
+  /* The hack below is fixed here, but transported units do not count because 
+   *  they cannot defend. We also don't want to allow land units to attack ship's 
+   *  cargo if they have land units as transport.*/	
   unit_list_iterate(ptile->units, aunit) {
+    if (aunit->transported_by == -1
+       && game.info.layered_combat
+       &&  can_unit_attack_unit_at_tile(punit, aunit, ptile)) {
+      return TRUE;
+    }
+	  
+	  
     /* HACK: we don't count transported units here.  This prevents some
      * bugs like a submarine carrying a cruise missile being invulnerable
      * to other sea units.  However from a gameplay perspective it's a hack,
@@ -162,6 +173,67 @@
   return can_unit_attack_all_at_tile(punit, dest_tile);
 }
 
+
+/**************************************************************************
+In layered combat, gives the target priority. The battle will be
+(or won't begin at all, as may be specified elsewhere) with
+a target with the biggest number, see get_defender below.
+Ships are preferred a little because they are generally easier
+targets from air.
+If you change this function, remember missiles. They must target
+sea or land units. Helicopters can be attacked only by fighters.
+-**************************************************************************/
+int layer_priority(const struct unit *attacker, const struct unit *defender)
+{
+  if (is_heli_unit(defender)) {
+    if (unit_has_type_flag(attacker, F_FIGHTER)) {
+      return 40;
+    }
+    return 0;
+  }
+
+  if (is_air_unit(defender)) {
+    if (uclass_has_flag(defender->utype, UCF_MISSILE)
+        ||  uclass_has_flag(attacker->utype, UCF_MISSILE)) {
+      return 0;
+    }
+
+    /* Fighter will attack aircraft first. And a defending fighter
+     * defends the stack from bombers/helicopters - if the attacking plane
+     * can't engage it, there won't be an attack at all.
+     */
+    if (unit_has_type_flag(attacker, F_FIGHTER)
+        || (unit_has_type_flag(defender, F_FIGHTER)
+            && is_air_unit(attacker))) {
+      return 50;
+    }
+    return 0;
+  }
+
+  if (is_ground_unit(defender)) {
+    if (unit_has_type_flag(attacker, F_NO_LAND_ATTACK)) {
+      return 0;
+    }
+    if (is_ground_unit(attacker)) {
+      return 30;
+    }
+    return 10;
+  }
+
+  if (is_sailing_unit(defender)) {
+    if (is_sailing_unit(attacker)) {
+      return 30;
+    }
+    if (is_ground_unit(attacker)) {
+      return 0;
+    }
+    return 20;
+  }
+  freelog(LOG_ERROR, "layer_priority bug: probably a new movetype");
+  assert(FALSE);
+}
+
+
 /***********************************************************************
 Returns the chance of the attacker winning, a number between 0 and 1.
 If you want the chance that the defender wins just use 1-chance(...)
@@ -556,6 +628,10 @@
 {
   struct unit *bestdef = NULL;
   int bestvalue = -99, best_cost = 0, rating_of_best = 0;
+  int priority = 0;
+  bool change;
+  bool outside = is_stack_vulnerable(ptile);
+  int build_cost, defense_rating, unit_def;
 
   /* Simply call win_chance with all the possible defenders in turn, and
    * take the best one.  It currently uses build cost as a tiebreaker in
@@ -570,12 +646,20 @@
     /* We used to skip over allied units, but the logic for that is
      * complicated and is now handled elsewhere. */
     if (unit_can_defend_here(defender)) {
-      bool change = FALSE;
-      int build_cost = unit_build_shield_cost(defender);
-      int defense_rating = get_defense_rating(attacker, defender);
+    if    (outside
+          && game.info.layered_combat
+          && layer_priority(attacker, defender) < priority) {
+        continue;
+      }
+      change = FALSE;
+      build_cost = unit_build_shield_cost(defender);
+      defense_rating = get_defense_rating(attacker, defender);      
+
+      change = FALSE;
+      build_cost = unit_build_shield_cost(defender);
+      defense_rating = get_defense_rating(attacker, defender);
       /* This will make units roughly evenly good defenders look alike. */
-      int unit_def 
-        = (int) (100000 * (1 - unit_win_chance(attacker, defender)));
+      unit_def = (int) (100000 * (1 - unit_win_chance(attacker, defender)));
 
       assert(unit_def >= 0);
 
@@ -584,15 +668,17 @@
         unit_def = -1; // then always use leader as last defender
         // FIXME: multiple gameloss units with varying defense value not handled
       }
-
-      if (unit_def > bestvalue) {
-	change = TRUE;
-      } else if (unit_def == bestvalue) {
-	if (build_cost < best_cost) {
+      if (outside
+         && game.info.layered_combat
+         && layer_priority(attacker, defender) < priority) {
+   	change = TRUE;
+	} else if (unit_def > bestvalue) {
+        change = TRUE;
+	if (rating_of_best < defense_rating) {
 	  change = TRUE;
-	} else if (build_cost == best_cost) {
-	  if (rating_of_best < defense_rating) {	
-	    change = TRUE;
+        } else if (rating_of_best == defense_rating) {
+	  if (build_cost < best_cost) {	
+	   change = TRUE;
 	  }
 	}
       }
diff -Nur -Xdiff_ignore svn_freeciv/common/combat.h freeciv/common/combat.h
--- svn_freeciv/common/combat.h	2009-04-09 23:09:18.000000000 +1000
+++ freeciv/common/combat.h	2009-05-03 13:24:46.000000000 +1000
@@ -15,6 +15,7 @@
 
 #include "fc_types.h"
 #include "unittype.h"
+#include "movement.h"
 
 /*
  * attack_strength and defense_strength are multiplied by POWER_FACTOR
@@ -35,6 +36,7 @@
 				 const struct tile *ptile);
 bool can_unit_attack_tile(const struct unit *punit,
 			  const struct tile *ptile);
+int layer_priority(const struct unit *attacker, const struct unit *defender);
 
 double win_chance(int as, int ahp, int afp, int ds, int dhp, int dfp);
 
diff -Nur -Xdiff_ignore svn_freeciv/common/movement.c freeciv/common/movement.c
--- svn_freeciv/common/movement.c	2009-04-09 23:09:18.000000000 +1000
+++ freeciv/common/movement.c	2009-05-03 18:21:23.000000000 +1000
@@ -137,6 +137,33 @@
 
 
 /****************************************************************************
+  Return TRUE iff this unit is a helicopter unit.
+****************************************************************************/
+bool is_heli_unit(const struct unit *punit)
+{
+if (unit_has_type_flag(punit, F_HELICOPTER)){
+  return TRUE;
+  }
+return FALSE;
+}
+
+
+/****************************************************************************
+  Return TRUE iff this unit is a submarine unit. Flag no land attack
+  also is true for aircraft carrier.
+****************************************************************************/
+bool is_sub_unit(const struct unit *punit)
+{
+if (unit_has_type_flag(punit, F_NO_LAND_ATTACK) 
+    && unit_has_type_flag(punit, F_PARTIAL_INVIS)){
+  return TRUE;
+  }
+return FALSE;
+}
+ 
+ 
+
+/****************************************************************************
   Return TRUE iff this unit type is a sailing/naval/sea/water unit type.
 ****************************************************************************/
 bool is_sailing_unittype(const struct unit_type *punittype)
diff -Nur -Xdiff_ignore svn_freeciv/common/movement.h freeciv/common/movement.h
--- svn_freeciv/common/movement.h	2009-04-09 23:09:18.000000000 +1000
+++ freeciv/common/movement.h	2009-05-03 17:29:41.000000000 +1000
@@ -26,6 +26,8 @@
 bool is_sailing_unit(const struct unit *punit);
 bool is_air_unit(const struct unit *punit);
 bool is_ground_unit(const struct unit *punit);
+bool is_heli_unit(const struct unit *punit);
+bool is_sub_unit(const struct unit *punit);
 bool is_sailing_unittype(const struct unit_type *punittype);
 bool is_ground_unittype(const struct unit_type *punittype);
 
diff -Nur -Xdiff_ignore svn_freeciv/common/packets.def freeciv/common/packets.def
--- svn_freeciv/common/packets.def	2009-05-02 12:20:09.000000000 +1000
+++ freeciv/common/packets.def	2009-05-02 12:20:14.000000000 +1000
@@ -254,7 +254,7 @@
   Spaceship
   Ruleset
 
-The last used packet number is 139.
+The last used packet number is 140.
 ****************************************************/
 
 
@@ -452,6 +452,7 @@
   UINT8 tech_leakage;
   YEAR tech_cost_double_year;
   BOOL killstack;
+  BOOL layered_combat;
   BOOL tired_attack;
   UINT8 border_city_radius_sq;
   UINT8 border_size_effect;
@@ -1016,7 +1017,7 @@
 PACKET_CONN_PONG=89;cs,handle-per-conn
 end
 
-PACKET_CLIENT_INFO=139;cs,handle-per-conn
+PACKET_CLIENT_INFO=140;cs,handle-per-conn
   GUI_TYPE gui;
 end
 
diff -Nur -Xdiff_ignore svn_freeciv/common/unit.c freeciv/common/unit.c
--- svn_freeciv/common/unit.c	2009-04-09 23:09:18.000000000 +1000
+++ freeciv/common/unit.c	2009-05-04 14:35:30.000000000 +1000
@@ -226,6 +226,7 @@
   return unit_type(punit)->transport_capacity;
 }
 
+
 /**************************************************************************
   Is the unit capable of attacking?
 **************************************************************************/
@@ -254,6 +255,27 @@
 }
 
 /**************************************************************************
+  Do the units belong in the same layer?  (air, land, sea, underwater(subs 
+  only)). Helicopters are a stupid move type, in spite of being air units.
+**************************************************************************/
+bool is_same_layer(const struct unit *punit, const struct unit *aunit)
+{
+  if (unit_has_type_flag(punit, F_AIRUNIT)
+     && unit_has_type_flag(aunit, F_AIRUNIT)){
+    return TRUE;
+    }
+
+  if (is_sub_unit(punit)
+     && is_sub_unit(aunit)) {
+      return TRUE;
+    }
+
+  return (utype_move_type(punit) == utype_move_type(aunit));
+ }
+
+
+
+/**************************************************************************
   Return TRUE iff the player should consider this unit to be a threat on
   the ground.
 **************************************************************************/
diff -Nur -Xdiff_ignore svn_freeciv/common/unit.h freeciv/common/unit.h
--- svn_freeciv/common/unit.h	2009-04-09 23:09:18.000000000 +1000
+++ freeciv/common/unit.h	2009-05-03 18:46:49.000000000 +1000
@@ -278,7 +278,9 @@
 bool is_square_threatened(const struct player *pplayer,
 			  const struct tile *ptile);
 bool is_field_unit(const struct unit *punit);              /* ships+aero */
+bool is_same_layer(const struct unit *punit, const struct unit *aunit);
 bool is_hiding_unit(const struct unit *punit);
+bool is_sub_unit(const struct unit *punit);
 #define COULD_OCCUPY(punit) \
   (uclass_has_flag(unit_class(punit), UCF_CAN_OCCUPY) && is_military_unit(punit))
 bool can_unit_add_to_city (const struct unit *punit);
diff -Nur -Xdiff_ignore svn_freeciv/data/civ1/game.ruleset freeciv/data/civ1/game.ruleset
--- svn_freeciv/data/civ1/game.ruleset	2009-04-13 17:52:26.000000000 +1000
+++ freeciv/data/civ1/game.ruleset	2009-05-03 04:56:01.000000000 +1000
@@ -118,6 +118,14 @@
 ;If this options is set to 0, only the defender unit is destroyed.
 killstack = 1
 
+; Whether only units of a single layer (air, land, sea, sub) are
+; destroyed when the defender loses. For example, a fighter
+; attacks bombers on a square that has also enemy land units.
+; Even if the bombers are destroyed, the land units remain
+; intact. In Civ2 this is disabled, so they will die too.
+layered_combat = 0
+
+
 ;If tired_attack is set to 1, units that attack with only 1/3 or 2/3 moves
 ;left will have their attack power reduced by 2/3 or 1/3 respectively. If
 ;this is set to 0 units will attack with full strength even if they have
diff -Nur -Xdiff_ignore svn_freeciv/data/civ2/game.ruleset freeciv/data/civ2/game.ruleset
--- svn_freeciv/data/civ2/game.ruleset	2009-04-13 17:52:26.000000000 +1000
+++ freeciv/data/civ2/game.ruleset	2009-05-03 04:56:55.000000000 +1000
@@ -112,6 +112,13 @@
 ;If this options is set to 0, only the defender unit is destroyed.
 killstack = 1
 
+; Whether only units of a single layer (air, land, sea, sub) are
+; destroyed when the defender loses. For example, a fighter
+; attacks bombers on a square that has also enemy land units.
+; Even if the bombers are destroyed, the land units remain
+; intact. In Civ2 this is disabled, so they will die too.
+layered_combat = 0
+
 ;If tired_attack is set to 1, units that attack with only 1/3 or 2/3 moves
 ;left will have their attack power reduced by 2/3 or 1/3 respectively. If
 ;this is set to 0 units will attack with full strength even if they have
diff -Nur -Xdiff_ignore svn_freeciv/data/default/game.ruleset freeciv/data/default/game.ruleset
--- svn_freeciv/data/default/game.ruleset	2009-04-13 17:52:26.000000000 +1000
+++ freeciv/data/default/game.ruleset	2009-05-03 04:57:58.000000000 +1000
@@ -139,6 +139,13 @@
 ;If this options is set to 0, only the defender unit is destroyed.
 killstack = 1
 
+; Whether only units of a single layer (air, land, sea, sub) are
+; destroyed when the defender loses. For example, a fighter
+; attacks bombers on a square that has also enemy land units.
+; Even if the bombers are destroyed, the land units remain
+; intact. Is enabled.
+layered_combat = 1
+
 ;If tired_attack is set to 1, units that attack with only 1/3 or 2/3 moves
 ;left will have their attack power reduced by 2/3 or 1/3 respectively. If
 ;this is set to 0 units will attack with full strength even if they have
diff -Nur -Xdiff_ignore svn_freeciv/data/helpdata.txt freeciv/data/helpdata.txt
--- svn_freeciv/data/helpdata.txt	2009-04-09 23:23:59.000000000 +1000
+++ freeciv/data/helpdata.txt	2009-05-03 05:00:43.000000000 +1000
@@ -1077,7 +1077,11 @@
 "), _("\
 If the defender loses, and is not inside a city or fortress, all \
 other units at the defender's location are destroyed along with the \
-defender.\
+defender.But if the rule layered.combat is enabled (by default it \
+is), this means only units on the same layer (land, sea, air, underwater). For \
+example, if you attack a bomber, any land unit beneath is unharmed. \
+If you sink a ship, its cargo (ground units, or any air unit if it \
+is a carrier, or missiles if it is a sub) is lost too. \\
 ")
 
 [help_combat_modifying]
diff -Nur -Xdiff_ignore svn_freeciv/server/ruleset.c freeciv/server/ruleset.c
--- svn_freeciv/server/ruleset.c	2009-04-13 17:52:26.000000000 +1000
+++ freeciv/server/ruleset.c	2009-05-02 12:08:31.000000000 +1000
@@ -3128,6 +3128,10 @@
   /* Enable/Disable killstack */
   game.info.killstack = secfile_lookup_bool(&file, "combat_rules.killstack");
 
+  /* Enable/ Disable Layered Combat*/
+  game.info.layered_combat 
+    = secfile_lookup_bool(&file, "combat_rules.layered_combat");
+
   /* Enable/Disable tired attack penalty */
   game.info.tired_attack
     = secfile_lookup_bool_default(&file, GAME_DEFAULT_TIRED_ATTACK,
diff -Nur -Xdiff_ignore svn_freeciv/server/unittools.c freeciv/server/unittools.c
--- svn_freeciv/server/unittools.c	2009-04-13 17:52:26.000000000 +1000
+++ freeciv/server/unittools.c	2009-05-04 14:36:50.000000000 +1000
@@ -1671,12 +1671,13 @@
   }
 
   if (unitcount == 0) {
-    unit_list_iterate(punit->tile->units, vunit)
-      if (pplayers_at_war(pvictor, unit_owner(vunit)))
+    unit_list_iterate(punit->tile->units, vunit){
+      if (game.info.layered_combat && !is_same_layer(punit, vunit))
+        continue;
+      if (pplayers_at_war(pvictor, pvictim))
 	unitcount++;
-    unit_list_iterate_end;
+    }unit_list_iterate_end;
   }
-
   if (!is_stack_vulnerable(punit->tile) || unitcount == 1) {
     notify_player(pvictor, pkiller->tile, E_UNIT_WIN_ATT,
 		  /* TRANS: "... Cannon ... the Polish Destroyer." */
@@ -1710,6 +1711,8 @@
 
     /* count killed units */
     unit_list_iterate(punit->tile->units, vunit) {
+      if (game.info.layered_combat && !is_same_layer(punit, vunit))
+        continue;
       struct player *vplayer = unit_owner(vunit);
       if (pplayers_at_war(pvictor, vplayer)) {
 	num_killed[player_index(vplayer)]++;
@@ -1809,13 +1812,16 @@
 
     /* remove the units */
     unit_list_iterate_safe(punit->tile->units, punit2) {
+      if (game.info.layered_combat && !is_same_layer(punit, punit2))
+        continue;	    
       if (pplayers_at_war(pvictor, unit_owner(punit2))) {
-	wipe_unit(punit2);
-      }
+      wipe_unit(punit2);
+     }    
     } unit_list_iterate_safe_end;
   }
 }
 
+
 /**************************************************************************
   Package a unit_info packet.  This packet contains basically all
   information about a unit.
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to